From 43b746d2f8f4c009488ae7a54542dcdf41631ecb Mon Sep 17 00:00:00 2001 From: lewa_j Date: Sun, 13 May 2018 23:25:51 +0300 Subject: [PATCH 001/205] Fix MinGW build --- engine/common/common.h | 3 ++- engine/common/net_ws.c | 8 +++++++- engine/common/system.c | 10 +++++----- engine/platform/sdl/vid_sdl.c | 3 ++- engine/platform/win32/win_con.c | 4 ++-- engine/platform/win32/win_lib.c | 4 ++-- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index c5a96b93..43502194 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -425,7 +425,8 @@ typedef struct typedef struct host_parm_s { HINSTANCE hInst; - + HANDLE hMutex; + host_status_t status; // global host state game_status_t game; // game manager uint type; // running at diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 799f4124..c0cd1ce6 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -103,6 +103,11 @@ static dllfunc_t winsock_funcs[] = dll_info_t winsock_dll = { "wsock32.dll", winsock_funcs, false }; +static void (_stdcall *pInitializeCriticalSection)( void* ); +static void (_stdcall *pEnterCriticalSection)( void* ); +static void (_stdcall *pLeaveCriticalSection)( void* ); +static void (_stdcall *pDeleteCriticalSection)( void* ); + static dllfunc_t kernel32_funcs[] = { { "InitializeCriticalSection", (void **) &pInitializeCriticalSection }, @@ -1429,9 +1434,10 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to if( NET_IsSocketError( ret )) { + int err = 0; { #ifdef _WIN32 - int err = pWSAGetLastError(); + err = pWSAGetLastError(); // WSAEWOULDBLOCK is silent if( err == WSAEWOULDBLOCK ) diff --git a/engine/common/system.c b/engine/common/system.c index 2f712506..9c096dea 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -538,7 +538,7 @@ void Sys_WaitForQuit( void ) #ifdef _WIN32 MSG msg; - Con_RegisterHotkeys(); + Wcon_RegisterHotkeys(); msg.message = 0; @@ -616,8 +616,8 @@ void Sys_Error( const char *error, ... ) if( host_developer.value ) { #ifdef _WIN32 - Con_ShowConsole( true ); - Con_DisableInput(); // disable input line for dedicated server + Wcon_ShowConsole( true ); + Wcon_DisableInput(); // disable input line for dedicated server #endif Sys_Print( text ); // print error message Sys_WaitForQuit(); @@ -625,7 +625,7 @@ void Sys_Error( const char *error, ... ) else { #ifdef _WIN32 - Con_ShowConsole( false ); + Wcon_ShowConsole( false ); #endif MSGBOX( text ); } @@ -730,7 +730,7 @@ void Sys_Print( const char *pMsg ) *b = *c = 0; // terminator - Con_WinPrint( buffer ); + Wcon_WinPrint( buffer ); } #endif diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index df55143a..d4300f22 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -302,7 +302,7 @@ static void WIN_SetDPIAwareness( void ) MsgDev( D_NOTE, "SetDPIAwareness: Success\n" ); bSuccess = TRUE; } - else if( hResult = E_INVALIDARG ) MsgDev( D_NOTE, "SetDPIAwareness: Invalid argument\n" ); + else if( hResult == E_INVALIDARG ) MsgDev( D_NOTE, "SetDPIAwareness: Invalid argument\n" ); else if( hResult == E_ACCESSDENIED ) MsgDev( D_NOTE, "SetDPIAwareness: Access Denied\n" ); } else MsgDev( D_NOTE, "SetDPIAwareness: Can't get SetProcessDpiAwareness\n" ); @@ -540,6 +540,7 @@ void VID_RestoreScreenResolution( void ) } #if defined(_WIN32) && !defined(XASH_64BIT) // ICO support only for Win32 +#include "SDL_syswm.h" static void WIN_SetWindowIcon( HICON ico ) { SDL_SysWMinfo wminfo; diff --git a/engine/platform/win32/win_con.c b/engine/platform/win32/win_con.c index 92b982b5..8725a720 100644 --- a/engine/platform/win32/win_con.c +++ b/engine/platform/win32/win_con.c @@ -270,7 +270,7 @@ void Wcon_CreateConsole( void ) string FontName; wc.style = 0; - wc.lpfnWndProc = (WNDPROC)Con_WndProc; + wc.lpfnWndProc = (WNDPROC)Wcon_WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = host.hInst; @@ -384,7 +384,7 @@ register console commands (dedicated only) void Wcon_InitConsoleCommands( void ) { if( host.type != HOST_DEDICATED ) return; - Cmd_AddCommand( "clear", Con_Clear_f, "clear console history" ); + Cmd_AddCommand( "clear", Wcon_Clear_f, "clear console history" ); } /* diff --git a/engine/platform/win32/win_lib.c b/engine/platform/win32/win_lib.c index dbfb6fc2..49743de4 100644 --- a/engine/platform/win32/win_lib.c +++ b/engine/platform/win32/win_lib.c @@ -915,7 +915,7 @@ void COM_FreeLibrary( void *hInstance ) Mem_Free( hInst ); // done } -dword COM_FunctionFromName( void *hInstance, const char *pName ) +void *COM_FunctionFromName( void *hInstance, const char *pName ) { dll_user_t *hInst = (dll_user_t *)hInstance; int i, index; @@ -938,7 +938,7 @@ dword COM_FunctionFromName( void *hInstance, const char *pName ) return 0; } -const char *COM_NameForFunction( void *hInstance, dword function ) +const char *COM_NameForFunction( void *hInstance, void *function ) { dll_user_t *hInst = (dll_user_t *)hInstance; int i, index; From d9320f964edef63cd3db6ad532b3c380ecbf9c51 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 May 2018 10:14:41 +0300 Subject: [PATCH 002/205] Crashhandler refactoring for POSIX systems. Shows engine version now. --- engine/client/gl_local.h | 3 - engine/client/gl_rmain.c | 4 +- engine/client/gl_rsurf.c | 2 +- engine/common/crashhandler.c | 177 ++++++++++++++++++++++------------- engine/common/sys_con.c | 5 + 5 files changed, 120 insertions(+), 71 deletions(-) diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 41119e89..b290a71d 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -235,8 +235,6 @@ extern ref_instance_t RI; extern ref_globals_t tr; extern float gldepthmin, gldepthmax; -extern mleaf_t *r_viewleaf, *r_oldviewleaf; -extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; extern dlight_t cl_dlights[MAX_DLIGHTS]; extern dlight_t cl_elights[MAX_ELIGHTS]; #define r_numEntities (tr.draw_list->num_solid_entities + tr.draw_list->num_trans_entities) @@ -328,7 +326,6 @@ void R_PushDlights( void ); void R_AnimateLight( void ); void R_GetLightSpot( vec3_t lightspot ); void R_MarkLights( dlight_t *light, int bit, mnode_t *node ); -void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, qboolean useAmbient, float radius ); colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot ); int R_CountSurfaceDlights( msurface_t *surf ); colorVec R_LightPoint( const vec3_t p0 ); diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index ac816760..796d4e8b 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -24,9 +24,7 @@ GNU General Public License for more details. #define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) -msurface_t *r_debug_surface; -const char *r_debug_hitbox; -float gldepthmin, gldepthmax; +float gldepthmin, gldepthmax; ref_instance_t RI; static int R_RankForRenderMode( int rendermode ) diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index da2576ec..190aae7c 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -208,7 +208,7 @@ void GL_SetupFogColorForSurfaces( void ) return; if( RI.currententity && RI.currententity->curstate.rendermode == kRenderTransTexture ) - { + { pglFogfv( GL_FOG_COLOR, RI.fogColor ); return; } diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 9ee66781..5ce4b938 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -12,6 +12,7 @@ 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. */ +#define _GNU_SOURCE #include "common.h" @@ -186,7 +187,7 @@ LPTOP_LEVEL_EXCEPTION_FILTER oldFilter; long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) { // save config - if( host.state != HOST_CRASHED ) + if( host.status != HOST_CRASHED ) { // check to avoid recursive call host.crashed = true; @@ -199,7 +200,7 @@ long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) if( host.type == HOST_NORMAL ) CL_Crashed(); // tell client about crash - else host.state = HOST_CRASHED; + else host.status = HOST_CRASHED; if( host.developer <= 0 ) { @@ -232,139 +233,187 @@ void Sys_RestoreCrashHandler( void ) #elif XASH_CRASHHANDLER == CRASHHANDLER_UCONTEXT // Posix signal handler + #include "library.h" -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined __ANDROID__ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__ANDROID__) || defined(__linux__) #define HAVE_UCONTEXT_H 1 #endif #ifdef HAVE_UCONTEXT_H #include #endif +#include #include int printframe( char *buf, int len, int i, void *addr ) { Dl_info dlinfo; - if( len <= 0 ) return 0; // overflow + if( len <= 0 ) + return 0; // overflow + if( dladdr( addr, &dlinfo )) { if( dlinfo.dli_sname ) - return Q_snprintf( buf, len, "% 2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname, + return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname, (unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address else - return Q_snprintf( buf, len, "% 2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address + return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address } else - return Q_snprintf( buf, len, "% 2d: %p\n", i, addr ); // print only address + return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address } struct sigaction oldFilter; +#define STACK_BACKTRACE_STR_LEN 17 +#define STACK_BACKTRACE_STR "Stack backtrace:\n" +#define STACK_DUMP_STR_LEN 12 +#define STACK_DUMP_STR "Stack dump:\n" +#define ALIGN( x, y ) (((int) (x) + ((y)-1)) & ~((y)-1)) + static void Sys_Crash( int signal, siginfo_t *si, void *context) { - void *trace[32]; + void *pc, **bp, **sp; // this must be set for every OS! + char message[8192]; + int len, logfd, i = 0; + size_t pagesize; - char message[4096], stackframe[256]; - int len, stacklen, logfd, i = 0; #if defined(__OpenBSD__) struct sigcontext *ucontext = (struct sigcontext*)context; #else ucontext_t *ucontext = (ucontext_t*)context; #endif + #if defined(__x86_64__) #if defined(__FreeBSD__) - void *pc = (void*)ucontext->uc_mcontext.mc_rip, **bp = (void**)ucontext->uc_mcontext.mc_rbp, **sp = (void**)ucontext->uc_mcontext.mc_rsp; + pc = (void*)ucontext->uc_mcontext.mc_rip; + bp = (void**)ucontext->uc_mcontext.mc_rbp; + sp = (void**)ucontext->uc_mcontext.mc_rsp; #elif defined(__NetBSD__) - void *pc = (void*)ucontext->uc_mcontext.__gregs[REG_RIP], **bp = (void**)ucontext->uc_mcontext.__gregs[REG_RBP], **sp = (void**)ucontext->uc_mcontext.__gregs[REG_RSP]; + pc = (void*)ucontext->uc_mcontext.__gregs[REG_RIP]; + bp = (void**)ucontext->uc_mcontext.__gregs[REG_RBP]; + sp = (void**)ucontext->uc_mcontext.__gregs[REG_RSP]; #elif defined(__OpenBSD__) - void *pc = (void*)ucontext->sc_rip, **bp = (void**)ucontext->sc_rbp, **sp = (void**)ucontext->sc_rsp; + pc = (void*)ucontext->sc_rip; + bp = (void**)ucontext->sc_rbp; + sp = (void**)ucontext->sc_rsp; #else - void *pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP], **bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP], **sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP]; + pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP]; + bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP]; + sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP]; #endif #elif defined(__i386__) #if defined(__FreeBSD__) - void *pc = (void*)ucontext->uc_mcontext.mc_eip, **bp = (void**)ucontext->uc_mcontext.mc_ebp, **sp = (void**)ucontext->uc_mcontext.mc_esp; + pc = (void*)ucontext->uc_mcontext.mc_eip; + bp = (void**)ucontext->uc_mcontext.mc_ebp; + sp = (void**)ucontext->uc_mcontext.mc_esp; #elif defined(__NetBSD__) - void *pc = (void*)ucontext->uc_mcontext.__gregs[REG_EIP], **bp = (void**)ucontext->uc_mcontext.__gregs[REG_EBP], **sp = (void**)ucontext->uc_mcontext.__gregs[REG_ESP]; + pc = (void*)ucontext->uc_mcontext.__gregs[REG_EIP]; + bp = (void**)ucontext->uc_mcontext.__gregs[REG_EBP]; + sp = (void**)ucontext->uc_mcontext.__gregs[REG_ESP]; #elif defined(__OpenBSD__) - void *pc = (void*)ucontext->sc_eip, **bp = (void**)ucontext->sc_ebp, **sp = (void**)ucontext->sc_esp; + pc = (void*)ucontext->sc_eip; + bp = (void**)ucontext->sc_ebp; + sp = (void**)ucontext->sc_esp; #else - void *pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP], **bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP], **sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP]; + pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP]; + bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP]; + sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP]; #endif #elif defined(__aarch64__) // arm not tested - void *pc = (void*)ucontext->uc_mcontext.pc, **bp = (void*)ucontext->uc_mcontext.regs[29], **sp = (void*)ucontext->uc_mcontext.sp; + pc = (void*)ucontext->uc_mcontext.pc; + bp = (void*)ucontext->uc_mcontext.regs[29]; + sp = (void*)ucontext->uc_mcontext.sp; #elif defined(__arm__) - void *pc = (void*)ucontext->uc_mcontext.arm_pc, **bp = (void*)ucontext->uc_mcontext.arm_fp, **sp = (void*)ucontext->uc_mcontext.arm_sp; + pc = (void*)ucontext->uc_mcontext.arm_pc; + bp = (void*)ucontext->uc_mcontext.arm_fp; + sp = (void*)ucontext->uc_mcontext.arm_sp; #else -#error "Unknown arch!!!" + #error "Unknown arch!!!" #endif - // Safe actions first, stack and memory may be corrupted - #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - len = Q_snprintf( message, 4096, "Sys_Crash: signal %d, err %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr ); - #else - len = Q_snprintf( message, 4096, "Sys_Crash: signal %d, err %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr ); - #endif - write(2, message, len); - // Flush buffers before writing directly to descriptors + + // safe actions first, stack and memory may be corrupted + len = Q_snprintf( message, sizeof( message ), "Ver: %s %s (build %i-%s, %s-%s)\n", + XASH_ENGINE_NAME, XASH_VERSION, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); + +#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) + len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr ); +#else + len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr ); +#endif + + write( 2, message, len ); + + // flush buffers before writing directly to descriptors fflush( stdout ); fflush( stderr ); - // Now get log fd and write trace directly to log + + // now get log fd and write trace directly to log logfd = Sys_LogFileNo(); write( logfd, message, len ); - write( 2, "Stack backtrace:\n", 17 ); - write( logfd, "Stack backtrace:\n", 17 ); - strncpy(message + len, "Stack backtrace:\n", 4096 - len); - len += 17; - size_t pagesize = sysconf(_SC_PAGESIZE); + + // try to print backtrace + write( 2, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN ); + write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN ); + strncpy( message + len, STACK_BACKTRACE_STR, sizeof( message ) - len ); + len += STACK_BACKTRACE_STR_LEN; + + pagesize = sysconf( _SC_PAGESIZE ); + do { - int line = printframe( message + len, 4096 - len, ++i, pc); + int line = printframe( message + len, sizeof( message ) - len, ++i, pc); write( 2, message + len, line ); write( logfd, message + len, line ); len += line; - //if( !dladdr(bp,0) ) break; // Only when bp is in module - if( ( mprotect((char *)(((int) bp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && - ( mprotect((char *)(((int) bp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_EXEC ) == -1) && - ( mprotect((char *)(((int) bp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE ) == -1) && - ( mprotect((char *)(((int) bp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ ) == -1) ) + //if( !dladdr(bp,0) ) break; // only when bp is in module + if( ( mprotect( (char *)ALIGN( bp, pagesize ), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && + ( mprotect( (char *)ALIGN( bp, pagesize ), pagesize, PROT_READ | PROT_EXEC ) == -1) && + ( mprotect( (char *)ALIGN( bp, pagesize ), pagesize, PROT_READ | PROT_WRITE ) == -1) && + ( mprotect( (char *)ALIGN( bp, pagesize ), pagesize, PROT_READ ) == -1) ) break; - if( ( mprotect((char *)(((int) bp[0] + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && - ( mprotect((char *)(((int) bp[0] + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_EXEC ) == -1) && - ( mprotect((char *)(((int) bp[0] + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE ) == -1) && - ( mprotect((char *)(((int) bp[0] + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ ) == -1) ) + if( ( mprotect( (char *)ALIGN( bp[0], pagesize ), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && + ( mprotect( (char *)ALIGN( bp[0], pagesize ), pagesize, PROT_READ | PROT_EXEC ) == -1) && + ( mprotect( (char *)ALIGN( bp[0], pagesize ), pagesize, PROT_READ | PROT_WRITE ) == -1) && + ( mprotect( (char *)ALIGN( bp[0], pagesize ), pagesize, PROT_READ ) == -1) ) break; pc = bp[1]; bp = (void**)bp[0]; } while( bp && i < 128 ); - // Try to print stack - write( 2, "Stack dump:\n", 12 ); - write( logfd, "Stack dump:\n", 12 ); - strncpy( message + len, "Stack dump:\n", 4096 - len ); - len += 12; - if( ( mprotect((char *)(((int) sp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) != -1) || - ( mprotect((char *)(((int) sp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_EXEC ) != -1) || - ( mprotect((char *)(((int) sp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ | PROT_WRITE ) != -1) || - ( mprotect((char *)(((int) sp + (pagesize-1)) & ~(pagesize-1)), pagesize, PROT_READ ) != -1) ) + // try to print stack + write( 2, STACK_DUMP_STR, STACK_DUMP_STR_LEN ); + write( logfd, STACK_DUMP_STR, STACK_DUMP_STR_LEN ); + strncpy( message + len, STACK_DUMP_STR, sizeof( message ) - len ); + len += STACK_DUMP_STR_LEN; + + if( ( mprotect((char *)ALIGN( sp, pagesize ), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC ) != -1) || + ( mprotect((char *)ALIGN( sp, pagesize ), pagesize, PROT_READ | PROT_EXEC ) != -1) || + ( mprotect((char *)ALIGN( sp, pagesize ), pagesize, PROT_READ | PROT_WRITE ) != -1) || + ( mprotect((char *)ALIGN( sp, pagesize ), pagesize, PROT_READ ) != -1) ) + { for( i = 0; i < 32; i++ ) { - int line = printframe( message + len, 4096 - len, i, sp[i] ); + int line = printframe( message + len, sizeof( message ) - len, i, sp[i] ); write( 2, message + len, line ); write( logfd, message + len, line ); len += line; } - // Put MessageBox as Sys_Error + } + + // put MessageBox as Sys_Error Msg( "%s\n", message ); #ifdef XASH_SDL SDL_SetWindowGrab( host.hWnd, SDL_FALSE ); #endif MSGBOX( message ); - // Log saved, now we can try to save configs and close log correctly, it may crash + // log saved, now we can try to save configs and close log correctly, it may crash if( host.type == HOST_NORMAL ) CL_Crashed(); - host.state = HOST_CRASHED; + host.status = HOST_CRASHED; host.crashed = true; Sys_Quit(); @@ -375,18 +424,18 @@ void Sys_SetupCrashHandler( void ) struct sigaction act; act.sa_sigaction = Sys_Crash; act.sa_flags = SA_SIGINFO | SA_ONSTACK; - sigaction(SIGSEGV, &act, &oldFilter); - sigaction(SIGABRT, &act, &oldFilter); - sigaction(SIGBUS, &act, &oldFilter); - sigaction(SIGILL, &act, &oldFilter); + sigaction( SIGSEGV, &act, &oldFilter ); + sigaction( SIGABRT, &act, &oldFilter ); + sigaction( SIGBUS, &act, &oldFilter ); + sigaction( SIGILL, &act, &oldFilter ); } void Sys_RestoreCrashHandler( void ) { sigaction( SIGSEGV, &oldFilter, NULL ); sigaction( SIGABRT, &oldFilter, NULL ); - sigaction( SIGBUS, &oldFilter, NULL ); - sigaction( SIGILL, &oldFilter, NULL ); + sigaction( SIGBUS, &oldFilter, NULL ); + sigaction( SIGILL, &oldFilter, NULL ); } #elif XASH_CRASHHANDLER == CRASHHANDLER_NULL diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 409f8934..b6174c78 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -90,6 +90,11 @@ SYSTEM LOG =============================================================================== */ +int Sys_LogFileNo( void ) +{ + return s_ld.logfileno; +} + void Sys_InitLog( void ) { const char *mode; From 0c058f2eca3ebeea9dbf0642ac1aaed3ba3e61c4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 May 2018 18:17:25 +0300 Subject: [PATCH 003/205] Add waf buildscripts, add updated game_launch --- engine/client/gl_rmain.c | 2 +- engine/common/infostring.c | 1 + engine/common/zone.c | 2 +- engine/wscript | 80 +++++++++++++ game_launch/game.cpp | 185 +++++++++++++++++++++++++++++ game_launch/game.rc | 28 +++++ game_launch/icon-xash-material.ico | Bin 0 -> 370070 bytes game_launch/icon-xash-material.png | Bin 0 -> 71270 bytes game_launch/wscript | 58 +++++++++ vgui_support/wscript | 70 +++++++++++ wscript | 97 +++++++++++++++ 11 files changed, 521 insertions(+), 2 deletions(-) create mode 100644 engine/wscript create mode 100644 game_launch/game.cpp create mode 100644 game_launch/game.rc create mode 100644 game_launch/icon-xash-material.ico create mode 100644 game_launch/icon-xash-material.png create mode 100644 game_launch/wscript create mode 100644 vgui_support/wscript create mode 100644 wscript diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 796d4e8b..9850ccb9 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -205,7 +205,7 @@ void R_PushScene( void ) /* =============== -R_PushScene +R_PopScene =============== */ void R_PopScene( void ) diff --git a/engine/common/infostring.c b/engine/common/infostring.c index a7de6c46..d89561e6 100644 --- a/engine/common/infostring.c +++ b/engine/common/infostring.c @@ -21,6 +21,7 @@ GNU General Public License for more details. ======================================================================= INFOSTRING STUFF + ======================================================================= */ /* diff --git a/engine/common/zone.c b/engine/common/zone.c index d5be638d..8cdde6a8 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -280,7 +280,7 @@ void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, const char *filena size_t newsize = memhdr->size < size ? memhdr->size : size; // upper data can be trucnated! memcpy( nb, memptr, newsize ); _Mem_Free( memptr, filename, fileline ); // free unused old block - } + } return (void *)nb; } diff --git a/engine/wscript b/engine/wscript new file mode 100644 index 00000000..4b63c1b6 --- /dev/null +++ b/engine/wscript @@ -0,0 +1,80 @@ +#! /usr/bin/env python +# encoding: utf-8 +# mittorn, 2018 + +from waflib import Logs +import os + +top = '.' + +def options(opt): + # stub + return + +def configure(conf): + # check for dedicated server build + if conf.options.DEDICATED: + conf.check( lib='rt' ) + conf.env.append_unique('DEFINES', 'SINGLE_BINARY') + conf.env.append_unique('DEFINES', 'XASH_DEDICATED') + else: + # TODO: add way to specify SDL2 path, move to separate function + try: + conf.check_cfg( + path='sdl2-config', + args='--cflags --libs', + package='', + msg='Checking for SDL2', + uselib_store='SDL2') + except conf.errors.ConfigurationError: + conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + conf.env.append_unique('DEFINES', 'XASH_SDL') + +def get_subproject_name(ctx): + return os.path.basename(os.path.realpath(str(ctx.path))) + +def build(bld): + bld.load_envs() + bld.env = bld.all_envs[get_subproject_name(bld)] + + # basic build: dedicated only, no dependencies + if bld.env.DEST_OS != 'win32': + libs = [ 'DL', 'M', 'PTHREAD' ] + + source = bld.path.ant_glob([ + 'common/*.c', + 'common/imagelib/*.c', + 'common/soundlib/*.c', + 'common/soundlib/libmpg/*.c', + 'server/*.c']) + + # add client files and sdl2 library + if not bld.env.DEDICATED: + libs.append( 'SDL2' ) + source += bld.path.ant_glob([ + 'client/*.c', + 'client/vgui/*.c', + 'client/avi/*.c', + 'platform/sdl/*.c']) + else: + if(bld.env.DEST_OS == 'linux'): + libs.append('RT') + + includes = ['common', 'server', 'client', 'client/vgui', '.', '../common', '../pm_shared' ] + + if(bld.env.SINGLE_BINARY): + bld( + source = source, + target = 'xash', + features = 'c cprogram', + includes = includes, + use = libs + ) + else: + bld.shlib( + source = source, + target = 'xash', + features = 'c', + includes = includes, + use = libs + ) diff --git a/game_launch/game.cpp b/game_launch/game.cpp new file mode 100644 index 00000000..4715e8e5 --- /dev/null +++ b/game_launch/game.cpp @@ -0,0 +1,185 @@ +/* +game.cpp -- executable to run Xash Engine +Copyright (C) 2011 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "port.h" + +#ifdef XASH_SDL +#include +#include +#endif + +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(__unix__) + #define XASHLIB "libxash." OS_LIB_EXT +#elif _WIN32 + #if !__MINGW32__ && _MSC_VER >= 1200 + #define USE_WINMAIN + #endif + #ifndef XASH_DEDICATED + #define XASHLIB "xash_sdl.dll" + #else + #define XASHLIB "xash_dedicated.dll" + #endif + #include +#endif + +#ifdef WIN32 +extern "C" +{ +// Enable NVIDIA High Performance Graphics while using Integrated Graphics. +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + +// Enable AMD High Performance Graphics while using Integrated Graphics. +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} +#endif + +#define GAME_PATH "valve" // default dir to start from + +typedef void (*pfnChangeGame)( const char *progname ); +typedef int (*pfnInit)( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); +typedef void (*pfnShutdown)( void ); + +static pfnInit Xash_Main; +static pfnShutdown Xash_Shutdown = NULL; +static char szGameDir[128]; // safe place to keep gamedir +static int szArgc; +static char **szArgv; +static HINSTANCE hEngine; + +static void Xash_Error( const char *szFmt, ... ) +{ + static char buffer[16384]; // must support > 1k messages + va_list args; + + va_start( args, szFmt ); + vsnprintf( buffer, sizeof(buffer), szFmt, args ); + va_end( args ); + +#ifdef XASH_SDL + SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Xash Error", buffer, NULL ); +#elif defined( _WIN32 ) + MessageBoxA( NULL, buffer, "Xash Error", MB_OK ); +#else + fprintf( stderr, "Xash Error: %s\n", buffer ); +#endif + exit( 1 ); +} + +#ifdef _WIN32 +static const char *GetStringLastError() +{ + static char buf[1024]; + + FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ), + buf, sizeof( buf ), NULL ); + + return buf; +} +#endif + +static void Sys_LoadEngine( void ) +{ + if(( hEngine = LoadLibrary( XASHLIB )) == NULL ) + { + Xash_Error("Unable to load the " XASHLIB ": %s", dlerror() ); + } + + if(( Xash_Main = (pfnInit)GetProcAddress( hEngine, "Host_Main" )) == NULL ) + { + Xash_Error( XASHLIB " missed 'Host_Main' export: %s", dlerror() ); + } + + // this is non-fatal for us but change game will not working + Xash_Shutdown = (pfnShutdown)GetProcAddress( hEngine, "Host_Shutdown" ); +} + +static void Sys_UnloadEngine( void ) +{ + if( Xash_Shutdown ) Xash_Shutdown( ); + if( hEngine ) FreeLibrary( hEngine ); + + Xash_Main = NULL; + Xash_Shutdown = NULL; +} + +static void Sys_ChangeGame( const char *progname ) +{ + if( !progname || !progname[0] ) + Xash_Error( "Sys_ChangeGame: NULL gamedir" ); + + if( Xash_Shutdown == NULL ) + Xash_Error( "Sys_ChangeGame: missed 'Host_Shutdown' export\n" ); + + strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); + + Sys_UnloadEngine (); + Sys_LoadEngine (); + + Xash_Main( szArgc, szArgv, szGameDir, 1, Sys_ChangeGame ); +} + +_inline int Sys_Start( void ) +{ + int ret; + + Sys_LoadEngine(); + ret = Xash_Main( szArgc, szArgv, GAME_PATH, 0, Xash_Shutdown ? Sys_ChangeGame : NULL ); + Sys_UnloadEngine(); + + return ret; +} + +#ifndef USE_WINMAIN +int main( int argc, char **argv ) +{ + szArgc = argc; + szArgv = argv; + + return Sys_Start(); +} +#else +//#pragma comment(lib, "shell32.lib") +int __stdcall WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow ) +{ + LPWSTR* lpArgv; + int ret, i; + + lpArgv = CommandLineToArgvW( GetCommandLineW(), &szArgc ); + szArgv = ( char** )malloc( szArgc * sizeof( char* )); + + for( i = 0; i < szArgc; ++i ) + { + int size = wcslen(lpArgv[i]) + 1; + szArgv[i] = ( char* )malloc( size ); + wcstombs( szArgv[i], lpArgv[i], size ); + } + + LocalFree( lpArgv ); + + ret = Sys_Start(); + + for( ; i < szArgc; ++i ) + free( szArgv[i] ); + free( szArgv ); + + return ret; +} +#endif diff --git a/game_launch/game.rc b/game_launch/game.rc new file mode 100644 index 00000000..1d8bbcc9 --- /dev/null +++ b/game_launch/game.rc @@ -0,0 +1,28 @@ +#include + +#define IDI_ICON1 101 + +#define VER_FILEVERSION 1,00 +#define VER_FILEVERSION_STR "1.00" +#define VER_PRODUCTVERSION 1,00 +#define VER_PRODUCTVERSION_STR "1.00" + +#define VER_FILEFLAGSMASK VS_FF_PRERELEASE | VS_FF_PATCHED +#define VER_FILEFLAGS VS_FF_PRERELEASE +#define VER_FILEOS VOS__WINDOWS32 +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN + +#define VER_COMPANYNAME_STR "Flying With Gauss" +#define VER_LEGALCOPYRIGHT_STR "Flying With Gauss" +#define VER_PRODUCTNAME_STR "Xash3D Launcher" + +#define VER_ANSICP + +#define VER_FILEDESCRIPTION_STR "Xash3D FWGS Launcher" +#define VER_ORIGINALFILENAME_STR "xash.exe" +#define VER_INTERNALNAME_STR "xash" + +#include + +IDI_ICON1 ICON DISCARDABLE "icon-xash-material.ico" diff --git a/game_launch/icon-xash-material.ico b/game_launch/icon-xash-material.ico new file mode 100644 index 0000000000000000000000000000000000000000..7c1ac3cc57811e4ca80f584a0c926ad4e4eb096e GIT binary patch literal 370070 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 literal 0 HcmV?d00001 diff --git a/game_launch/wscript b/game_launch/wscript new file mode 100644 index 00000000..f298b54d --- /dev/null +++ b/game_launch/wscript @@ -0,0 +1,58 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from waflib import Logs +import os + +top = '.' + +def options(opt): + # stub + return + +def configure(conf): + if(conf.env.SINGLE_BINARY): + return + + # check for dedicated server build + if conf.env.DEST_OS != 'win32' and not conf.env.DEDICATED: + # TODO: add way to specify SDL2 path, move to separate function + try: + conf.check_cfg( + path='sdl2-config', + args='--cflags --libs', + package='', + msg='Checking for SDL2', + uselib_store='SDL2') + except conf.errors.ConfigurationError: + conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + conf.env.append_unique('DEFINES', 'XASH_SDL') + +def get_subproject_name(ctx): + return os.path.basename(os.path.realpath(str(ctx.path))) + +def build(bld): + bld.load_envs() + bld.env = bld.all_envs[get_subproject_name(bld)] + + source = 'game.cpp' + includes = '. ../common' + libs = [] + + if bld.env.DEST_OS != 'win32': + libs += [ 'DL' ] + if not bld.env.DEDICATED: + libs += [ 'SDL2' ] + else: + # compile resource on Windows + bld.load('winres') + source += 'game.rc' + + bld( + source = source, + target = 'xash3d', # hl.exe + features = 'c cprogram', + includes = includes, + use = libs + ) diff --git a/vgui_support/wscript b/vgui_support/wscript new file mode 100644 index 00000000..e73d0b15 --- /dev/null +++ b/vgui_support/wscript @@ -0,0 +1,70 @@ +#! /usr/bin/env python +# encoding: utf-8 +# mittorn, 2018 + +from waflib import Logs +import os + +top = '.' + +def options(opt): + opt.add_option( + '--vgui', action = 'store', type='string', dest = 'VGUI_DEV', + help = 'path to vgui-dev repo', default='' ) + + # stub + return + +def configure(conf): + if conf.options.DEDICATED: + return + + if not conf.options.VGUI_DEV: + conf.fatal("Provide a path to vgui-dev repository using --vgui key") + + if conf.env.DEST_CPU != 'x86' and not (conf.env.DEST_CPU == 'x86_64' and not conf.options.ALLOW64): # multilib case + conf.fatal('vgui is not supported on this CPU: ' + conf.env.DEST_CPU) + + if conf.env.DEST_OS == 'win32': + conf.env.LIB_VGUI = ['vgui.lib'] + conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib/win32_vc6/'))] + else: + if conf.env.DEST_OS == 'linux': + conf.env.LIB_VGUI = [':vgui.so'] + elif conf.env.DEST_OS == 'darwin': + conf.env.LIB_VGUI = ['vgui.dylib'] + else: + conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) + conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib'))] + conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'include'))] + + conf.env.HAVE_VGUI = 1 + + Logs.info('VGUI configured as {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) + +def get_subproject_name(ctx): + return os.path.basename(os.path.realpath(str(ctx.path))) + +def build(bld): + bld.load_envs() + bld.env = bld.all_envs[get_subproject_name(bld)] + + if bld.env.DEDICATED: + return + + # basic build: dedicated only, no dependencies + if bld.env.DEST_OS != 'win32': + libs = [ 'DL', 'M' ] + + libs.append('VGUI') + + source = bld.path.ant_glob(['*.cpp']) + + includes = [ '.', '../common', '../engine' ] + + bld.shlib( + source = source, + target = 'vgui_support', + features = 'cxx', + includes = includes, + use = libs) diff --git a/wscript b/wscript new file mode 100644 index 00000000..4b05d1af --- /dev/null +++ b/wscript @@ -0,0 +1,97 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from __future__ import print_function +from waflib import Logs + +import os +import sys + +def get_git_version(): + # try grab the current version number from git + version = "notset" + if os.path.exists(".git"): + try: + version = os.popen("git describe --dirty --always").read().strip() + except Exception as e: + print(e) + return version + +VERSION = '0.99' +APPNAME = 'xash3d-fwgs' +GIT_SHA = get_git_version() +SUBDIRS = [ 'game_launch', 'vgui_support', 'engine' ] + +top = '.' + +def options(opt): + opt.load('compiler_cxx compiler_c') + if sys.platform == 'win32': + opt.load('msvc') + + opt.add_option( + '--dedicated', action = 'store_true', dest = 'DEDICATED', default=False, + help = 'build Xash Dedicated Server(XashDS)') + + opt.add_option( + '--64bits', action = 'store_true', dest = 'ALLOW64', default=False, + help = 'allow targetting 64-bit engine') + + opt.add_option( + '--release', action = 'store_true', dest = 'RELEASE', default=False, + help = 'strip debug info from binary and enable optimizations') + + opt.recurse(SUBDIRS) + +def configure(conf): + conf.load('compiler_cxx compiler_c') + conf.check_cc( + fragment=''' + #include + int main( void ) { printf("%ld", sizeof( void * )); return 0; } + ''', + execute = True, + define_ret = True, + uselib_store = 'SIZEOF_VOID_P', + msg = 'Checking sizeof(void*)') + + if(conf.env.SIZEOF_VOID_P != '4' and not conf.options.ALLOW64): + conf.env.append_value('LINKFLAGS', '-m32') + conf.env.append_value('CFLAGS', '-m32') + conf.env.append_value('CXXFLAGS', '-m32') + Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') + else: + Logs.warn('WARNING: 64-bit engine may be unstable') + + if(conf.env.COMPILER_CC == 'gcc'): + conf.env.append_value('LINKFLAGS', '-Wl,--no-undefined') + + if(conf.options.RELEASE): + conf.env.append_unique('CFLAGS', '-O2') + else: + conf.env.append_unique('CFLAGS', '-Og') + conf.env.append_unique('CFLAGS', '-g') + + + conf.check( lib='dl' ) + conf.check( lib='m' ) + conf.check( lib='pthread' ) + + conf.env.DEDICATED = conf.options.DEDICATED + conf.env.SINGLE_BINARY = conf.options.DEDICATED + + # global + conf.env.append_unique('XASH_BUILD_COMMIT', GIT_SHA) + + for i in SUBDIRS: + conf.setenv(i, conf.env) # derive new env from global one + conf.env.ENVNAME = i + Logs.info('Configuring ' + i) + # configure in standalone env + conf.recurse(i) + conf.setenv('') + +def build(bld): + for i in SUBDIRS: + bld.recurse(SUBDIRS) From 7e372645da7d8dbb296604e535ede42a83772cf0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 May 2018 18:55:18 +0300 Subject: [PATCH 004/205] Add my personal IDE settings to contrib. Add QtCreator generated files to gitignore. --- .gitignore | 6 +- contib/a1batross/xash3d.config | 2 + contib/a1batross/xash3d.creator | 1 + contib/a1batross/xash3d.files | 374 +++++++++++++++++++++++++++++++ contib/a1batross/xash3d.includes | 22 ++ 5 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 contib/a1batross/xash3d.config create mode 100644 contib/a1batross/xash3d.creator create mode 100644 contib/a1batross/xash3d.files create mode 100644 contib/a1batross/xash3d.includes diff --git a/.gitignore b/.gitignore index c0f5d266..cac86b0e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Other *.save + # Qt Creator for some reason creates *.user.$version files, so exclude it too *.user* *~ @@ -47,7 +48,6 @@ Network Trash Folder Temporary Items .apdisk - ### CMake ### CMakeCache.txt CMakeFiles @@ -307,6 +307,10 @@ build-* # Android *.apk +*.config +*.creator +*.includes +*.files # Waf .waf-* diff --git a/contib/a1batross/xash3d.config b/contib/a1batross/xash3d.config new file mode 100644 index 00000000..e0284f42 --- /dev/null +++ b/contib/a1batross/xash3d.config @@ -0,0 +1,2 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 diff --git a/contib/a1batross/xash3d.creator b/contib/a1batross/xash3d.creator new file mode 100644 index 00000000..e94cbbd3 --- /dev/null +++ b/contib/a1batross/xash3d.creator @@ -0,0 +1 @@ +[General] diff --git a/contib/a1batross/xash3d.files b/contib/a1batross/xash3d.files new file mode 100644 index 00000000..9dc843a1 --- /dev/null +++ b/contib/a1batross/xash3d.files @@ -0,0 +1,374 @@ +common/backends.h +common/beamdef.h +common/bspfile.h +common/cl_entity.h +common/com_model.h +common/con_nprint.h +common/const.h +common/cvardef.h +common/defaults.h +common/demo_api.h +common/dlight.h +common/enginefeatures.h +common/entity_state.h +common/entity_types.h +common/event_api.h +common/event_args.h +common/event_flags.h +common/gameinfo.h +common/hltv.h +common/ivoicetweak.h +common/kbutton.h +common/lightstyle.h +common/mathlib.h +common/net_api.h +common/netadr.h +common/particledef.h +common/pmtrace.h +common/port.h +common/qfont.h +common/r_efx.h +common/r_studioint.h +common/ref_params.h +common/render_api.h +common/screenfade.h +common/studio_event.h +common/triangleapi.h +common/usercmd.h +common/wadfile.h +common/weaponinfo.h +common/wrect.h +common/xash3d_types.h +engine/alias.h +engine/anorms.h +engine/cdll_exp.h +engine/cdll_int.h +engine/client/avi/avi.h +engine/client/avi/avi_stub.c +engine/client/avi/avi_win.c +engine/client/cl_cmds.c +engine/client/cl_custom.c +engine/client/cl_demo.c +engine/client/cl_events.c +engine/client/cl_frame.c +engine/client/cl_game.c +engine/client/cl_gameui.c +engine/client/cl_main.c +engine/client/cl_mobile.c +engine/client/cl_netgraph.c +engine/client/cl_parse.c +engine/client/cl_pmove.c +engine/client/cl_remap.c +engine/client/cl_scrn.c +engine/client/cl_tent.c +engine/client/cl_tent.h +engine/client/cl_video.c +engine/client/cl_view.c +engine/client/client.h +engine/client/console.c +engine/client/gl_alias.c +engine/client/gl_backend.c +engine/client/gl_beams.c +engine/client/gl_cull.c +engine/client/gl_decals.c +engine/client/gl_draw.c +engine/client/gl_export.h +engine/client/gl_frustum.c +engine/client/gl_frustum.h +engine/client/gl_image.c +engine/client/gl_local.h +engine/client/gl_refrag.c +engine/client/gl_rlight.c +engine/client/gl_rmain.c +engine/client/gl_rmath.c +engine/client/gl_rmisc.c +engine/client/gl_rpart.c +engine/client/gl_rsurf.c +engine/client/gl_sprite.c +engine/client/gl_studio.c +engine/client/gl_warp.c +engine/client/in_evdev.c +engine/client/in_joy.c +engine/client/in_touch.c +engine/client/input.c +engine/client/input.h +engine/client/keys.c +engine/client/s_dsp.c +engine/client/s_load.c +engine/client/s_main.c +engine/client/s_mix.c +engine/client/s_mouth.c +engine/client/s_stream.c +engine/client/s_utils.c +engine/client/s_vox.c +engine/client/sound.h +engine/client/titles.c +engine/client/vgui/vgui_draw.c +engine/client/vgui/vgui_draw.h +engine/client/vid_common.c +engine/client/vid_common.h +engine/client/vox.h +engine/common/build.c +engine/common/cfgscript.c +engine/common/cmd.c +engine/common/com_strings.h +engine/common/common.c +engine/common/common.h +engine/common/con_utils.c +engine/common/crashhandler.c +engine/common/crclib.c +engine/common/crtlib.c +engine/common/crtlib.h +engine/common/custom.c +engine/common/cvar.c +engine/common/cvar.h +engine/common/dedicated.c +engine/common/filesystem.c +engine/common/filesystem.h +engine/common/gamma.c +engine/common/host.c +engine/common/host_state.c +engine/common/hpak.c +engine/common/identification.c +engine/common/imagelib/imagelib.h +engine/common/imagelib/img_bmp.c +engine/common/imagelib/img_dds.c +engine/common/imagelib/img_main.c +engine/common/imagelib/img_quant.c +engine/common/imagelib/img_tga.c +engine/common/imagelib/img_utils.c +engine/common/imagelib/img_wad.c +engine/common/infostring.c +engine/common/launcher.c +engine/common/lib_common.c +engine/common/lib_posix.c +engine/common/library.h +engine/common/mathlib.c +engine/common/mathlib.h +engine/common/matrixlib.c +engine/common/mod_bmodel.c +engine/common/mod_local.h +engine/common/mod_studio.c +engine/common/model.c +engine/common/net_buffer.c +engine/common/net_buffer.h +engine/common/net_chan.c +engine/common/net_encode.c +engine/common/net_encode.h +engine/common/net_ws.c +engine/common/net_ws.h +engine/common/netchan.h +engine/common/pm_debug.c +engine/common/pm_local.h +engine/common/pm_surface.c +engine/common/pm_trace.c +engine/common/protocol.h +engine/common/sequence.c +engine/common/soundlib/libmpg/dct36.c +engine/common/soundlib/libmpg/dct64.c +engine/common/soundlib/libmpg/fmt123.h +engine/common/soundlib/libmpg/format.c +engine/common/soundlib/libmpg/frame.c +engine/common/soundlib/libmpg/frame.h +engine/common/soundlib/libmpg/getbits.h +engine/common/soundlib/libmpg/huffman.h +engine/common/soundlib/libmpg/index.c +engine/common/soundlib/libmpg/index.h +engine/common/soundlib/libmpg/layer3.c +engine/common/soundlib/libmpg/libmpg.c +engine/common/soundlib/libmpg/libmpg.h +engine/common/soundlib/libmpg/mpeghead.h +engine/common/soundlib/libmpg/mpg123.c +engine/common/soundlib/libmpg/mpg123.h +engine/common/soundlib/libmpg/parse.c +engine/common/soundlib/libmpg/reader.c +engine/common/soundlib/libmpg/reader.h +engine/common/soundlib/libmpg/sample.h +engine/common/soundlib/libmpg/synth.c +engine/common/soundlib/libmpg/synth.h +engine/common/soundlib/libmpg/tabinit.c +engine/common/soundlib/snd_main.c +engine/common/soundlib/snd_mp3.c +engine/common/soundlib/snd_utils.c +engine/common/soundlib/snd_wav.c +engine/common/soundlib/soundlib.h +engine/common/sys_con.c +engine/common/system.c +engine/common/system.h +engine/common/world.c +engine/common/world.h +engine/common/zone.c +engine/custom.h +engine/customentity.h +engine/edict.h +engine/eiface.h +engine/keydefs.h +engine/menu_int.h +engine/mobility_int.h +engine/physint.h +engine/platform/android/android_lib.c +engine/platform/android/android_lib.h +engine/platform/apple/ios_lib.c +engine/platform/apple/ios_lib.h +engine/platform/emscripten/em_lib.c +engine/platform/emscripten/em_lib.h +engine/platform/sdl/events.c +engine/platform/sdl/events.h +engine/platform/sdl/s_backend.c +engine/platform/sdl/vid_sdl.c +engine/platform/win32/win_con.c +engine/platform/win32/win_lib.c +engine/progdefs.h +engine/sequence.h +engine/server/server.h +engine/server/sv_client.c +engine/server/sv_cmds.c +engine/server/sv_custom.c +engine/server/sv_frame.c +engine/server/sv_game.c +engine/server/sv_init.c +engine/server/sv_log.c +engine/server/sv_main.c +engine/server/sv_move.c +engine/server/sv_phys.c +engine/server/sv_pmove.c +engine/server/sv_save.c +engine/server/sv_world.c +engine/shake.h +engine/sprite.h +engine/studio.h +engine/vgui_api.h +engine/warpsin.h +game_launch/game.cpp +mainui/BaseMenu.cpp +mainui/BaseMenu.h +mainui/Btns.cpp +mainui/BtnsBMPTable.h +mainui/CFGScript.cpp +mainui/CFGScript.h +mainui/Color.cpp +mainui/Color.h +mainui/Coord.h +mainui/EngineCallback.cpp +mainui/EventSystem.cpp +mainui/EventSystem.h +mainui/MenuStrings.cpp +mainui/MenuStrings.h +mainui/Primitive.h +mainui/Scissor.cpp +mainui/Scissor.h +mainui/Utils.cpp +mainui/Utils.h +mainui/controls/Action.cpp +mainui/controls/Action.h +mainui/controls/BackgroundBitmap.cpp +mainui/controls/BackgroundBitmap.h +mainui/controls/BaseItem.cpp +mainui/controls/BaseItem.h +mainui/controls/BaseWindow.cpp +mainui/controls/BaseWindow.h +mainui/controls/Bitmap.cpp +mainui/controls/Bitmap.h +mainui/controls/CheckBox.cpp +mainui/controls/CheckBox.h +mainui/controls/Editable.cpp +mainui/controls/Editable.h +mainui/controls/Field.cpp +mainui/controls/Field.h +mainui/controls/Framework.cpp +mainui/controls/Framework.h +mainui/controls/ItemsHolder.cpp +mainui/controls/ItemsHolder.h +mainui/controls/MessageBox.cpp +mainui/controls/MessageBox.h +mainui/controls/PicButton.cpp +mainui/controls/PicButton.h +mainui/controls/PlayerModelView.cpp +mainui/controls/PlayerModelView.h +mainui/controls/ProgressBar.cpp +mainui/controls/ProgressBar.h +mainui/controls/ScrollView.cpp +mainui/controls/ScrollView.h +mainui/controls/Slider.cpp +mainui/controls/Slider.h +mainui/controls/SpinControl.cpp +mainui/controls/SpinControl.h +mainui/controls/Switch.cpp +mainui/controls/Switch.h +mainui/controls/TabView.cpp +mainui/controls/TabView.h +mainui/controls/Table.cpp +mainui/controls/Table.h +mainui/controls/YesNoMessageBox.cpp +mainui/controls/YesNoMessageBox.h +mainui/enginecallback_menu.h +mainui/extdll_menu.h +mainui/font/BaseFontBackend.cpp +mainui/font/BaseFontBackend.h +mainui/font/BitmapFont.cpp +mainui/font/BitmapFont.h +mainui/font/FontManager.cpp +mainui/font/FontManager.h +mainui/font/FreeTypeFont.cpp +mainui/font/FreeTypeFont.h +mainui/font/StbFont.cpp +mainui/font/StbFont.h +mainui/font/WinAPIFont.cpp +mainui/font/WinAPIFont.h +mainui/font/stb_truetype.h +mainui/legacy/menu_playdemo.cpp +mainui/legacy/menu_playrec.cpp +mainui/legacy/menu_recdemo.cpp +mainui/menufont.h +mainui/menus/AdvancedControls.cpp +mainui/menus/Audio.cpp +mainui/menus/Configuration.cpp +mainui/menus/ConnectionProgress.cpp +mainui/menus/ConnectionProgress.h +mainui/menus/ConnectionWarning.cpp +mainui/menus/ConnectionWarning.h +mainui/menus/Controls.cpp +mainui/menus/CreateGame.cpp +mainui/menus/Credits.cpp +mainui/menus/CustomGame.cpp +mainui/menus/FileDialog.cpp +mainui/menus/GameOptions.cpp +mainui/menus/Gamepad.cpp +mainui/menus/InputDevices.cpp +mainui/menus/LoadGame.cpp +mainui/menus/Main.cpp +mainui/menus/Multiplayer.cpp +mainui/menus/NewGame.cpp +mainui/menus/PlayerIntroduceDialog.cpp +mainui/menus/PlayerIntroduceDialog.h +mainui/menus/PlayerSetup.cpp +mainui/menus/SaveLoad.cpp +mainui/menus/ServerBrowser.cpp +mainui/menus/Touch.cpp +mainui/menus/TouchButtons.cpp +mainui/menus/TouchEdit.cpp +mainui/menus/TouchOptions.cpp +mainui/menus/Video.cpp +mainui/menus/VideoModes.cpp +mainui/menus/VideoOptions.cpp +mainui/menus/Zoo.cpp +mainui/menus/dynamic/ScriptMenu.cpp +mainui/model/BaseArrayModel.h +mainui/model/BaseModel.h +mainui/model/StringArrayModel.h +mainui/udll_int.cpp +mainui/utl/utlmemory.h +mainui/utl/utlrbtree.h +mainui/utl/utlvector.h +pm_shared/pm_defs.h +pm_shared/pm_info.h +pm_shared/pm_movevars.h +vgui_support/utlmemory.h +vgui_support/utlrbtree.h +vgui_support/utlvector.h +vgui_support/vgui_clip.cpp +vgui_support/vgui_font.cpp +vgui_support/vgui_input.cpp +vgui_support/vgui_int.cpp +vgui_support/vgui_main.h +vgui_support/vgui_surf.cpp diff --git a/contib/a1batross/xash3d.includes b/contib/a1batross/xash3d.includes new file mode 100644 index 00000000..04720c01 --- /dev/null +++ b/contib/a1batross/xash3d.includes @@ -0,0 +1,22 @@ +engine +engine/client +engine/client/avi +engine/client/vgui +engine/common +engine/common/imagelib +engine/common/soundlib +engine/common/soundlib/libmpg +engine/platform/android +engine/platform/apple +engine/platform/emscripten +engine/platform/sdl +engine/server +mainui +mainui/controls +mainui/font +mainui/menus +mainui/model +mainui/utl +pm_shared +vgui_support +common From aa5d52cebc67c6440fd9ddbfce019619b094a8ea Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 May 2018 23:44:52 +0300 Subject: [PATCH 005/205] Change libxashmenu to libmenu to keep same names between different OSes --- common/port.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/port.h b/common/port.h index a0de7c40..2be6a948 100644 --- a/common/port.h +++ b/common/port.h @@ -67,15 +67,15 @@ GNU General Public License for more details. #define SERVERDLL "libserver" POSTFIX "." OS_LIB_EXT #define GAMEPATH "/sdcard/xash" #else - #define MENUDLL "libxashmenu" ARCH_SUFFIX "." OS_LIB_EXT - #define CLIENTDLL "client" ARCH_SUFFIX "." OS_LIB_EXT + #define MENUDLL "libmenu" ARCH_SUFFIX "." OS_LIB_EXT + #define CLIENTDLL "client" ARCH_SUFFIX "." OS_LIB_EXT #endif #define VGUI_SUPPORT_DLL "libvgui_support." OS_LIB_EXT // Windows-specific #define __cdecl - #define __stdcall + #define __stdcall #define _inline static inline #define O_BINARY 0 // O_BINARY is Windows extension #define O_TEXT 0 // O_TEXT is Windows extension From fac536f8c4f8e54dd054a9d9d92b20bba8b83697 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 May 2018 23:47:38 +0300 Subject: [PATCH 006/205] Add mainui_cpp. Use appropriate message in vgui_support's wscript. --- .gitmodules | 3 +++ mainui | 1 + vgui_support/wscript | 2 +- wscript | 3 +-- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .gitmodules create mode 160000 mainui diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f275c17e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mainui"] + path = mainui + url = https://github.com/FWGS/mainui_cpp diff --git a/mainui b/mainui new file mode 160000 index 00000000..a1c3ad25 --- /dev/null +++ b/mainui @@ -0,0 +1 @@ +Subproject commit a1c3ad25311549999f67dd2a9bc8f5d153bf9355 diff --git a/vgui_support/wscript b/vgui_support/wscript index e73d0b15..65da5144 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -40,7 +40,7 @@ def configure(conf): conf.env.HAVE_VGUI = 1 - Logs.info('VGUI configured as {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) + conf.msg('Checking VGUI', '{0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) diff --git a/wscript b/wscript index 4b05d1af..afe3fc06 100644 --- a/wscript +++ b/wscript @@ -21,7 +21,7 @@ def get_git_version(): VERSION = '0.99' APPNAME = 'xash3d-fwgs' GIT_SHA = get_git_version() -SUBDIRS = [ 'game_launch', 'vgui_support', 'engine' ] +SUBDIRS = [ 'game_launch', 'vgui_support', 'engine', 'mainui' ] top = '.' @@ -72,7 +72,6 @@ def configure(conf): else: conf.env.append_unique('CFLAGS', '-Og') conf.env.append_unique('CFLAGS', '-g') - conf.check( lib='dl' ) conf.check( lib='m' ) From 79d8bc2cdde141b87b7ec37722619e40c78f73c8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 29 May 2018 00:20:07 +0300 Subject: [PATCH 007/205] Get rid of accidentally added CMakeLists --- vgui_support/CMakeLists.txt | 89 ------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 vgui_support/CMakeLists.txt diff --git a/vgui_support/CMakeLists.txt b/vgui_support/CMakeLists.txt deleted file mode 100644 index 68271295..00000000 --- a/vgui_support/CMakeLists.txt +++ /dev/null @@ -1,89 +0,0 @@ -# -# Copyright (c) 2015 Pavlo Lavrenenko -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -cmake_minimum_required(VERSION 2.8.0) -project(VGUI_SUPPORT) - -set(VGUI_SUPPORT vgui_support) -fwgs_fix_default_msvc_settings() -cmake_dependent_option(VGUI_SUPPORT_OLD_VGUI_BINARY "Build against old version of VGUI, from HLSDK2.3" OFF "MSVC" OFF) - -if(NOT MINGW) - file(GLOB VGUI_SUPPORT_SOURCES *.cpp *.c) -else() - # Download prebuilt VGUI_support, as there is no way to have same C++ ABI for VC++ and MinGW. - # Also there is no way to have a target without source code, so let's just have custom install() rule - - # Prebuilt VGUI support is downloaded from github.com/FWGS/vgui_support_bin - set(FORCE_UNPACK FALSE) - message(STATUS "Downloading prebuilt vgui_support for MinGW... See vgui_support/CMakeLists.txt for details") - if(NOT EXISTS ${CMAKE_BINARY_DIR}/vgui_support.zip) - file(DOWNLOAD https://github.com/FWGS/vgui_support_bin/archive/master.zip ${CMAKE_BINARY_DIR}/vgui_support.zip) - set(FORCE_UNPACK TRUE) - endif() - - if(NOT EXISTS ${CMAKE_BINARY_DIR}/vgui_support_prebuilt) - set(FORCE_UNPACK TRUE) - endif() - - if(FORCE_UNPACK) - fwgs_unpack_file(${CMAKE_BINARY_DIR}/vgui_support.zip vgui_support_prebuilt) - endif() - - # HACKHACK: create empty target - execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/u_cant_touch_this.cpp) - set(VGUI_SUPPORT_SOURCES ${CMAKE_BINARY_DIR}/u_cant_touch_this.cpp) -endif() -include_directories( . ../common ../engine ../engine/common ../engine/client ../engine/client/vgui ) - -add_library(${VGUI_SUPPORT} SHARED ${VGUI_SUPPORT_SOURCES}) - -set(VGUI_BRANCH "master") -if(VGUI_SUPPORT_OLD_VGUI_BINARY) - set(VGUI_BRANCH "pre-2013") -endif() -fwgs_library_dependency(${VGUI_SUPPORT} VGUI - "https://github.com/FWGS/vgui-dev/archive/${VGUI_BRANCH}.zip" "VGUI.zip" "HL_SDK_DIR" "vgui-dev-${VGUI_BRANCH}") -if(MSVC) - string(REGEX REPLACE "lib$" "dll" VGUI_DLL "${VGUI_LIBRARY}") - install(FILES ${VGUI_DLL} - CONFIGURATIONS Debug - DESTINATION ${LIB_INSTALL_DIR}/Debug/) - install(FILES ${VGUI_DLL} - CONFIGURATIONS Release - DESTINATION ${LIB_INSTALL_DIR}/Release/) -endif() - -fwgs_set_default_properties(${VGUI_SUPPORT}) -if(NOT MINGW) - fwgs_install(${VGUI_SUPPORT}) - - if(NOT WIN32 AND NOT XASH_NO_INSTALL_VGUI_BIN) - install(FILES ${VGUI_LIBRARY} DESTINATION ${LIB_INSTALL_DIR}/${LIB_INSTALL_SUBDIR} - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) - endif() -else() - # Use prebuilt. - # TODO: this allows only HLSDK 2.4 VGUI - install(FILES ${CMAKE_BINARY_DIR}/vgui_support_prebuilt/vgui_support_bin-master/vgui_support.dll - DESTINATION ${LIB_INSTALL_DIR}/${LIB_INSTALL_SUBDIR}) -endif() From 236c16e35f198f57d1d0856ed612d353fedbf3a4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 29 May 2018 00:58:45 +0300 Subject: [PATCH 008/205] Update QtCreator project --- contib/a1batross/xash3d.creator.user | 216 +++++++++++++++++++++++++++ contib/a1batross/xash3d.files | 5 + 2 files changed, 221 insertions(+) create mode 100644 contib/a1batross/xash3d.creator.user diff --git a/contib/a1batross/xash3d.creator.user b/contib/a1batross/xash3d.creator.user new file mode 100644 index 00000000..262b4a06 --- /dev/null +++ b/contib/a1batross/xash3d.creator.user @@ -0,0 +1,216 @@ + + + + + + EnvironmentId + {e98eea9b-9ef7-4df9-8f97-1e67893b8b3b} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {cc7c1b7f-457b-4e71-9eeb-5e67c737c51a} + 0 + 0 + 0 + + /home/a1ba/projects/xash/xash3d-fwgs + + + true + configure --vgui=vgui-dev --win-style-install + ./waf + %{buildDir} + ОÑобый + + ProjectExplorer.ProcessStep + + + + false + build -j5 -v + ./waf + true + Сборка + + GenericProjectManager.GenericMakeStep + + 2 + Сборка + + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + + ./waf + true + Сборка + + GenericProjectManager.GenericMakeStep + + 1 + ОчиÑтка + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + По умолчанию + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + + true + install --destdir=/home/a1ba/projects/builtXash + ./waf + %{buildDir} + ОÑобый + + ProjectExplorer.ProcessStep + + 1 + УÑтановка + + ProjectExplorer.BuildSteps.Deploy + + 1 + Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ ÑƒÑтановка + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + LD_LIBRARY_PATH=/home/a1ba/projects/builtXash + + -dev 5 + /home/a1ba/projects/builtXash/xash3d + /home/a1ba/projects/builtXash + ЗапуÑк /home/a1ba/projects/builtXash/xash3d + + ProjectExplorer.CustomExecutableRunConfiguration + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/contib/a1batross/xash3d.files b/contib/a1batross/xash3d.files index 9dc843a1..3a145cd8 100644 --- a/contib/a1batross/xash3d.files +++ b/contib/a1batross/xash3d.files @@ -239,7 +239,10 @@ engine/sprite.h engine/studio.h engine/vgui_api.h engine/warpsin.h +engine/wscript game_launch/game.cpp +game_launch/wscript +mainui/wscript mainui/BaseMenu.cpp mainui/BaseMenu.h mainui/Btns.cpp @@ -372,3 +375,5 @@ vgui_support/vgui_input.cpp vgui_support/vgui_int.cpp vgui_support/vgui_main.h vgui_support/vgui_surf.cpp +vgui_support/wscript +wscript From 0db8d95bc9ea4aea18a1c4d3acc6f57ee58d4b4e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 29 May 2018 01:02:32 +0300 Subject: [PATCH 009/205] Update mainui. Add possibility to install engine ignoring *nix file hierarchy in wscript. Fix debugging. --- engine/wscript | 6 ++++-- game_launch/wscript | 3 ++- mainui | 2 +- vgui_support/wscript | 3 ++- wscript | 16 ++++++++++++++++ 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/engine/wscript b/engine/wscript index 4b63c1b6..c53d26c8 100644 --- a/engine/wscript +++ b/engine/wscript @@ -68,7 +68,8 @@ def build(bld): target = 'xash', features = 'c cprogram', includes = includes, - use = libs + use = libs, + install_path = bld.env.BINDIR ) else: bld.shlib( @@ -76,5 +77,6 @@ def build(bld): target = 'xash', features = 'c', includes = includes, - use = libs + use = libs, + install_path = bld.env.LIBDIR ) diff --git a/game_launch/wscript b/game_launch/wscript index f298b54d..38e58914 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -54,5 +54,6 @@ def build(bld): target = 'xash3d', # hl.exe features = 'c cprogram', includes = includes, - use = libs + use = libs, + install_path = bld.env.BINDIR ) diff --git a/mainui b/mainui index a1c3ad25..b82d68cc 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit a1c3ad25311549999f67dd2a9bc8f5d153bf9355 +Subproject commit b82d68cc40b144a0114a76fa5dfabbc8e3fddf29 diff --git a/vgui_support/wscript b/vgui_support/wscript index 65da5144..b4f59c29 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -67,4 +67,5 @@ def build(bld): target = 'vgui_support', features = 'cxx', includes = includes, - use = libs) + use = libs, + install_path = bld.env.LIBDIR) diff --git a/wscript b/wscript index afe3fc06..0aa3d3c7 100644 --- a/wscript +++ b/wscript @@ -41,6 +41,10 @@ def options(opt): opt.add_option( '--release', action = 'store_true', dest = 'RELEASE', default=False, help = 'strip debug info from binary and enable optimizations') + + opt.add_option( + '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, + help = 'install like Windows build, ignore prefix, useful for development') opt.recurse(SUBDIRS) @@ -69,9 +73,12 @@ def configure(conf): if(conf.options.RELEASE): conf.env.append_unique('CFLAGS', '-O2') + conf.env.append_unique('CXXFLAGS', '-O2') else: conf.env.append_unique('CFLAGS', '-Og') conf.env.append_unique('CFLAGS', '-g') + conf.env.append_unique('CXXFLAGS', '-Og') + conf.env.append_unique('CXXFLAGS', '-g') conf.check( lib='dl' ) conf.check( lib='m' ) @@ -80,6 +87,15 @@ def configure(conf): conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.DEDICATED + # indicate if we are packaging for Linux/BSD + if(not conf.options.WIN_INSTALL and + conf.env.DEST_OS != 'win32' and + conf.env.DEST_OS != 'darwin'): + conf.env.LIBDIR = conf.env.BINDIR = '${PREFIX}/lib/xash3d' + else: + # prefix is ignored + conf.env.LIBDIR = conf.env.BINDIR = '/' + # global conf.env.append_unique('XASH_BUILD_COMMIT', GIT_SHA) From 69549787bcd22f74570253251034def93fa9971c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jun 2018 19:29:47 +0300 Subject: [PATCH 010/205] net_ws refactoring, fix minor bugs --- engine/common/net_ws.c | 348 ++++++++++++++++------------------------- 1 file changed, 138 insertions(+), 210 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index c0cd1ce6..6d6eec87 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -119,7 +119,6 @@ static dllfunc_t kernel32_funcs[] = dll_info_t kernel32_dll = { "kernel32.dll", kernel32_funcs, false }; - static void NET_InitializeCriticalSections( void ); qboolean NET_OpenWinSock( void ) @@ -136,7 +135,7 @@ void NET_FreeWinSock( void ) { Sys_FreeLibrary( &winsock_dll ); } -#else +#else // _WIN32 #define pHtons htons #define pConnect connect #define pInet_Addr inet_addr @@ -157,9 +156,47 @@ void NET_FreeWinSock( void ) #define pGetHostByName gethostbyname #define pSelect select #define pGetAddrInfo getaddrinfo +#define pWSAGetLastError() errno + #define SOCKET int -#define INVALID_SOCKET 0 -#endif +#define INVALID_SOCKET -1 + +#define WSAEINTR EINTR +#define WSAEBADF EBADF +#define WSAEACCES EACCES +#define WSAEFAULT EFAULT +#define WSAEINVAL EINVAL +#define WSAEMFILE EMFILE +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINPROGRESS EINPROGRESS +#define WSAEALREADY EALREADY +#define WSAENOTSOCK ENOTSOCK +#define WSAEDESTADDRREQ EDESTADDRREQ +#define WSAEMSGSIZE EMSGSIZE +#define WSAEPROTOTYPE EPROTOTYPE +#define WSAENOPROTOOPT ENOPROTOOPT +#define WSAEPROTONOSUPPORT EPROTONOSUPPORT +#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT +#define WSAEOPNOTSUPP EOPNOTSUPP +#define WSAEPFNOSUPPORT EPFNOSUPPORT +#define WSAEAFNOSUPPORT EAFNOSUPPORT +#define WSAEADDRINUSE EADDRINUSE +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL +#define WSAENETDOWN ENETDOWN +#define WSAENETUNREACH ENETUNREACH +#define WSAENETRESET ENETRESET +#define WSAECONNABORTED ECONNABORTED +#define WSAECONNRESET ECONNRESET +#define WSAENOBUFS ENOBUFS +#define WSAEISCONN EISCONN +#define WSAENOTCONN ENOTCONN +#define WSAESHUTDOWN ESHUTDOWN +#define WSAETOOMANYREFS ETOOMANYREFS +#define WSAETIMEDOUT ETIMEDOUT +#define WSAECONNREFUSED ECONNREFUSED +#define WSAELOOP ELOOP +#define WSAENAMETOOLONG ENAMETOOLONG +#define WSAEHOSTDOWN EHOSTDOWN #ifdef __EMSCRIPTEN__ /* All socket operations are non-blocking already */ @@ -169,7 +206,8 @@ static int ioctl_stub( int d, unsigned long r, ...) } #undef pIoctlSocket #define pIoctlSocket ioctl_stub -#endif +#endif // __EMSCRIPTEN__ +#endif // !_WIN32 typedef struct { @@ -223,6 +261,7 @@ typedef struct long sequence_number; int ip_sockets[NS_COUNT]; qboolean initialized; + qboolean threads_initialized; qboolean configured; qboolean allow_ip; #ifdef _WIN32 @@ -260,7 +299,6 @@ char *NET_ErrorString( void ) case WSAEINTR: return "WSAEINTR"; case WSAEBADF: return "WSAEBADF"; case WSAEACCES: return "WSAEACCES"; - case WSAEDISCON: return "WSAEDISCON"; case WSAEFAULT: return "WSAEFAULT"; case WSAEINVAL: return "WSAEINVAL"; case WSAEMFILE: return "WSAEMFILE"; @@ -294,6 +332,7 @@ char *NET_ErrorString( void ) case WSAELOOP: return "WSAELOOP"; case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; + case WSAEDISCON: return "WSAEDISCON"; case WSASYSNOTREADY: return "WSASYSNOTREADY"; case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; case WSANOTINITIALISED: return "WSANOTINITIALISED"; @@ -322,7 +361,7 @@ _inline qboolean NET_IsSocketValid( int socket ) #ifdef _WIN32 return socket != INVALID_SOCKET; #else - return socket; + return socket >= 0; #endif } @@ -364,12 +403,52 @@ static void NET_SockadrToNetadr( struct sockaddr *s, netadr_t *a ) } } +/* +============ +NET_GetHostByName +============ +*/ +int NET_GetHostByName( const char *hostname ) +{ +#ifdef HAVE_GETADDRINFO + struct addrinfo *ai = NULL, *cur; + struct addrinfo hints; + int ip; + + memset( &hints, 0, sizeof( hints )); + hints.ai_family = AF_INET; + + if( !pGetAddrInfo( hostname, NULL, &hints, &ai )) + { + for( cur = ai; cur; cur = cur->ai_next ) + { + if( cur->ai_family == AF_INET ) + { + ip = *((int*)&((struct sockaddr_in *)cur->ai_addr)->sin_addr); + break; + } + } + + if( ai ) + freeaddrinfo( ai ); + } + + return ip; +#else + struct hostent *h; + if(!( h = pGetHostByName( copy ))) + return 0; + return *(int *)h->h_addr_list[0]; +#endif +} + #if !defined XASH_NO_ASYNC_NS_RESOLVE && ( defined _WIN32 || !defined __EMSCRIPTEN__ ) #define CAN_ASYNC_NS_RESOLVE #endif #ifdef CAN_ASYNC_NS_RESOLVE static void NET_ResolveThread( void ); + #if !defined _WIN32 #include #define mutex_lock pthread_mutex_lock @@ -385,14 +464,14 @@ void *NET_ThreadStart( void *unused ) NET_ResolveThread(); return NULL; } - #else // WIN32 -struct cs { +typedef struct cs +{ void* p1; int i1, i2; void *p2, *p3; uint i4; -}; +} mutex_t; #define mutex_lock pEnterCriticalSection #define mutex_unlock pLeaveCriticalSection #define detach_thread( x ) CloseHandle(x) @@ -405,7 +484,7 @@ DWORD WINAPI NET_ThreadStart( LPVOID unused ) ExitThread(0); return 0; } -#endif +#endif // !_WIN32 #ifdef DEBUG_RESOLVE #define RESOLVE_DBG(x) Sys_PrintLog(x) @@ -430,6 +509,7 @@ static struct nsthread_s #ifdef _WIN32 static void NET_InitializeCriticalSections( void ) { + net.threads_initialized = true; pInitializeCriticalSection( &nsthread.mutexns ); pInitializeCriticalSection( &nsthread.mutexres ); } @@ -437,75 +517,29 @@ static void NET_InitializeCriticalSections( void ) void NET_ResolveThread( void ) { -#ifdef HAVE_GETADDRINFO - struct addrinfo *ai = NULL, *cur; - struct addrinfo hints; int sin_addr = 0; RESOLVE_DBG( "[resolve thread] starting resolve for " ); RESOLVE_DBG( nsthread.hostname ); +#ifdef HAVE_GETADDRINFO RESOLVE_DBG( " with getaddrinfo\n" ); - memset( &hints, 0, sizeof( hints ) ); - hints.ai_family = AF_INET; - if( !pGetAddrInfo( nsthread.hostname, NULL, &hints, &ai ) ) - { - for( cur = ai; cur; cur = cur->ai_next ) { - if( cur->ai_family == AF_INET ) { - sin_addr = *((int*)&((struct sockaddr_in *)cur->ai_addr)->sin_addr); - freeaddrinfo( ai ); - ai = NULL; - break; - } - } +#else + RESOLVE_DBG( " with gethostbyname\n" ); +#endif - if( ai ) - freeaddrinfo( ai ); - } + sin_addr = NET_GetHostByName( nsthread.hostname ); if( sin_addr ) - RESOLVE_DBG( "[resolve thread] getaddrinfo success\n" ); + RESOLVE_DBG( "[resolve thread] success\n" ); else - RESOLVE_DBG( "[resolve thread] getaddrinfo failed\n" ); + RESOLVE_DBG( "[resolve thread] failed\n" ); mutex_lock( &nsthread.mutexres ); nsthread.result = sin_addr; nsthread.busy = false; RESOLVE_DBG( "[resolve thread] returning result\n" ); mutex_unlock( &nsthread.mutexres ); RESOLVE_DBG( "[resolve thread] exiting thread\n" ); -#else - struct hostent *res; - - RESOLVE_DBG( "[resolve thread] starting resolve for " ); - RESOLVE_DBG( nsthread.hostname ); - RESOLVE_DBG( " with gethostbyname\n" ); - - mutex_lock( &nsthread.mutexns ); - RESOLVE_DBG( "[resolve thread] locked gethostbyname mutex\n" ); - res = pGetHostByName( nsthread.hostname ); - if(res) - RESOLVE_DBG( "[resolve thread] gethostbyname success\n" ); - else - RESOLVE_DBG( "[resolve thread] gethostbyname failed\n" ); - - mutex_lock( &nsthread.mutexres ); - RESOLVE_DBG( "[resolve thread] returning result\n" ); - if( res ) - nsthread.result = *(int *)res->h_addr_list[0]; - else - nsthread.result = 0; - - nsthread.busy = false; - - mutex_unlock( &nsthread.mutexns ); - - RESOLVE_DBG( "[resolve thread] unlocked gethostbyname mutex\n" ); - - mutex_unlock( &nsthread.mutexres ); - - RESOLVE_DBG( "[resolve thread] exiting thread\n" ); -#endif } - #endif // CAN_ASYNC_NS_RESOLVE @@ -526,7 +560,8 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean char *colon; char copy[128]; - if( !net.initialized ) return false; + if( !net.initialized ) + return false; memset( sadr, 0, sizeof( *sadr )); @@ -551,122 +586,50 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean } else { + qboolean asyncfailed = true; + #ifdef CAN_ASYNC_NS_RESOLVE - qboolean asyncfailed = false; -#ifdef _WIN32 - if( pInitializeCriticalSection ) -#endif // _WIN32 + if( net.threads_initialized && !nonblocking ) { - if( !nonblocking ) + mutex_lock( &nsthread.mutexres ); + + if( nsthread.busy ) { -#ifdef HAVE_GETADDRINFO - struct addrinfo *ai = NULL, *cur; - struct addrinfo hints; + mutex_unlock( &nsthread.mutexres ); + return 2; + } - memset( &hints, 0, sizeof( hints ) ); - hints.ai_family = AF_INET; - if( !pGetAddrInfo( copy, NULL, &hints, &ai ) ) - { - for( cur = ai; cur; cur = cur->ai_next ) - { - if( cur->ai_family == AF_INET ) - { - ip = *((int*)&((struct sockaddr_in *)cur->ai_addr)->sin_addr); - freeaddrinfo(ai); - ai = NULL; - break; - } - } - - if( ai ) - freeaddrinfo(ai); - } -#else - struct hostent *h; - - mutex_lock( &nsthread.mutexns ); - h = pGetHostByName( copy ); - if( !h ) - { - mutex_unlock( &nsthread.mutexns ); - return 0; - } - - ip = *(int *)h->h_addr_list[0]; - mutex_unlock( &nsthread.mutexns ); -#endif + if( !Q_strcmp( copy, nsthread.hostname ) ) + { + ip = nsthread.result; + nsthread.hostname[0] = 0; + detach_thread( nsthread.thread ); } else { - mutex_lock( &nsthread.mutexres ); + Q_strncpy( nsthread.hostname, copy, MAX_STRING ); + nsthread.busy = true; + mutex_unlock( &nsthread.mutexres ); - if( nsthread.busy ) + if( create_thread( NET_ThreadStart ) ) { - mutex_unlock( &nsthread.mutexres ); + asyncfailed = false; return 2; } - - if( !Q_strcmp( copy, nsthread.hostname ) ) + else // failed to create thread { - ip = nsthread.result; - nsthread.hostname[0] = 0; - detach_thread( nsthread.thread ); + MsgDev( D_ERROR, "NET_StringToSockaddr: failed to create thread!\n"); + nsthread.busy = false; } - else - { - Q_strncpy( nsthread.hostname, copy, MAX_STRING ); - nsthread.busy = true; - mutex_unlock( &nsthread.mutexres ); - - if( create_thread( NET_ThreadStart ) ) - return 2; - else // failed to create thread - { - MsgDev( D_ERROR, "NET_StringToSockaddr: failed to create thread!\n"); - nsthread.busy = false; - asyncfailed = true; - } - } - - mutex_unlock( &nsthread.mutexres ); } + + mutex_unlock( &nsthread.mutexres ); } -#ifdef _WIN32 - else - asyncfailed = true; -#else - if( asyncfailed ) -#endif // _WIN32 #endif // CAN_ASYNC_NS_RESOLVE + + if( asyncfailed ) { -#ifdef HAVE_GETADDRINFO - struct addrinfo *ai = NULL, *cur; - struct addrinfo hints; - - memset( &hints, 0, sizeof( hints ) ); - hints.ai_family = AF_INET; - if( !pGetAddrInfo( copy, NULL, &hints, &ai ) ) - { - for( cur = ai; cur; cur = cur->ai_next ) - { - if( cur->ai_family == AF_INET ) - { - ip = *((int*)&((struct sockaddr_in *)cur->ai_addr)->sin_addr); - freeaddrinfo(ai); - ai = NULL; - break; - } - } - - if( ai ) - freeaddrinfo(ai); - } -#else - struct hostent *h; - if(!( h = pGetHostByName( copy ))) - return 0; - ip = *(int *)h->h_addr_list[0]; -#endif + ip = NET_GetHostByName( copy ); } if( !ip ) @@ -1272,7 +1235,6 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len } else { -#ifdef _WIN32 int err = pWSAGetLastError(); switch( err ) @@ -1286,19 +1248,6 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len MsgDev( D_ERROR, "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); break; } -#else - switch( errno ) - { - case EWOULDBLOCK: - case ECONNRESET: - case ECONNREFUSED: - case EMSGSIZE: - break; - default: // let's continue even after errors - MsgDev( D_ERROR, "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); - break; - } -#endif } } @@ -1434,37 +1383,21 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to if( NET_IsSocketError( ret )) { - int err = 0; - { -#ifdef _WIN32 - err = pWSAGetLastError(); + int err = pWSAGetLastError(); - // WSAEWOULDBLOCK is silent - if( err == WSAEWOULDBLOCK ) - return; + // WSAEWOULDBLOCK is silent + if( err == WSAEWOULDBLOCK ) + return; - // some PPP links don't allow broadcasts - if( err == WSAEADDRNOTAVAIL && to.type == NA_BROADCAST ) - return; -#else - // WSAEWOULDBLOCK is silent - if( errno == EWOULDBLOCK ) - return; + // some PPP links don't allow broadcasts + if( err == WSAEADDRNOTAVAIL && to.type == NA_BROADCAST ) + return; - // some PPP links don't allow broadcasts - if( errno == EADDRNOTAVAIL && to.type == NA_BROADCAST ) - return; -#endif - } if( Host_IsDedicated() ) { MsgDev( D_ERROR, "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); } -#ifdef _WIN32 else if( err == WSAEADDRNOTAVAIL || err == WSAENOBUFS ) -#else - else if( errno == EADDRNOTAVAIL || errno == ENOBUFS ) -#endif { MsgDev( D_ERROR, "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); } @@ -1552,12 +1485,8 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast if( NET_IsSocketError(( net_socket = pSocket( PF_INET, SOCK_DGRAM, IPPROTO_UDP )) ) ) { -#ifdef _WIN32 - int err = pWSAGetLastError(); + err = pWSAGetLastError(); if( err != WSAEAFNOSUPPORT ) -#else - if( err != EAFNOSUPPORT ) -#endif MsgDev( D_WARN, "NET_UDPSocket: socket = %s\n", NET_ErrorString( )); return INVALID_SOCKET; } @@ -1594,12 +1523,8 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast if( NET_IsSocketError( pSetSockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval )) ) ) { -#ifdef _WIN32 err = pWSAGetLastError(); if( err != WSAENOPROTOOPT ) -#else - if( errno != ENOPROTOOPT ) -#endif Con_Printf( S_WARN "NET_UDPSocket: port: %d setsockopt IP_TOS: %s\n", port, NET_ErrorString( )); pCloseSocket( net_socket ); return INVALID_SOCKET; @@ -1887,6 +1812,9 @@ void NET_Init( void ) NET_FreeWinSock(); return; } +#else + // we have pthreads by default + net.threads_initialized = true; #endif if( Sys_CheckParm( "-noip" )) From 5a449a56ea292c2118783687bd4193a6d13c0c04 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jun 2018 19:57:54 +0300 Subject: [PATCH 011/205] Port restricted cmds and cvars from old engine. TODO: implement better stufftext filter --- engine/common/cmd.c | 162 ++++++++++++----------------------------- engine/common/crtlib.h | 2 + engine/common/cvar.h | 1 + 3 files changed, 48 insertions(+), 117 deletions(-) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index eee29541..60cd99b1 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -603,32 +603,40 @@ void Cmd_TokenizeString( char *text ) /* ============ -Cmd_AddCommand +Cmd_AddCommandEx ============ */ -void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ) +static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcommand_t function, + const char *cmd_desc, int iFlags ) { cmd_t *cmd, *cur, *prev; + if( !cmd_name || !*cmd_name ) + { + MsgDev( D_ERROR, "Cmd_AddServerCommand: NULL name\n" ); + return 0; + } + // fail if the command is a variable name if( Cvar_FindVar( cmd_name )) { - Con_Printf( S_ERROR "Cmd_AddCommand: %s already defined as a var\n", cmd_name ); - return; + MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined as a var\n", cmd_name ); + return 0; } - + // fail if the command already exists if( Cmd_Exists( cmd_name )) { - Con_Printf( S_ERROR "Cmd_AddCommand: %s already defined\n", cmd_name ); - return; + MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined\n", cmd_name ); + return 0; } // use a small malloc to avoid zone fragmentation - cmd = Z_Malloc( sizeof( cmd_t )); + cmd = Z_Malloc( sizeof( cmd_t ) ); cmd->name = copystring( cmd_name ); cmd->desc = copystring( cmd_desc ); cmd->function = function; + cmd->flags = iFlags; // insert it at the right alphanumeric position for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next ); @@ -636,6 +644,32 @@ void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_ if( prev ) prev->next = cmd; else cmd_functions = cmd; cmd->next = cur; + +#if defined(XASH_HASHED_VARS) + BaseCmd_Insert( HM_CMD, cmd, cmd->name ); +#endif + + return 1; +} + +/* +============ +Cmd_AddCommand +============ +*/ +void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ) +{ + Cmd_AddCommandEx( __FUNCTION__, cmd_name, function, cmd_desc, 0 ); +} + +/* +============ +Cmd_AddRestrictedCommand +============ +*/ +void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ) +{ + Cmd_AddCommandEx( __FUNCTION__, cmd_name, function, cmd_desc, CMD_LOCALONLY ); } /* @@ -645,41 +679,7 @@ Cmd_AddServerCommand */ void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function ) { - cmd_t *cmd, *cur, *prev; - - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddServerCommand: NULL name\n" ); - return; - } - - // fail if the command is a variable name - if( Cvar_FindVar( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined as a var\n", cmd_name ); - return; - } - - // fail if the command already exists - if( Cmd_Exists( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined\n", cmd_name ); - return; - } - - // use a small malloc to avoid zone fragmentation - cmd = Z_Malloc( sizeof( cmd_t )); - cmd->name = copystring( cmd_name ); - cmd->desc = copystring( "server command" ); - cmd->function = function; - cmd->flags = CMD_SERVERDLL; - - // insert it at the right alphanumeric position - for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next ); - - if( prev ) prev->next = cmd; - else cmd_functions = cmd; - cmd->next = cur; + Cmd_AddCommandEx( __FUNCTION__, cmd_name, function, "server command", CMD_SERVERDLL ); } /* @@ -689,43 +689,7 @@ Cmd_AddClientCommand */ int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function ) { - cmd_t *cmd, *cur, *prev; - - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddClientCommand: NULL name\n" ); - return 0; - } - - // fail if the command is a variable name - if( Cvar_FindVar( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddClientCommand: %s already defined as a var\n", cmd_name ); - return 0; - } - - // fail if the command already exists - if( Cmd_Exists( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddClientCommand: %s already defined\n", cmd_name ); - return 0; - } - - // use a small malloc to avoid zone fragmentation - cmd = Z_Malloc( sizeof( cmd_t )); - cmd->name = copystring( cmd_name ); - cmd->desc = copystring( "client command" ); - cmd->function = function; - cmd->flags = CMD_CLIENTDLL; - - // insert it at the right alphanumeric position - for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next ); - - if( prev ) prev->next = cmd; - else cmd_functions = cmd; - cmd->next = cur; - - return 1; + return Cmd_AddCommandEx( __FUNCTION__, cmd_name, function, "server command", CMD_CLIENTDLL ); } /* @@ -735,43 +699,7 @@ Cmd_AddGameUICommand */ int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function ) { - cmd_t *cmd, *cur, *prev; - - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: NULL name\n" ); - return 0; - } - - // fail if the command is a variable name - if( Cvar_FindVar( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: %s already defined as a var\n", cmd_name ); - return 0; - } - - // fail if the command already exists - if( Cmd_Exists( cmd_name )) - { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: %s already defined\n", cmd_name ); - return 0; - } - - // use a small malloc to avoid zone fragmentation - cmd = Z_Malloc( sizeof( cmd_t )); - cmd->name = copystring( cmd_name ); - cmd->desc = copystring( "GameUI command" ); - cmd->function = function; - cmd->flags = CMD_GAMEUIDLL; - - // insert it at the right alphanumeric position - for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next ); - - if( prev ) prev->next = cmd; - else cmd_functions = cmd; - cmd->next = cur; - - return 1; + return Cmd_AddCommandEx( __FUNCTION__, cmd_name, function, "server command", CMD_GAMEUIDLL ); } /* diff --git a/engine/common/crtlib.h b/engine/common/crtlib.h index a3c1e262..034574a8 100644 --- a/engine/common/crtlib.h +++ b/engine/common/crtlib.h @@ -36,6 +36,7 @@ enum #define CMD_SERVERDLL BIT( 0 ) // added by server.dll #define CMD_CLIENTDLL BIT( 1 ) // added by client.dll #define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll +#define CMD_LOCALONLY BIT( 3 ) // restricted from server commands typedef void (*xcommand_t)( void ); @@ -54,6 +55,7 @@ const char *Cmd_Argv( int arg ); void Cmd_Init( void ); void Cmd_Unlink( int group ); void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ); +void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ); void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function ); int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function ); int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function ); diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 464a4dff..2ee7addf 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -43,6 +43,7 @@ typedef struct convar_s #define FCVAR_ALLOCATED (1<<19) // this convar_t is fully dynamic allocated (include description) #define FCVAR_VIDRESTART (1<<20) // recreate the window is cvar with this flag was changed #define FCVAR_TEMPORARY (1<<21) // these cvars holds their values and can be unlink in any time +#define FCVAR_LOCALONLY (1<<22) // can be set only from local buffers #define CVAR_DEFINE( cv, cvname, cvstr, cvflags, cvdesc ) convar_t cv = { cvname, cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, cvdesc } #define CVAR_DEFINE_AUTO( cv, cvstr, cvflags, cvdesc ) convar_t cv = { #cv, cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, cvdesc } From ae5257c65400ee493c3a98bf1022785c07769ac2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jun 2018 20:44:16 +0300 Subject: [PATCH 012/205] Add masterlist from engine. Use non-blocking network name resolution --- contib/a1batross/xash3d.files | 1 + engine/client/cl_main.c | 53 +++++-- engine/client/cl_mobile.c | 2 +- engine/client/client.h | 2 + engine/client/in_evdev.c | 2 +- engine/client/in_touch.c | 16 +-- engine/client/vid_common.c | 2 +- engine/common/common.h | 7 + engine/common/con_utils.c | 2 + engine/common/host.c | 1 + engine/common/masterlist.c | 256 ++++++++++++++++++++++++++++++++++ engine/common/net_ws.h | 3 +- engine/common/netchan.h | 1 + 13 files changed, 322 insertions(+), 26 deletions(-) create mode 100644 engine/common/masterlist.c diff --git a/contib/a1batross/xash3d.files b/contib/a1batross/xash3d.files index 3a145cd8..17423cc6 100644 --- a/contib/a1batross/xash3d.files +++ b/contib/a1batross/xash3d.files @@ -146,6 +146,7 @@ engine/common/library.h engine/common/mathlib.c engine/common/mathlib.h engine/common/matrixlib.c +engine/common/masterlist.c engine/common/mod_bmodel.c engine/common/mod_local.h engine/common/mod_studio.c diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 93bd18b1..c3a60036 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1020,6 +1020,10 @@ Resend a connect message if the last one has timed out void CL_CheckForResend( void ) { netadr_t adr; + int res; + + if( cls.internetservers_wait ) + CL_InternetServers_f(); // if the local server is running and we aren't then connect if( cls.state == ca_disconnected && SV_Active( )) @@ -1044,13 +1048,21 @@ void CL_CheckForResend( void ) if(( host.realtime - cls.connect_time ) < cl_resend.value ) return; - if( !NET_StringToAdr( cls.servername, &adr )) + res = NET_StringToAdrNB( cls.servername, &adr ); + + if( !res ) { MsgDev( D_ERROR, "CL_CheckForResend: bad server address\n" ); CL_Disconnect(); return; } + if( res == 2 ) + { + cls.connect_time = MAX_HEARTBEAT; + return; + } + // only retry so many times before failure. if( cls.connect_retry >= CL_CONNECTION_RETRIES ) { @@ -1424,6 +1436,8 @@ void CL_LocalServers_f( void ) Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); } +#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" + /* ================= CL_InternetServers_f @@ -1431,23 +1445,28 @@ CL_InternetServers_f */ void CL_InternetServers_f( void ) { - netadr_t adr; - char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\"; + char fullquery[512] = MS_SCAN_REQUEST; + char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1; + const size_t remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST ); + + // Info_SetValueForKey( info, "nat", cl_nat->string, remaining ); + Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); + + // let master know about client version + Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); - Con_Printf( "Scanning for servers on the internet area...\n" ); NET_Config( true ); // allow remote - if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) ) - MsgDev( D_ERROR, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); + cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery ); + cls.internetservers_pending = true; - Q_strcpy( &fullquery[22], GI->gamefolder ); - - NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamefolder ) + 23, fullquery, adr ); - - // now we clearing the vgui request - if( clgame.master_request != NULL ) - memset( clgame.master_request, 0, sizeof( net_request_t )); - clgame.request_type = NET_REQUEST_GAMEUI; + if( !cls.internetservers_wait ) + { + // now we clearing the vgui request + if( clgame.master_request != NULL ) + memset( clgame.master_request, 0, sizeof( net_request_t )); + clgame.request_type = NET_REQUEST_GAMEUI; + } } /* @@ -1907,6 +1926,12 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); } } + + if( cls.internetservers_pending ) + { + Cbuf_AddText( "menu_resetping\n" ); // TODO: New Menu API + cls.internetservers_pending = false; + } } else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { diff --git a/engine/client/cl_mobile.c b/engine/client/cl_mobile.c index d3dd0286..c58598a5 100644 --- a/engine/client/cl_mobile.c +++ b/engine/client/cl_mobile.c @@ -54,7 +54,7 @@ static void Vibrate_f() { if( Cmd_Argc() != 2 ) { - Msg( "Usage: vibrate \n" ); + Msg( S_USAGE "touch_setcolor \n" ); } void IN_TouchSetTexture_f( void ) @@ -594,7 +594,7 @@ void IN_TouchSetTexture_f( void ) IN_TouchSetTexture( &touch.list_user, Cmd_Argv( 1 ), Cmd_Argv( 2 ) ); return; } - Msg( "Usage: touch_settexture \n" ); + Msg( S_USAGE "touch_settexture \n" ); } void IN_TouchSetFlags_f( void ) @@ -606,7 +606,7 @@ void IN_TouchSetFlags_f( void ) button->flags = Q_atoi( Cmd_Argv( 2 ) ); return; } - Msg( "Usage: touch_setflags \n" ); + Msg( S_USAGE "touch_setflags \n" ); } void IN_TouchSetCommand_f( void ) @@ -616,7 +616,7 @@ void IN_TouchSetCommand_f( void ) IN_TouchSetCommand( &touch.list_user, Cmd_Argv( 1 ), Cmd_Argv( 2 ) ); return; } - Msg( "Usage: touch_command \n" ); + Msg( S_USAGE "touch_command \n" ); } void IN_TouchReloadConfig_f( void ) { @@ -779,7 +779,7 @@ void IN_TouchAddButton_f( void ) IN_TouchAddButton( &touch.list_user, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), 0.4, 0.4, 0.6, 0.6, color ); return; } - Msg( "Usage: touch_addbutton [ [ r g b a ] ]\n" ); + Msg( S_USAGE "touch_addbutton [ [ r g b a ] ]\n" ); } void IN_TouchEnableEdit_f( void ) @@ -806,7 +806,7 @@ void IN_TouchDeleteProfile_f( void ) { if( Cmd_Argc() != 2 ) { - Msg( "Usage: touch_deleteprofile \n" ); + Msg( S_USAGE "touch_deleteprofile \n" ); return; } diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 8ad167dc..0cf27fcd 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -404,7 +404,7 @@ static void VID_Mode_f( void ) break; } default: - Msg( "Usage: vid_mode |\n" ); + Msg( S_USAGE "vid_mode |\n" ); return; } diff --git a/engine/common/common.h b/engine/common/common.h index 43502194..936847ec 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -1096,6 +1096,13 @@ typedef struct sentenceEntry_ sentenceEntry_s; sequenceEntry_s *Sequence_Get( const char *fileName, const char *entryName ); sentenceEntry_s *Sequence_PickSentence( const char *groupName, int pickMethod, int *picked ); +// +// masterlist.c +// +void NET_InitMasters( void ); +void NET_SaveMasters( void ); +qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ); + #ifdef __cplusplus } #endif diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index bcd6479b..644a8a83 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -951,6 +951,8 @@ void Host_WriteConfig( void ) FS_Close( f ); } else MsgDev( D_ERROR, "Couldn't write config.cfg.\n" ); + + NET_SaveMasters(); } #endif diff --git a/engine/common/host.c b/engine/common/host.c index 317ff463..40987edb 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -906,6 +906,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa Mod_Init(); NET_Init(); + NET_InitMasters(); Netchan_Init(); // allow to change game from the console diff --git a/engine/common/masterlist.c b/engine/common/masterlist.c new file mode 100644 index 00000000..5ea022b6 --- /dev/null +++ b/engine/common/masterlist.c @@ -0,0 +1,256 @@ +/* +masterlist.c - multi-master list +Copyright (C) 2018 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "common.h" +#include "netchan.h" + +typedef struct master_s +{ + struct master_s *next; + qboolean sent; + qboolean save; + string address; +} master_t; + +struct masterlist_s +{ + master_t *list; + qboolean modified; +} ml; + +/* +======================== +NET_SendToMasters + +Send request to all masterservers list +return true if would block +======================== +*/ +qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ) +{ + master_t *list; + qboolean wait = false; + + for( list = ml.list; list; list = list->next ) + { + netadr_t adr; + int res; + + if( list->sent ) + continue; + + res = NET_StringToAdrNB( list->address, &adr ); + + if( !res ) + { + MsgDev( D_INFO, "Can't resolve adr: %s\n", list->address ); + list->sent = true; + continue; + } + + if( res == 2 ) + { + list->sent = false; + wait = true; + continue; + } + + list->sent = true; + + NET_SendPacket( sock, len, data, adr ); + } + + if( !wait ) + { + list = ml.list; + + while( list ) + { + list->sent = false; + list = list->next; + } + } + + return wait; +} + +/* +======================== +NET_AddMaster + +Add master to the list +======================== +*/ +static void NET_AddMaster( char *addr, qboolean save ) +{ + master_t *master, *last; + + for( last = ml.list; last && last->next; last = last->next ) + { + if( !Q_strcmp( last->address, addr ) ) // already exists + return; + } + + master = Mem_Alloc( host.mempool, sizeof( master_t ) ); + Q_strncpy( master->address, addr, MAX_STRING ); + master->sent = false; + master->save = save; + master->next = NULL; + + // link in + if( last ) + last->next = master; + else + ml.list = master; +} + +static void NET_AddMaster_f( void ) +{ + if( Cmd_Argc() != 2 ) + { + Msg( S_USAGE "addmaster
\n"); + return; + } + + NET_AddMaster( Cmd_Argv( 1 ), true ); // save them into config + ml.modified = true; // save config +} + +/* +======================== +NET_ClearMasters + +Clear master list +======================== +*/ +static void NET_ClearMasters_f( void ) +{ + while( ml.list ) + { + master_t *prev = ml.list; + ml.list = ml.list->next; + Mem_Free( prev ); + } +} + +/* +======================== +NET_ListMasters_f + +Display current master linked list +======================== +*/ +static void NET_ListMasters_f( void ) +{ + master_t *list; + int i; + + Msg( "Master servers\n=============\n" ); + + + for( i = 1, list = ml.list; list; i++, list = list->next ) + { + Msg( "%d\t%s\n", i, list->address ); + } +} + +/* +======================== +NET_LoadMasters + +Load master server list from xashcomm.lst +======================== +*/ +static void NET_LoadMasters( void ) +{ + byte *afile, *pfile; + char token[MAX_TOKEN]; + + pfile = afile = FS_LoadFile( "xashcomm.lst", NULL, true ); + + if( !afile ) // file doesn't exist yet + { + MsgDev( D_INFO, "Cannot load xashcomm.lst\n" ); + return; + } + + // format: master \n + while( ( pfile = COM_ParseFile( pfile, token ) ) ) + { + if( !Q_strcmp( token, "master" ) ) // load addr + { + pfile = COM_ParseFile( pfile, token ); + + NET_AddMaster( token, true ); + } + } + + Mem_Free( afile ); + + ml.modified = false; +} + +/* +======================== +NET_SaveMasters + +Save master server list to xashcomm.lst, except for default +======================== +*/ +void NET_SaveMasters( void ) +{ + file_t *f; + master_t *m; + + if( !ml.modified ) + { + MsgDev( D_NOTE, "Master server list not changed\n" ); + return; + } + + f = FS_Open( "xashcomm.lst", "w", true ); + + if( !f ) + { + MsgDev( D_ERROR, "Couldn't write xashcomm.lst\n" ); + return; + } + + for( m = ml.list; m; m = m->next ) + { + if( m->save ) + FS_Printf( f, "master %s\n", m->address ); + } + + FS_Close( f ); +} + +/* +======================== +NET_InitMasters + +Initialize master server list +======================== +*/ +void NET_InitMasters( void ) +{ + Cmd_AddRestrictedCommand( "addmaster", NET_AddMaster_f, "add address to masterserver list" ); + Cmd_AddRestrictedCommand( "clearmasters", NET_ClearMasters_f, "clear masterserver list" ); + Cmd_AddCommand( "listmasters", NET_ListMasters_f, "list masterservers" ); + + // keep main master always there + NET_AddMaster( MASTERSERVER_ADR, false ); + NET_AddMaster( MASTERSERVER_ADR2, false ); + NET_LoadMasters( ); +} diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 067090b0..c946dd3f 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -54,6 +54,7 @@ char *NET_BaseAdrToString( const netadr_t a ); qboolean NET_IsReservedAdr( netadr_t a ); qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ); qboolean NET_StringToAdr( const char *string, netadr_t *adr ); +int NET_StringToAdrNB( const char *string, netadr_t *adr ); qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ); qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ); qboolean NET_GetPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ); @@ -62,4 +63,4 @@ qboolean NET_BufferToBufferDecompress( char *dest, uint *destLen, char *source, void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ); void NET_ClearLagData( qboolean bClient, qboolean bServer ); -#endif//NET_WS_H \ No newline at end of file +#endif//NET_WS_H diff --git a/engine/common/netchan.h b/engine/common/netchan.h index bc5384c3..31c57858 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -70,6 +70,7 @@ GNU General Public License for more details. #define NET_MAX_MESSAGE PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 ) #define MASTERSERVER_ADR "ms.xash.su:27010" +#define MASTERSERVER_ADR2 "ms2.xash.su:27010" #define PORT_MASTER 27010 #define PORT_CLIENT 27005 #define PORT_SERVER 27015 From 0561ac207627815a8ae9c384a6367fdcbd67b48f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jun 2018 21:28:25 +0300 Subject: [PATCH 013/205] Add basecmd from old engine. Add basecmd_stats command --- contib/a1batross/xash3d.files | 2 + engine/client/cl_main.c | 2 + engine/common/base_cmd.c | 229 ++++++++++++++++++++++++++++++++++ engine/common/base_cmd.h | 58 +++++++++ engine/common/cmd.c | 46 ++++++- engine/common/cvar.c | 10 +- engine/common/cvar.h | 2 +- engine/common/host.c | 3 + 8 files changed, 343 insertions(+), 9 deletions(-) create mode 100644 engine/common/base_cmd.c create mode 100644 engine/common/base_cmd.h diff --git a/contib/a1batross/xash3d.files b/contib/a1batross/xash3d.files index 17423cc6..97887702 100644 --- a/contib/a1batross/xash3d.files +++ b/contib/a1batross/xash3d.files @@ -108,6 +108,8 @@ engine/client/vgui/vgui_draw.h engine/client/vid_common.c engine/client/vid_common.h engine/client/vox.h +engine/common/base_cmd.c +engine/common/base_cmd.h engine/common/build.c engine/common/cfgscript.c engine/common/cmd.c diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index c3a60036..8aec7a72 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -83,6 +83,8 @@ client_t cl; client_static_t cls; clgame_static_t clgame; +void CL_InternetServers_f( void ); + //====================================================================== int CL_Active( void ) { diff --git a/engine/common/base_cmd.c b/engine/common/base_cmd.c new file mode 100644 index 00000000..3aae5093 --- /dev/null +++ b/engine/common/base_cmd.c @@ -0,0 +1,229 @@ +/* +base_cmd.c - command & cvar hashmap. Insipred by Doom III +Copyright (C) 2016 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "base_cmd.h" + +// TODO: use another hash function, as COM_HashKey depends on string length +#define HASH_SIZE 128 // 128 * 4 * 4 == 2048 bytes +static base_command_hashmap_t *hashed_cmds[HASH_SIZE]; + +/* +============ +BaseCmd_FindInBucket + +Find base command in bucket +============ +*/ +base_command_hashmap_t *BaseCmd_FindInBucket( base_command_hashmap_t *bucket, base_command_type_e type, const char *name ) +{ + base_command_hashmap_t *i = bucket; + for( ; i && ( i->type != type || Q_stricmp( name, i->name ) ); // filter out + i = i->next ); + + return i; +} + +/* +============ +BaseCmd_GetBucket + +Get bucket which contain basecmd by given name +============ +*/ +base_command_hashmap_t *BaseCmd_GetBucket( const char *name ) +{ + return hashed_cmds[ COM_HashKey( name, HASH_SIZE ) ]; +} + +/* +============ +BaseCmd_Find + +Find base command in hashmap +============ +*/ +base_command_t *BaseCmd_Find( base_command_type_e type, const char *name ) +{ + base_command_hashmap_t *base = BaseCmd_GetBucket( name ); + base_command_hashmap_t *found = BaseCmd_FindInBucket( base, type, name ); + + if( found ) + return found->basecmd; + return NULL; +} + +/* +============ +BaseCmd_Find + +Find every type of base command and write into arguments +============ +*/ +void BaseCmd_FindAll(const char *name, base_command_t **cmd, base_command_t **alias, base_command_t **cvar) +{ + base_command_hashmap_t *base = BaseCmd_GetBucket( name ); + base_command_hashmap_t *i = base; + + ASSERT( cmd && alias && cvar ); + + *cmd = *alias = *cvar = NULL; + + for( ; i; i = i->next ) + { + if( !Q_stricmp( i->name, name ) ) + { + switch( i->type ) + { + case HM_CMD: + *cmd = i->basecmd; + break; + case HM_CMDALIAS: + *alias = i->basecmd; + break; + case HM_CVAR: + *cvar = i->basecmd; + break; + default: break; + } + } + } + +} + +/* +============ +BaseCmd_Insert + +Add new typed base command to hashmap +============ +*/ +void BaseCmd_Insert( base_command_type_e type, base_command_t *basecmd, const char *name ) +{ + uint hash = COM_HashKey( name, HASH_SIZE ); + base_command_hashmap_t *elem; + + elem = Z_Malloc( sizeof( base_command_hashmap_t ) ); + elem->basecmd = basecmd; + elem->type = type; + elem->name = name; + elem->next = hashed_cmds[hash]; + hashed_cmds[hash] = elem; +} + +/* +============ +BaseCmd_Replace + +Used in case, when basecmd has been registered, but gamedll wants to register it's own +============ +*/ +qboolean BaseCmd_Replace( base_command_type_e type, base_command_t *basecmd, const char *name ) +{ + base_command_hashmap_t *i = BaseCmd_GetBucket( name ); + + for( ; i && ( i->type != type || Q_stricmp( name, i->name ) ) ; // filter out + i = i->next ); + + if( !i ) + { + MsgDev( D_ERROR, "BaseCmd_Replace: couldn't find %s\n", name); + return false; + } + + i->basecmd = basecmd; + i->name = name; // may be freed after + + return true; +} + +/* +============ +BaseCmd_Remove + +Remove base command from hashmap +============ +*/ +void BaseCmd_Remove( base_command_type_e type, const char *name ) +{ + uint hash = COM_HashKey( name, HASH_SIZE ); + base_command_hashmap_t *i, *prev; + + for( prev = NULL, i = hashed_cmds[hash]; i && + ( Q_strcmp( i->name, name ) || i->type != type); // filter out + prev = i, i = i->next ); + + if( !i ) + { + MsgDev( D_ERROR, "Couldn't find %s in buckets\n", name ); + return; + } + + if( prev ) + prev->next = i->next; + else + hashed_cmds[hash] = i->next; + + Z_Free( i ); +} + +/* +============ +BaseCmd_Init + +initialize base command hashmap system +============ +*/ +void BaseCmd_Init( void ) +{ + memset( hashed_cmds, 0, sizeof( hashed_cmds ) ); +} + +/* +============ +BaseCmd_Stats_f + +============ +*/ +void BaseCmd_Stats_f( void ) +{ + int i, minsize = 99999, maxsize = -1, empty = 0; + + for( i = 0; i < HASH_SIZE; i++ ) + { + base_command_hashmap_t *hm; + int len = 0; + + // count bucket length + for( hm = hashed_cmds[i]; hm; hm = hm->next, len++ ); + + if( len == 0 ) + { + empty++; + continue; + } + + if( len < minsize ) + minsize = len; + + if( len > maxsize ) + maxsize = len; + } + + Con_Printf( "Base command stats:\n"); + Con_Printf( "Bucket minimal length: %d\n", minsize ); + Con_Printf( "Bucket maximum length: %d\n", maxsize ); + Con_Printf( "Empty buckets: %d\n", empty ); +} diff --git a/engine/common/base_cmd.h b/engine/common/base_cmd.h new file mode 100644 index 00000000..9d07886c --- /dev/null +++ b/engine/common/base_cmd.h @@ -0,0 +1,58 @@ +/* +base_cmd.h - command & cvar hashmap. Insipred by Doom III +Copyright (C) 2016 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#ifndef BASE_CMD_H +#define BASE_CMD_H + +// TODO: Find cases when command hashmap works incorrect +// and maybe disable it +#define XASH_HASHED_VARS + +#ifdef XASH_HASHED_VARS + +typedef enum base_command_type +{ + HM_DONTCARE = 0, + HM_CVAR, + HM_CMD, + HM_CMDALIAS +} base_command_type_e; + +typedef void base_command_t; + +typedef struct base_command_hashmap_s +{ + base_command_t *basecmd; // base command: cvar, alias or command + const char *name; // key for searching + base_command_type_e type; // type for faster searching + struct base_command_hashmap_s *next; +} base_command_hashmap_t; + + +void BaseCmd_Init( void ); +base_command_hashmap_t *BaseCmd_GetBucket( const char *name ); +base_command_hashmap_t *BaseCmd_FindInBucket( base_command_hashmap_t *bucket, base_command_type_e type, const char *name ); +base_command_t *BaseCmd_Find( base_command_type_e type, const char *name ); +void BaseCmd_FindAll( const char *name, + base_command_t **cmd, base_command_t **alias, base_command_t **cvar ); +void BaseCmd_Insert ( base_command_type_e type, base_command_t *basecmd, const char *name ); +qboolean BaseCmd_Replace( base_command_type_e type, base_command_t *basecmd, const char *name ); // only if same name +void BaseCmd_Remove ( base_command_type_e type, const char *name ); +void BaseCmd_Stats_f( void ); // to be registered later + +#endif // XASH_HASHED_VARS + +#endif // BASE_CMD_H diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 60cd99b1..f838fdf1 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -16,6 +16,7 @@ GNU General Public License for more details. #include "common.h" #include "client.h" #include "server.h" +#include "base_cmd.h" #define MAX_CMD_BUFFER 32768 #define MAX_CMD_LINE 2048 @@ -380,6 +381,10 @@ void Cmd_Alias_f( void ) if( prev ) prev->next = a; else cmd_alias = a; a->next = cur; + +#if defined( XASH_HASHED_VARS ) + BaseCmd_Insert( HM_CMDALIAS, a, a->name ); +#endif } // copy the rest of the command line @@ -425,6 +430,9 @@ static void Cmd_UnAlias_f ( void ) { if( !Q_strcmp( s, a->name )) { +#if defined( XASH_HASHED_VARS ) + BaseCmd_Remove( HM_CMDALIAS, a->name ); +#endif if( a == cmd_alias ) cmd_alias = a->next; if( p ) p->next = a->next; @@ -722,6 +730,10 @@ void Cmd_RemoveCommand( const char *cmd_name ) if( !Q_strcmp( cmd_name, cmd->name )) { +#if defined(XASH_HASHED_VARS) + BaseCmd_Remove( HM_CMD, cmd->name ); +#endif + *back = cmd->next; if( cmd->name ) @@ -768,6 +780,9 @@ Cmd_Exists */ qboolean Cmd_Exists( const char *cmd_name ) { +#if defined(XASH_HASHED_VARS) + return BaseCmd_Find( HM_CMD, cmd_name ) != NULL; +#else cmd_t *cmd; for( cmd = cmd_functions; cmd; cmd = cmd->next ) @@ -776,6 +791,7 @@ qboolean Cmd_Exists( const char *cmd_name ) return true; } return false; +#endif } /* @@ -860,8 +876,9 @@ A complete command line has been parsed, so try to execute it */ void Cmd_ExecuteString( char *text ) { - cmd_t *cmd; - cmdalias_t *a; + cmd_t *cmd = NULL; + cmdalias_t *a = NULL; + convar_t *cvar = NULL; char command[MAX_CMD_LINE]; char *pcmd = command; int len = 0; @@ -916,9 +933,22 @@ void Cmd_ExecuteString( char *text ) if( !Cmd_Argc( )) return; // no tokens +#if defined(XASH_HASHED_VARS) + BaseCmd_FindAll( cmd_argv[0], + (base_command_t**)&cmd, + (base_command_t**)&a, + (base_command_t**)&cvar ); +#endif + if( !host.apply_game_config ) { // check aliases + if( a ) // already found in basecmd + { + Cbuf_InsertText( a->value ); + return; + } + for( a = cmd_alias; a; a = a->next ) { if( !Q_stricmp( cmd_argv[0], a->name )) @@ -933,6 +963,12 @@ void Cmd_ExecuteString( char *text ) if( !host.apply_game_config || !Q_strcmp( cmd_argv[0], "exec" )) { // check functions + if( cmd && cmd->function ) // already found in basecmd + { + cmd->function(); + return; + } + for( cmd = cmd_functions; cmd; cmd = cmd->next ) { if( !Q_stricmp( cmd_argv[0], cmd->name ) && cmd->function ) @@ -944,7 +980,7 @@ void Cmd_ExecuteString( char *text ) } // check cvars - if( Cvar_Command( )) return; + if( Cvar_Command( cvar )) return; if( host.apply_game_config ) return; // don't send nothing to server: we is a server! @@ -1125,4 +1161,8 @@ void Cmd_Init( void ) Cmd_AddCommand( "unalias", Cmd_UnAlias_f, "remove a script function" ); Cmd_AddCommand( "if", Cmd_If_f, "compare and set condition bits" ); Cmd_AddCommand( "else", Cmd_Else_f, "invert condition bit" ); + +#if defined(XASH_HASHED_VARS) + Cmd_AddCommand( "basecmd_stats", BaseCmd_Stats_f, "print info about basecmd usage" ); +#endif } diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 55932406..15521c10 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -13,8 +13,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#include // fabs... #include "common.h" -#include "math.h" // fabs... +#include "base_cmd.h" convar_t *cvar_vars = NULL; // head of list convar_t *cmd_scripting; @@ -690,10 +691,8 @@ Cvar_Command Handles variable inspection and changing from the console ============ */ -qboolean Cvar_Command( void ) +qboolean Cvar_Command( convar_t *v ) { - convar_t *v; - // special case for setup opengl configuration if( host.apply_opengl_config ) { @@ -702,7 +701,8 @@ qboolean Cvar_Command( void ) } // check variables - v = Cvar_FindVar( Cmd_Argv( 0 )); + if( !v ) // already found in basecmd + v = Cvar_FindVar( Cmd_Argv( 0 )); if( !v ) return false; // perform a variable print or set diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 2ee7addf..5d5e5407 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -66,7 +66,7 @@ const char *Cvar_VariableString( const char *var_name ); void Cvar_WriteVariables( file_t *f, int group ); void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); -qboolean Cvar_Command( void ); +qboolean Cvar_Command( convar_t *v ); void Cvar_Init( void ); void Cvar_Unlink( int group ); diff --git a/engine/common/host.c b/engine/common/host.c index 40987edb..0da0e0ad 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -822,6 +822,9 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha // init host state machine COM_InitHostState(); + // init hashed commands + BaseCmd_Init(); + // startup cmds and cvars subsystem Cmd_Init(); Cvar_Init(); From 5d487086159d1738289cdcd296311772f9557278 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jun 2018 22:11:22 +0300 Subject: [PATCH 014/205] Forgot to add cvars into basecmd. Add basecmd_test to check is basecmd valid --- engine/common/base_cmd.c | 58 ++++++++++++++++++++++++++++++++++++++++ engine/common/base_cmd.h | 1 + engine/common/cmd.c | 1 + engine/common/cvar.c | 19 +++++++++++++ 4 files changed, 79 insertions(+) diff --git a/engine/common/base_cmd.c b/engine/common/base_cmd.c index 3aae5093..15c03ec4 100644 --- a/engine/common/base_cmd.c +++ b/engine/common/base_cmd.c @@ -15,6 +15,7 @@ GNU General Public License for more details. #include "common.h" #include "base_cmd.h" +#include "cdll_int.h" // TODO: use another hash function, as COM_HashKey depends on string length #define HASH_SIZE 128 // 128 * 4 * 4 == 2048 bytes @@ -227,3 +228,60 @@ void BaseCmd_Stats_f( void ) Con_Printf( "Bucket maximum length: %d\n", maxsize ); Con_Printf( "Empty buckets: %d\n", empty ); } + +static void BaseCmd_CheckCvars( const char *key, const char *value, void *buffer, void *ptr ) +{ + base_command_t *v = BaseCmd_Find( HM_CVAR, key ); + qboolean *invalid = ptr; + + if( !v ) + { + Con_Printf( "Cvar %s is missing in basecmd\n", key ); + *invalid = true; + } +} + +/* +============ +BaseCmd_Stats_f + +testing order matches cbuf execute +============ +*/ +void BaseCmd_Test_f( void ) +{ + void *cmd; + cmdalias_t *a; + qboolean invalid = false; + + // Cmd_LookupCmds don't allows to check alias, so just iterate + for( a = Cmd_AliasGetList(); a; a = a->next ) + { + base_command_t *v = BaseCmd_Find( HM_CMDALIAS, a->name ); + + if( !v ) + { + Con_Printf( "Alias %s is missing in basecmd\n", a->name ); + invalid = true; + } + } + + for( cmd = Cmd_GetFirstFunctionHandle(); cmd; + cmd = Cmd_GetNextFunctionHandle( cmd ) ) + { + base_command_t *v = BaseCmd_Find( HM_CMD, Cmd_GetName( cmd ) ); + + if( !v ) + { + Con_Printf( "Command %s is missing in basecmd\n", Cmd_GetName( cmd ) ); + invalid = true; + } + } + + Cvar_LookupVars( 0, NULL, &invalid, BaseCmd_CheckCvars ); + + if( !invalid ) + { + Con_Printf( "BaseCmd is valid\n" ); + } +} diff --git a/engine/common/base_cmd.h b/engine/common/base_cmd.h index 9d07886c..f507c9e1 100644 --- a/engine/common/base_cmd.h +++ b/engine/common/base_cmd.h @@ -52,6 +52,7 @@ void BaseCmd_Insert ( base_command_type_e type, base_command_t *basecmd, const c qboolean BaseCmd_Replace( base_command_type_e type, base_command_t *basecmd, const char *name ); // only if same name void BaseCmd_Remove ( base_command_type_e type, const char *name ); void BaseCmd_Stats_f( void ); // to be registered later +void BaseCmd_Test_f( void ); // to be registered later #endif // XASH_HASHED_VARS diff --git a/engine/common/cmd.c b/engine/common/cmd.c index f838fdf1..d7ccda01 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1164,5 +1164,6 @@ void Cmd_Init( void ) #if defined(XASH_HASHED_VARS) Cmd_AddCommand( "basecmd_stats", BaseCmd_Stats_f, "print info about basecmd usage" ); + Cmd_AddCommand( "basecmd_test", BaseCmd_Test_f, "test basecmd" ); #endif } diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 15521c10..2fb7ea01 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -40,6 +40,10 @@ find the specified variable by name */ convar_t *Cvar_FindVarExt( const char *var_name, int ignore_group ) { + // TODO: ignore group for cvar +#if defined(XASH_HASHED_VARS) + return (convar_t *)BaseCmd_Find( HM_CVAR, var_name ); +#else convar_t *var; if( !var_name ) @@ -55,6 +59,7 @@ convar_t *Cvar_FindVarExt( const char *var_name, int ignore_group ) } return NULL; +#endif } /* @@ -239,6 +244,10 @@ int Cvar_UnlinkVar( const char *var_name, int group ) continue; } +#if defined(XASH_HASHED_VARS) + BaseCmd_Remove( HM_CVAR, var->name ); +#endif + // unlink variable from list freestring( var->string ); *prev = var->next; @@ -401,6 +410,11 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char * // tell engine about changes Cvar_Changed( var ); +#if defined(XASH_HASHED_VARS) + // add to map + BaseCmd_Insert( HM_CVAR, var, var->name ); +#endif + return var; } @@ -464,6 +478,11 @@ void Cvar_RegisterVariable( convar_t *var ) // tell engine about changes Cvar_Changed( var ); + +#if defined(XASH_HASHED_VARS) + // add to map + BaseCmd_Insert( HM_CVAR, var, var->name ); +#endif } /* From 814b7eda07b28bac8ef13f1d14581211cb331aec Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 9 Jun 2018 01:28:35 +0300 Subject: [PATCH 015/205] Apply 4140 update --- common/bspfile.h | 12 +- common/com_model.h | 1 + common/event_api.h | 4 +- common/render_api.h | 4 +- engine/client/cl_demo.c | 6 +- engine/client/cl_events.c | 2 +- engine/client/cl_frame.c | 6 + engine/client/cl_game.c | 12 +- engine/client/cl_gameui.c | 4 +- engine/client/cl_main.c | 19 +- engine/client/cl_parse.c | 16 +- engine/client/cl_pmove.c | 25 + engine/client/cl_remap.c | 4 +- engine/client/cl_scrn.c | 5 +- engine/client/cl_tent.c | 4 +- engine/client/client.h | 2 + engine/client/gl_alias.c | 82 +-- engine/client/gl_backend.c | 14 +- engine/client/gl_beams.c | 11 +- engine/client/gl_decals.c | 2 +- engine/client/gl_image.c | 43 +- engine/client/gl_local.h | 12 +- engine/client/gl_rlight.c | 54 +- engine/client/gl_rmain.c | 29 +- engine/client/gl_rpart.c | 2 +- engine/client/gl_rsurf.c | 30 +- engine/client/gl_sprite.c | 20 +- engine/client/gl_studio.c | 95 ++-- engine/client/gl_vidnt.c | 28 +- engine/client/gl_warp.c | 2 +- engine/client/s_dsp.c | 2 +- engine/client/s_load.c | 4 +- engine/client/s_main.c | 2 +- engine/client/s_vox.c | 6 + engine/client/vgui/vgui_surf.cpp | 2 +- engine/client/vox.h | 2 +- engine/common/avikit.c | 8 +- engine/common/build.c | 6 +- engine/common/cmd.c | 1 + engine/common/common.h | 3 +- engine/common/con_utils.c | 6 +- engine/common/console.c | 4 +- engine/common/crtlib.c | 2 +- engine/common/crtlib.h | 9 +- engine/common/custom.c | 2 +- engine/common/filesystem.c | 128 +++-- engine/common/host.c | 2 +- engine/common/imagelib/img_bmp.c | 6 +- engine/common/imagelib/img_dds.c | 2 +- engine/common/imagelib/img_main.c | 8 +- engine/common/imagelib/img_quant.c | 2 +- engine/common/imagelib/img_tga.c | 4 +- engine/common/imagelib/img_utils.c | 17 +- engine/common/imagelib/img_wad.c | 2 +- engine/common/input.h | 6 +- engine/common/library.c | 6 +- engine/common/library.h | 4 + engine/common/mathlib.h | 2 + engine/common/mod_bmodel.c | 69 +-- engine/common/mod_dbghulls.c | 777 +++++++++++++++++++++++++++++ engine/common/mod_local.h | 32 ++ engine/common/mod_studio.c | 4 +- engine/common/model.c | 11 +- engine/common/net_chan.c | 14 +- engine/common/net_encode.c | 2 +- engine/common/soundlib/snd_main.c | 2 +- engine/common/soundlib/snd_mp3.c | 4 +- engine/common/soundlib/snd_utils.c | 2 +- engine/common/soundlib/snd_wav.c | 4 +- engine/common/titles.c | 2 +- engine/common/zone.c | 159 +----- engine/server/sv_client.c | 4 +- engine/server/sv_game.c | 24 +- engine/server/sv_phys.c | 4 +- engine/server/sv_pmove.c | 2 +- engine/server/sv_save.c | 12 +- 76 files changed, 1397 insertions(+), 529 deletions(-) create mode 100644 engine/common/mod_dbghulls.c diff --git a/common/bspfile.h b/common/bspfile.h index ec1575b5..201e03dd 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -117,14 +117,14 @@ BRUSH MODELS #define LUMP_FACEINFO 1 // landscape and lightmap resolution info #define LUMP_CUBEMAPS 2 // cubemap description #define LUMP_VERTNORMALS 3 // phong shaded vertex normals -#define LUMP_VERTEX_LIGHT 4 // contain compressed light cubes per empty leafs +#define LUMP_LEAF_LIGHTING 4 // store vertex lighting for statics #define LUMP_WORLDLIGHTS 5 // list of all the virtual and real lights (used to relight models in-game) -#define LUMP_COLLISION 6 // physics engine collision hull dump -#define LUMP_AINODEGRAPH 7 // node graph that stored into the bsp +#define LUMP_COLLISION 6 // physics engine collision hull dump (userdata) +#define LUMP_AINODEGRAPH 7 // node graph that stored into the bsp (userdata) #define LUMP_SHADOWMAP 8 // contains shadow map for direct light -#define LUMP_UNUSED1 9 // one lump reserved for me -#define LUMP_UNUSED2 10 // one lump reserved for me -#define LUMP_UNUSED3 11 // one lump reserved for me +#define LUMP_VERTEX_LIGHT 9 // store vertex lighting for statics +#define LUMP_UNUSED0 10 // one lump reserved for me +#define LUMP_UNUSED1 11 // one lump reserved for me #define EXTRA_LUMPS 12 // count of the extra lumps // texture flags diff --git a/common/com_model.h b/common/com_model.h index d71e98fa..0a85c084 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -166,6 +166,7 @@ struct decal_s // Xash3D specific vec3_t position; // location of the decal center in world space. glpoly_t *polys; // precomputed decal vertices + int reserved[4]; // just for future expansions or mod-makers }; typedef struct mleaf_s diff --git a/common/event_api.h b/common/event_api.h index 20e50705..8b7ae640 100644 --- a/common/event_api.h +++ b/common/event_api.h @@ -51,6 +51,8 @@ typedef struct event_api_s struct pmtrace_s *( *EV_VisTraceLine )( float *start, float *end, int flags ); struct physent_s *( *EV_GetVisent )( int idx ); int ( *EV_TestLine)( const vec3_t start, const vec3_t end, int flags ); + void ( *EV_PushTraceBounds)( int hullnum, const float *mins, const float *maxs ); + void ( *EV_PopTraceBounds)( void ); } event_api_t; -#endif//EVENT_API_H \ No newline at end of file +#endif//EVENT_API_H diff --git a/common/render_api.h b/common/render_api.h index 59f5e6f5..1deb14a7 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -96,7 +96,7 @@ typedef enum TF_BORDER = (1<<19), // zero clamp for projected textures TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D TF_ATLAS_PAGE = (1<<21), // bit who indicate lightmap page or deluxemap page -// reserved + TF_ALPHACONTRAST = (1<<22), // special texture mode for A2C // reserved // reserved TF_IMG_UPLOADED = (1<<25), // this is set for first time when called glTexImage, otherwise it will be call glTexSubImage @@ -211,7 +211,7 @@ typedef struct render_api_s void (*GL_DrawParticles)( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime ); void (*EnvShot)( const float *vieworg, const char *name, qboolean skyshot, int shotsize ); // store skybox into gfx\env folder int (*SPR_LoadExt)( const char *szPicName, unsigned int texFlags ); // extended version of SPR_Load - colorVec (*LightVec)( const float *start, const float *end, float *lightspot ); + colorVec (*LightVec)( const float *start, const float *end, float *lightspot, float *lightvec ); struct mstudiotex_s *( *StudioGetTexture )( struct cl_entity_s *e ); const struct ref_overview_s *( *GetOverviewParms )( void ); const char *( *GetFileByIndex )( int fileindex ); diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 04b7ec18..47cf1b4b 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -380,7 +380,7 @@ void CL_WriteDemoHeader( const char *name ) FS_Write( cls.demofile, &demo.header, sizeof( demo.header )); demo.directory.numentries = 2; - demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); + demo.directory.entries = Mem_Calloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); // DIRECTORY ENTRY # 0 demo.entry = &demo.directory.entries[0]; // only one here. @@ -812,7 +812,7 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) return false; // header is ended, skip frame case dem_userdata: FS_Read( cls.demofile, &size, sizeof( int )); - userbuf = Mem_Alloc( cls.mempool, size ); + userbuf = Mem_Malloc( cls.mempool, size ); FS_Read( cls.demofile, userbuf, size ); if( clgame.hInstance ) @@ -1316,7 +1316,7 @@ void CL_PlayDemo_f( void ) } // allocate demo entries - demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); + demo.directory.entries = Mem_Malloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); for( i = 0; i < demo.directory.numentries; i++ ) { diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index cbcb3519..c7404dc9 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -164,7 +164,7 @@ void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ) // clear existing or allocate new one if( !clgame.events[lastnum] ) - clgame.events[lastnum] = Mem_Alloc( cls.mempool, sizeof( cl_user_event_t )); + clgame.events[lastnum] = Mem_Calloc( cls.mempool, sizeof( cl_user_event_t )); else memset( clgame.events[lastnum], 0, sizeof( cl_user_event_t )); ev = clgame.events[lastnum]; diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index fa88f05a..576405d4 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -1226,6 +1226,12 @@ void CL_EmitEntities( void ) if( !cl.frames[cl.parsecountmod].valid ) return; + // animate lightestyles + CL_RunLightStyles (); + + // decay dynamic lights + CL_DecayLights (); + // compute last interpolation amount CL_UpdateFrameLerp (); diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 3105a11f..cda41ad0 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1109,16 +1109,16 @@ void CL_InitEdicts( void ) CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; cls.num_client_entities = CL_UPDATE_BACKUP * NUM_PACKET_ENTITIES; - cls.packet_entities = Z_Realloc( cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities ); - clgame.entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities ); - clgame.static_entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES ); + cls.packet_entities = Mem_Realloc( clgame.mempool, cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities ); + clgame.entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities ); + clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES ); clgame.numStatics = 0; if(( clgame.maxRemapInfos - 1 ) != clgame.maxEntities ) { CL_ClearAllRemaps (); // purge old remap info clgame.maxRemapInfos = clgame.maxEntities + 1; - clgame.remap_info = (remap_info_t **)Mem_Alloc( clgame.mempool, sizeof( remap_info_t* ) * clgame.maxRemapInfos ); + clgame.remap_info = (remap_info_t **)Mem_Calloc( clgame.mempool, sizeof( remap_info_t* ) * clgame.maxRemapInfos ); } if( clgame.drawFuncs.R_ProcessEntData != NULL ) @@ -1529,7 +1529,7 @@ static client_sprite_t *pfnSPR_GetList( char *psz, int *piCount ) Q_strncpy( pEntry->szListName, psz, sizeof( pEntry->szListName )); // name, res, pic, x, y, w, h - pEntry->pList = Mem_Alloc( cls.mempool, sizeof( client_sprite_t ) * numSprites ); + pEntry->pList = Mem_Calloc( cls.mempool, sizeof( client_sprite_t ) * numSprites ); for( index = 0; index < numSprites; index++ ) { @@ -3715,6 +3715,8 @@ static event_api_t gEventApi = CL_VisTraceLine, pfnGetVisent, CL_TestLine, + CL_PushTraceBounds, + CL_PopTraceBounds, }; static demo_api_t gDemoApi = diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 95b6ee76..cf9690e8 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -768,7 +768,7 @@ pfnMemAlloc */ static void *pfnMemAlloc( size_t cb, const char *filename, const int fileline ) { - return _Mem_Alloc( gameui.mempool, cb, filename, fileline ); + return _Mem_Alloc( gameui.mempool, cb, true, filename, fileline ); } /* @@ -1050,7 +1050,7 @@ qboolean UI_LoadProgs( void ) // setup gameinfo for( i = 0; i < SI.numgames; i++ ) { - gameui.modsInfo[i] = Mem_Alloc( gameui.mempool, sizeof( GAMEINFO )); + gameui.modsInfo[i] = Mem_Calloc( gameui.mempool, sizeof( GAMEINFO )); UI_ConvertGameInfo( gameui.modsInfo[i], SI.games[i] ); } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ad2628cf..0de9f5b6 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2059,13 +2059,11 @@ void CL_ReadPackets( void ) if( cl.maxclients > 1 && cls.state == ca_active && !host_developer.value ) Cvar_SetCheatState(); #endif - // singleplayer never has connection timeout - if( NET_IsLocalAddress( cls.netchan.remote_address )) - return; - // hot precache and downloading resources if( cls.signon == SIGNONS && cl.lastresourcecheck < host.realtime ) { + double checktime = Host_IsLocalGame() ? 0.1 : 1.0; + if( !cls.dl.custom && cl.resourcesneeded.pNext != &cl.resourcesneeded ) { // check resource for downloading and precache @@ -2073,9 +2071,14 @@ void CL_ReadPackets( void ) CL_BatchResourceRequest( false ); cls.dl.custom = true; } - cl.lastresourcecheck = host.realtime + 5.0f; // don't checking too often + + cl.lastresourcecheck = host.realtime + checktime; } + // singleplayer never has connection timeout + if( NET_IsLocalAddress( cls.netchan.remote_address )) + return; + // if in the debugger last frame, don't timeout if( host.frametime > 5.0f ) cls.netchan.last_received = Sys_DoubleTime(); @@ -2774,12 +2777,6 @@ void Host_ClientFrame( void ) // update audio SND_UpdateSound (); - // animate lightestyles - CL_RunLightStyles (); - - // decay dynamic lights - CL_DecayLights (); - // play avi-files SCR_RunCinematic (); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index fcce4559..b43a0183 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -285,7 +285,7 @@ void CL_ParseSoundPacket( sizebuf_t *msg ) char sentenceName[32]; if( FBitSet( flags, SND_SEQUENCE )) - Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound ); + Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS ); else Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); @@ -352,7 +352,7 @@ void CL_ParseRestoreSoundPacket( sizebuf_t *msg ) char sentenceName[32]; if( flags & SND_SEQUENCE ) - Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound ); + Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS ); else Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); @@ -778,7 +778,7 @@ int CL_EstimateNeededResources( void ) break; case t_model: nSize = FS_FileSize( p->szFileName, false ); - if( p->szFileName[0] != '*' && p->ucFlags && nSize == -1 ) + if( p->szFileName[0] != '*' && nSize == -1 ) { SetBits( p->ucFlags, RES_WASMISSING ); nTotalSize += p->nDownloadSize; @@ -897,7 +897,7 @@ void CL_ParseCustomization( sizebuf_t *msg ) if( i >= MAX_CLIENTS ) Host_Error( "Bogus player index during customization parsing.\n" ); - pRes = Mem_Alloc( cls.mempool, sizeof( resource_t )); + pRes = Mem_Calloc( cls.mempool, sizeof( resource_t )); pRes->type = MSG_ReadByte( msg ); Q_strncpy( pRes->szFileName, MSG_ReadString( msg ), sizeof( pRes->szFileName )); @@ -1576,7 +1576,7 @@ void CL_ParseResource( sizebuf_t *msg ) { resource_t *pResource; - pResource = Mem_Alloc( cls.mempool, sizeof( resource_t )); + pResource = Mem_Calloc( cls.mempool, sizeof( resource_t )); pResource->type = MSG_ReadUBitLong( msg, 4 ); Q_strncpy( pResource->szFileName, MSG_ReadString( msg ), sizeof( pResource->szFileName )); @@ -1834,7 +1834,7 @@ void CL_ParseResourceList( sizebuf_t *msg ) for( i = 0; i < total; i++ ) { - pResource = Mem_Alloc( cls.mempool, sizeof( resource_t )); + pResource = Mem_Calloc( cls.mempool, sizeof( resource_t )); pResource->type = MSG_ReadUBitLong( msg, 4 ); Q_strncpy( pResource->szFileName, MSG_ReadString( msg ), sizeof( pResource->szFileName )); @@ -1982,8 +1982,8 @@ void CL_ParseScreenFade( sizebuf_t *msg ) screenfade_t *sf = &clgame.fade; float flScale; - duration = (float)MSG_ReadShort( msg ); - holdTime = (float)MSG_ReadShort( msg ); + duration = (float)MSG_ReadWord( msg ); + holdTime = (float)MSG_ReadWord( msg ); sf->fadeFlags = MSG_ReadShort( msg ); flScale = ( sf->fadeFlags & FFADE_LONGFADE ) ? (1.0f / 256.0f) : (1.0f / 4096.0f); diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 5e1a1c94..5dcbdb17 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -81,6 +81,31 @@ void CL_PopPMStates( void ) } } +/* +============= +CL_PushTraceBounds + +============= +*/ +void CL_PushTraceBounds( int hullnum, const float *mins, const float *maxs ) +{ + hullnum = bound( 0, hullnum, 3 ); + VectorCopy( mins, clgame.pmove->player_mins[hullnum] ); + VectorCopy( maxs, clgame.pmove->player_maxs[hullnum] ); +} + +/* +============= +CL_PopTraceBounds + +============= +*/ +void CL_PopTraceBounds( void ) +{ + memcpy( clgame.pmove->player_mins, host.player_mins, sizeof( host.player_mins )); + memcpy( clgame.pmove->player_maxs, host.player_maxs, sizeof( host.player_maxs )); +} + /* =============== CL_IsPredicted diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c index 5c79d6b7..30ab070e 100644 --- a/engine/client/cl_remap.c +++ b/engine/client/cl_remap.c @@ -288,7 +288,7 @@ void CL_AllocRemapInfo( int topcolor, int bottomcolor ) // e.g. playermodel 'barney' with playermodel 'gordon' if( clgame.remap_info[i] ) CL_FreeRemapInfo( clgame.remap_info[i] ); // free old info size = sizeof( remap_info_t ) + ( sizeof( mstudiotexture_t ) * phdr->numtextures ); - info = clgame.remap_info[i] = Mem_Alloc( clgame.mempool, size ); + info = clgame.remap_info[i] = Mem_Calloc( clgame.mempool, size ); info->ptexture = (mstudiotexture_t *)(info + 1); // textures are immediately comes after remap_info } else @@ -325,7 +325,7 @@ void CL_AllocRemapInfo( int topcolor, int bottomcolor ) // this code catches studiomodel change with another studiomodel with remap textures // e.g. playermodel 'barney' with playermodel 'gordon' if( clgame.remap_info[i] ) CL_FreeRemapInfo( clgame.remap_info[i] ); // free old info - info = clgame.remap_info[i] = Mem_Alloc( clgame.mempool, sizeof( remap_info_t )); + info = clgame.remap_info[i] = Mem_Calloc( clgame.mempool, sizeof( remap_info_t )); } else { diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 4c37072b..41f9c88e 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -176,7 +176,7 @@ SCR_RSpeeds */ void SCR_RSpeeds( void ) { - char msg[MAX_SYSPATH]; + char msg[2048]; if( !host.allow_console ) return; @@ -202,6 +202,9 @@ void SCR_RSpeeds( void ) Con_DrawString( x, y, p, color ); y += height; + // handle '\n\n' + if( *p == '\n' ) + y += height; if( end ) p = end + 1; else break; } while( 1 ); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 16aba9b8..1398660c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -127,7 +127,7 @@ void CL_AddClientResource( const char *filename, int type ) if( p != &cl.resourcesneeded ) return; // already in list? - pResource = Mem_Alloc( cls.mempool, sizeof( resource_t )); + pResource = Mem_Calloc( cls.mempool, sizeof( resource_t )); Q_strncpy( pResource->szFileName, filename, sizeof( pResource->szFileName )); pResource->type = type; @@ -325,7 +325,7 @@ CL_InitTempents */ void CL_InitTempEnts( void ) { - cl_tempents = Mem_Alloc( cls.mempool, sizeof( TEMPENTITY ) * GI->max_tents ); + cl_tempents = Mem_Calloc( cls.mempool, sizeof( TEMPENTITY ) * GI->max_tents ); CL_ClearTempEnts(); // load tempent sprites (glowshell, muzzleflashes etc) diff --git a/engine/client/client.h b/engine/client/client.h index c863d4fe..446401da 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -898,6 +898,8 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q int CL_TestLine( const vec3_t start, const vec3_t end, int flags ); pmtrace_t *CL_VisTraceLine( vec3_t start, vec3_t end, int flags ); pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags ); +void CL_PushTraceBounds( int hullnum, const float *mins, const float *maxs ); +void CL_PopTraceBounds( void ); void CL_MoveSpectatorCamera( void ); void CL_SetLastUpdate( void ); void CL_RedoPrediction( void ); diff --git a/engine/client/gl_alias.c b/engine/client/gl_alias.c index c2812059..124f23ec 100644 --- a/engine/client/gl_alias.c +++ b/engine/client/gl_alias.c @@ -318,10 +318,10 @@ void GL_MakeAliasModelDisplayLists( model_t *m ) // save the data out m_pAliasHeader->poseverts = g_numorder; - m_pAliasHeader->commands = Mem_Alloc( m->mempool, g_numcommands * 4 ); + m_pAliasHeader->commands = Mem_Malloc( m->mempool, g_numcommands * 4 ); memcpy( m_pAliasHeader->commands, g_commands, g_numcommands * 4 ); - m_pAliasHeader->posedata = Mem_Alloc( m->mempool, m_pAliasHeader->numposes * m_pAliasHeader->poseverts * sizeof( trivertex_t )); + m_pAliasHeader->posedata = Mem_Malloc( m->mempool, m_pAliasHeader->numposes * m_pAliasHeader->poseverts * sizeof( trivertex_t )); verts = m_pAliasHeader->posedata; for( i = 0; i < m_pAliasHeader->numposes; i++ ) @@ -453,7 +453,7 @@ rgbdata_t *Mod_CreateSkinData( model_t *mod, byte *data, int width, int height ) i = mod->numtextures; mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* )); size = width * height + 768; - tx = Mem_Alloc( mod->mempool, sizeof( *tx ) + size ); + tx = Mem_Calloc( mod->mempool, sizeof( *tx ) + size ); mod->textures[i] = tx; Q_strncpy( tx->name, "DM_Skin", sizeof( tx->name )); @@ -639,7 +639,7 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) // skin and group info size = sizeof( aliashdr_t ) + (pinmodel->numframes - 1) * sizeof( maliasframedesc_t ); - m_pAliasHeader = Mem_Alloc( mod->mempool, size ); + m_pAliasHeader = Mem_Calloc( mod->mempool, size ); mod->flags = pinmodel->flags; // share effects flags // endian-adjust and copy the data, starting with the alias model header @@ -863,40 +863,44 @@ void R_AliasDynamicLight( cl_entity_t *ent, alight_t *plight ) VectorScale( lightDir, 2048.0f, vecEnd ); VectorAdd( vecEnd, vecSrc, vecEnd ); - light = R_LightVec( vecSrc, vecEnd, g_alias.lightspot ); + light = R_LightVec( vecSrc, vecEnd, g_alias.lightspot, g_alias.lightvec ); - VectorScale( lightDir, 2048.0f, vecEnd ); - VectorAdd( vecEnd, vecSrc, vecEnd ); + if( VectorIsNull( g_alias.lightvec )) + { + vecSrc[0] -= 16.0f; + vecSrc[1] -= 16.0f; + vecEnd[0] -= 16.0f; + vecEnd[1] -= 16.0f; - vecSrc[0] -= 16.0f; - vecSrc[1] -= 16.0f; - vecEnd[0] -= 16.0f; - vecEnd[1] -= 16.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[0] += 32.0f; + vecEnd[0] += 32.0f; - vecSrc[0] += 32.0f; - vecEnd[0] += 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[1] += 32.0f; + vecEnd[1] += 32.0f; - vecSrc[1] += 32.0f; - vecEnd[1] += 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[0] -= 32.0f; + vecEnd[0] -= 32.0f; - vecSrc[0] -= 32.0f; - vecEnd[0] -= 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - - lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; - lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; - VectorNormalize( lightDir ); + lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; + lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; + VectorNormalize( lightDir ); + } + else + { + VectorCopy( g_alias.lightvec, lightDir ); + } } VectorSet( finalLight, light.r, light.g, light.b ); @@ -1313,6 +1317,8 @@ static void R_AliasDrawLightTrace( cl_entity_t *e ) { if( r_drawentities->value == 7 ) { + vec3_t origin; + pglDisable( GL_TEXTURE_2D ); pglDisable( GL_DEPTH_TEST ); @@ -1322,6 +1328,13 @@ static void R_AliasDrawLightTrace( cl_entity_t *e ) pglVertex3fv( g_alias.lightspot ); pglEnd(); + pglBegin( GL_LINES ); + pglColor3f( 0, 0.5, 1 ); + VectorMA( g_alias.lightspot, -64.0f, g_alias.lightvec, origin ); + pglVertex3fv( g_alias.lightspot ); + pglVertex3fv( origin ); + pglEnd(); + pglPointSize( 5.0f ); pglColor3f( 1, 0, 0 ); pglBegin( GL_POINTS ); @@ -1429,11 +1442,16 @@ void R_DrawAliasModel( cl_entity_t *e ) R_AliasSetRemapColors( topcolor, bottomcolor ); } - pglTranslatef( m_pAliasHeader->scale_origin[0], m_pAliasHeader->scale_origin[1], m_pAliasHeader->scale_origin[2] ); - if( tr.fFlipViewModel ) + { + pglTranslatef( m_pAliasHeader->scale_origin[0], -m_pAliasHeader->scale_origin[1], m_pAliasHeader->scale_origin[2] ); pglScalef( m_pAliasHeader->scale[0], -m_pAliasHeader->scale[1], m_pAliasHeader->scale[2] ); - else pglScalef( m_pAliasHeader->scale[0], m_pAliasHeader->scale[1], m_pAliasHeader->scale[2] ); + } + else + { + pglTranslatef( m_pAliasHeader->scale_origin[0], m_pAliasHeader->scale_origin[1], m_pAliasHeader->scale_origin[2] ); + pglScalef( m_pAliasHeader->scale[0], m_pAliasHeader->scale[1], m_pAliasHeader->scale[2] ); + } anim = (int)(g_alias.time * 10) & 3; skin = bound( 0, RI.currententity->curstate.skin, m_pAliasHeader->numskins - 1 ); diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index aa510248..25c8b241 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -463,14 +463,14 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) int width = 0, height = 0; qboolean result; - r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); + r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); r_shot->width = (glState.width + 3) & ~3; r_shot->height = (glState.height + 3) & ~3; r_shot->flags = IMAGE_HAS_COLOR; r_shot->type = PF_RGB_24; r_shot->size = r_shot->width * r_shot->height * PFDesc[r_shot->type].bpp; r_shot->palette = NULL; - r_shot->buffer = Mem_Alloc( r_temppool, r_shot->size ); + r_shot->buffer = Mem_Malloc( r_temppool, r_shot->size ); // get screen frame pglReadPixels( 0, 0, r_shot->width, r_shot->height, GL_RGB, GL_UNSIGNED_BYTE, r_shot->buffer ); @@ -546,10 +546,10 @@ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qbo RI.params |= RP_ENVVIEW; // do not render non-bmodel entities // alloc space - temp = Mem_Alloc( r_temppool, size * size * 3 ); - buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 ); - r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); - r_side = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); + temp = Mem_Malloc( r_temppool, size * size * 3 ); + buffer = Mem_Malloc( r_temppool, size * size * 3 * 6 ); + r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); + r_side = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); // use client vieworg if( !vieworg ) vieworg = RI.vieworg; @@ -568,7 +568,7 @@ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qbo { R_DrawCubemapView( vieworg, r_envMapInfo[i].angles, size ); flags = r_envMapInfo[i].flags; - } + } pglReadPixels( 0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp ); r_side->flags = IMAGE_HAS_COLOR; diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index 056f9684..d88a5714 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -442,20 +442,15 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f } // Iterator to resample noise waveform (it needs to be generated in powers of 2) - noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); - noiseIndex = 0; - + noiseStep = noiseIndex = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); + if( FBitSet( flags, FBEAM_SINENOISE )) - { noiseIndex = 0; - } brightness = 1.0f; if( FBitSet( flags, FBEAM_SHADEIN )) - { brightness = 0; - } // Choose two vectors that are perpendicular to the beam R_BeamComputePerpendicular( delta, perp1 ); @@ -1333,7 +1328,7 @@ CL_InitViewBeams */ void CL_InitViewBeams( void ) { - cl_viewbeams = Mem_Alloc( cls.mempool, sizeof( BEAM ) * GI->max_beams ); + cl_viewbeams = Mem_Calloc( cls.mempool, sizeof( BEAM ) * GI->max_beams ); CL_ClearViewBeams(); } diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index d65ef267..0b4aa75a 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -524,7 +524,7 @@ glpoly_t *R_DecalCreatePoly( decalinfo_t *decalinfo, decal_t *pdecal, msurface_t if( !lnumverts ) return NULL; // probably this never happens // allocate glpoly - poly = Mem_Alloc( com_studiocache, sizeof( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof( float )); + poly = Mem_Calloc( com_studiocache, sizeof( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof( float )); poly->next = pdecal->polys; poly->flags = surf->flags; pdecal->polys = poly; diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 971a4923..33858d3a 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -173,7 +173,7 @@ void GL_ApplyTextureParams( gltexture_t *tex ) } // set texture anisotropy if available - if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 )) + if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_ALPHACONTRAST )) pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value ); // set texture LOD bias if available @@ -256,7 +256,7 @@ static void GL_UpdateTextureParams( int iTexture ) GL_Bind( GL_TEXTURE0, iTexture ); // set texture anisotropy if available - if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP )) + if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP|TF_ALPHACONTRAST )) pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value ); // set texture LOD bias if available @@ -554,10 +554,6 @@ static void GL_SetTextureDimensions( gltexture_t *tex, int width, int height, in height = scaled_height; } -#if 1 // TESTTEST - width = (width + 3) & ~3; - height = (height + 3) & ~3; -#endif if( width > maxTextureSize || height > maxTextureSize || depth > maxDepthSize ) { if( tex->target == GL_TEXTURE_1D ) @@ -709,7 +705,11 @@ static void GL_SetTextureFormat( gltexture_t *tex, pixformat_t format, int chann switch( GL_CalcTextureSamples( channelMask )) { - case 1: tex->format = GL_LUMINANCE8; break; + case 1: + if( FBitSet( tex->flags, TF_ALPHACONTRAST )) + tex->format = GL_INTENSITY8; + else tex->format = GL_LUMINANCE8; + break; case 2: tex->format = GL_LUMINANCE8_ALPHA8; break; case 3: switch( bits ) @@ -883,7 +883,7 @@ byte *GL_ApplyFilter( const byte *source, int width, int height ) byte *out = (byte *)source; int i; - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ) || glConfig.max_multisamples > 1 ) return in; for( i = 0; source && i < width * height; i++, in += 4 ) @@ -927,7 +927,7 @@ GL_BuildMipMap Operates in place, quartering the size of the texture ================= */ -static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, qboolean isNormalMap ) +static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, int flags ) { byte *out = in; int instride = ALIGN( srcWidth * 4, 1 ); @@ -937,15 +937,21 @@ static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, if( !in ) return; - mipWidth = max( 1, ( srcWidth >> 1 )); - mipHeight = max( 1, ( srcHeight >> 1 )); + mipWidth = Q_max( 1, ( srcWidth >> 1 )); + mipHeight = Q_max( 1, ( srcHeight >> 1 )); outpadding = ALIGN( mipWidth * 4, 1 ) - mipWidth * 4; row = srcWidth << 2; + if( FBitSet( flags, TF_ALPHACONTRAST )) + { + memset( in, mipWidth, mipWidth * mipHeight * 4 ); + return; + } + // move through all layers for( z = 0; z < srcDepth; z++ ) { - if( isNormalMap ) + if( FBitSet( flags, TF_NORMALMAP )) { for( y = 0; y < mipHeight; y++, in += instride * 2, out += outpadding ) { @@ -1218,10 +1224,10 @@ static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) if(( tex->depth == 1 ) && ( pic->width != tex->width ) || ( pic->height != tex->height )) data = GL_ResampleTexture( buf, pic->width, pic->height, tex->width, tex->height, normalMap ); else data = buf; - +#if 0 // g-cont. we can't apply gamma to each texture so we shouldn't do it at all if( !ImageDXT( pic->type ) && !FBitSet( tex->flags, TF_NOMIPMAP|TF_SKYSIDE )) data = GL_ApplyGamma( data, tex->width * tex->height * tex->depth, FBitSet( tex->flags, TF_NORMALMAP )); - +#endif if( !ImageDXT( pic->type ) && !FBitSet( tex->flags, TF_NOMIPMAP ) && FBitSet( pic->flags, IMAGE_ONEBIT_ALPHA )) data = GL_ApplyFilter( data, tex->width, tex->height ); @@ -1234,7 +1240,7 @@ static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) size = GL_CalcImageSize( pic->type, width, height, tex->depth ); GL_TextureImageRAW( tex, i, j, width, height, tex->depth, pic->type, data ); if( mipCount > 1 ) - GL_BuildMipMap( data, width, height, tex->depth, normalMap ); + GL_BuildMipMap( data, width, height, tex->depth, tex->flags ); tex->size += texsize; tex->numMips++; @@ -1495,11 +1501,11 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) else { // create new image - pic = Mem_Alloc( host.imagepool, sizeof( rgbdata_t )); + pic = Mem_Malloc( host.imagepool, sizeof( rgbdata_t )); memcpy( pic, src, sizeof( rgbdata_t )); // expand pic buffer for all layers - pic->buffer = Mem_Alloc( host.imagepool, pic->size * numLayers ); + pic->buffer = Mem_Malloc( host.imagepool, pic->size * numLayers ); pic->depth = 0; } @@ -1678,6 +1684,9 @@ int GL_CreateTexture( const char *name, int width, int height, const void *buffe r_empty.flags = IMAGE_HAS_COLOR | (( flags & TF_HAS_ALPHA ) ? IMAGE_HAS_ALPHA : 0 ); r_empty.buffer = (byte *)buffer; + if( FBitSet( flags, TF_ALPHACONTRAST )) + ClearBits( r_empty.flags, IMAGE_HAS_COLOR ); + if( FBitSet( flags, TF_TEXTURE_1D )) { r_empty.height = 1; diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index ab3ef9a0..75e69504 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -38,6 +38,7 @@ extern byte *r_temppool; #define SHADEDOT_QUANT 16 // precalculated dot products for quantized angles #define SHADE_LAMBERT 1.495f +#define DEFAULT_ALPHATEST 0.0f // refparams #define RP_NONE 0 @@ -293,6 +294,12 @@ void R_Set2DMode( qboolean enable ); void R_DrawTileClear( int x, int y, int w, int h ); void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height, const byte *data ); +// +// gl_drawhulls.c +// +void R_DrawWorldHull( void ); +void R_DrawModelHull( void ); + // // gl_image.c // @@ -329,7 +336,7 @@ void R_AnimateLight( void ); void R_GetLightSpot( vec3_t lightspot ); void R_MarkLights( dlight_t *light, int bit, mnode_t *node ); void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, qboolean useAmbient, float radius ); -colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot ); +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot, vec3_t lightvec ); int R_CountSurfaceDlights( msurface_t *surf ); colorVec R_LightPoint( const vec3_t p0 ); int R_CountDlights( void ); @@ -417,6 +424,7 @@ float CL_GetStudioEstimatedFrame( cl_entity_t *ent ); int R_GetEntityRenderMode( cl_entity_t *ent ); void R_DrawStudioModel( cl_entity_t *e ); player_info_t *pfnPlayerInfo( int index ); +void R_GatherPlayerLight( void ); // // gl_alias.c @@ -520,7 +528,6 @@ enum GL_ARB_DEPTH_FLOAT_EXT, GL_ARB_SEAMLESS_CUBEMAP, GL_EXT_GPU_SHADER4, // shaders only - GL_ARB_TEXTURE_RG, GL_DEPTH_TEXTURE, GL_DEBUG_OUTPUT, GL_EXTCOUNT, // must be last @@ -638,6 +645,7 @@ extern convar_t *gl_finish; extern convar_t *gl_nosort; extern convar_t *gl_clear; extern convar_t *gl_test; // cvar to testify new effects +extern convar_t *gl_msaa; extern convar_t *r_speeds; extern convar_t *r_fullbright; diff --git a/engine/client/gl_rlight.c b/engine/client/gl_rlight.c index 9d9fd675..91fe10e7 100644 --- a/engine/client/gl_rlight.c +++ b/engine/client/gl_rlight.c @@ -49,7 +49,7 @@ void CL_RunLightStyles( void ) // 'm' is normal light, 'a' is no light, 'z' is double bright for( i = 0, ls = cl.lightstyles; i < MAX_LIGHTSTYLES; i++, ls++ ) { - if( r_fullbright->value || !cl.worldmodel->lightdata ) + if( !cl.worldmodel->lightdata ) { tr.lightstylevalue[i] = 256 * 256; continue; @@ -217,6 +217,7 @@ int R_CountSurfaceDlights( msurface_t *surf ) ======================================================================= */ static vec3_t g_trace_lightspot; +static vec3_t g_trace_lightvec; static float g_trace_fraction; /* @@ -230,10 +231,11 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, int i, map, side, size; float ds, dt, s, t; int sample_size; + color24 *lm, *dm; mextrasurf_t *info; msurface_t *surf; mtexinfo_t *tex; - color24 *lm; + matrix3x4 tbn; vec3_t mid; // didn't hit anything @@ -306,6 +308,31 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds ); g_trace_fraction = midf; size = smax * tmax; + dm = NULL; + + if( surf->info->deluxemap ) + { + vec3_t faceNormal; + + if( FBitSet( surf->flags, SURF_PLANEBACK )) + VectorNegate( surf->plane->normal, faceNormal ); + else VectorCopy( surf->plane->normal, faceNormal ); + + // compute face TBN +#if 1 + Vector4Set( tbn[0], surf->info->lmvecs[0][0], surf->info->lmvecs[0][1], surf->info->lmvecs[0][2], 0.0f ); + Vector4Set( tbn[1], -surf->info->lmvecs[1][0], -surf->info->lmvecs[1][1], -surf->info->lmvecs[1][2], 0.0f ); + Vector4Set( tbn[2], faceNormal[0], faceNormal[1], faceNormal[2], 0.0f ); +#else + Vector4Set( tbn[0], surf->info->lmvecs[0][0], -surf->info->lmvecs[1][0], faceNormal[0], 0.0f ); + Vector4Set( tbn[1], surf->info->lmvecs[0][1], -surf->info->lmvecs[1][1], faceNormal[1], 0.0f ); + Vector4Set( tbn[2], surf->info->lmvecs[0][2], -surf->info->lmvecs[1][2], faceNormal[2], 0.0f ); +#endif + VectorNormalize( tbn[0] ); + VectorNormalize( tbn[1] ); + VectorNormalize( tbn[2] ); + dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds ); + } for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) { @@ -324,6 +351,18 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, cv->b += LightToTexGamma( lm->b ) * scale; } lm += size; // skip to next lightmap + + if( dm != NULL ) + { + vec3_t srcNormal, lightNormal; + float f = (1.0f / 255.0f); + + VectorSet( srcNormal, (dm->r * f) * 2.0f - 1.0f, (dm->g * f) * 2.0f - 1.0f, (dm->b * f) * 2.0f - 1.0f ); + Matrix3x4_VectorIRotate( tbn, srcNormal, lightNormal ); // turn to world space + VectorScale( lightNormal, (float)scale * -1.0f, lightNormal ); // turn direction from light + VectorAdd( g_trace_lightvec, lightNormal, g_trace_lightvec ); + dm += size; // skip to next deluxmap + } } return true; @@ -340,13 +379,14 @@ R_LightVec check bspmodels to get light from ================= */ -colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot ) +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) { float last_fraction; int i, maxEnts = 1; colorVec light, cv; if( lspot ) VectorClear( lspot ); + if( lvec ) VectorClear( lvec ); if( cl.worldmodel && cl.worldmodel->lightdata ) { @@ -354,7 +394,7 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot ) last_fraction = 1.0f; // get light from bmodels too - if( r_lighting_extended->value ) + if( CVAR_TO_BOOL( r_lighting_extended )) maxEnts = clgame.pmove->numphysent; // check all the bsp-models @@ -383,6 +423,7 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot ) } VectorClear( g_trace_lightspot ); + VectorClear( g_trace_lightvec ); g_trace_fraction = 1.0f; if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l )) @@ -391,6 +432,7 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot ) if( g_trace_fraction < last_fraction ) { if( lspot ) VectorCopy( g_trace_lightspot, lspot ); + if( lvec ) VectorNormalize2( g_trace_lightvec, lvec ); light.r = Q_min(( cv.r >> 7 ), 255 ); light.g = Q_min(( cv.g >> 7 ), 255 ); light.b = Q_min(( cv.b >> 7 ), 255 ); @@ -420,5 +462,5 @@ colorVec R_LightPoint( const vec3_t p0 ) VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); - return R_LightVec( p0, p1, NULL ); -} \ No newline at end of file + return R_LightVec( p0, p1, NULL, NULL ); +} diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 793b3c2b..eb3d0dd6 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -24,8 +24,6 @@ GNU General Public License for more details. #define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) -msurface_t *r_debug_surface; -const char *r_debug_hitbox; float gldepthmin, gldepthmax; ref_instance_t RI; @@ -242,10 +240,10 @@ qboolean R_AddEntity( struct cl_entity_s *clent, int type ) if( !clent || !clent->model ) return false; // if set to invisible, skip - if( clent->curstate.effects & EF_NODRAW ) + if( FBitSet( clent->curstate.effects, EF_NODRAW )) return false; // done - if( clent->curstate.rendermode != kRenderNormal && CL_FxBlend( clent ) <= 0 ) + if( !R_ModelOpaque( clent->curstate.rendermode ) && CL_FxBlend( clent ) <= 0 ) return true; // invisible if( type == ET_FRAGMENTED ) @@ -541,7 +539,7 @@ void R_SetupGL( qboolean set_gl_state ) pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.worldviewMatrix ); - if( RI.params & RP_CLIPPLANE ) + if( FBitSet( RI.params, RP_CLIPPLANE )) { GLdouble clip[4]; mplane_t *p = &RI.clipPlane; @@ -1084,8 +1082,22 @@ void R_RenderFrame( const ref_viewpass_t *rvp ) if( gl_finish->value && RI.drawWorld ) pglFinish(); - if( glConfig.max_multisamples > 1 ) - pglEnable( GL_MULTISAMPLE_ARB ); + if( glConfig.max_multisamples > 1 && FBitSet( gl_msaa->flags, FCVAR_CHANGED )) + { + if( CVAR_TO_BOOL( gl_msaa )) + { + pglEnable( GL_MULTISAMPLE_ARB ); + if( gl_msaa->value > 1.0f ) + pglEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); + else pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); + } + else + { + pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); + pglDisable( GL_MULTISAMPLE_ARB ); + } + ClearBits( gl_msaa->flags, FCVAR_CHANGED ); + } // completely override rendering if( clgame.drawFuncs.GL_RenderFrame != NULL ) @@ -1094,6 +1106,7 @@ void R_RenderFrame( const ref_viewpass_t *rvp ) if( clgame.drawFuncs.GL_RenderFrame( rvp )) { + R_GatherPlayerLight(); tr.realframecount++; tr.fResetVis = true; return; @@ -1383,7 +1396,7 @@ static const ref_overview_t *GL_GetOverviewParms( void ) static void *R_Mem_Alloc( size_t cb, const char *filename, const int fileline ) { - return _Mem_Alloc( cls.mempool, cb, filename, fileline ); + return _Mem_Alloc( cls.mempool, cb, true, filename, fileline ); } static void R_Mem_Free( void *mem, const char *filename, const int fileline ) diff --git a/engine/client/gl_rpart.c b/engine/client/gl_rpart.c index 44552b80..cb46866d 100644 --- a/engine/client/gl_rpart.c +++ b/engine/client/gl_rpart.c @@ -128,7 +128,7 @@ void CL_InitParticles( void ) { int i; - cl_particles = Mem_Alloc( cls.mempool, sizeof( particle_t ) * GI->max_particles ); + cl_particles = Mem_Calloc( cls.mempool, sizeof( particle_t ) * GI->max_particles ); CL_ClearParticles (); // this is used for EF_BRIGHTFIELD diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 669e6044..5437f64c 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -151,7 +151,7 @@ static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts ClearBits( warpface->flags, SURF_DRAWTURB_QUADS ); // add a point in the center to help keep warp valid - poly = Mem_Alloc( loadmodel->mempool, sizeof( glpoly_t ) + (numverts - 4) * VERTEXSIZE * sizeof( float )); + poly = Mem_Calloc( loadmodel->mempool, sizeof( glpoly_t ) + (numverts - 4) * VERTEXSIZE * sizeof( float )); poly->next = warpface->polys; poly->flags = warpface->flags; warpface->polys = poly; @@ -280,13 +280,10 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) float s, t; glpoly_t *poly; - // already created - if( !mod || fa->polys ) return; - - if( !fa->texinfo || !fa->texinfo->texture ) + if( !mod || !fa->texinfo || !fa->texinfo->texture ) return; // bad polygon ? - if( fa->flags & SURF_CONVEYOR && fa->texinfo->texture->gl_texturenum != 0 ) + if( FBitSet( fa->flags, SURF_CONVEYOR ) && fa->texinfo->texture->gl_texturenum != 0 ) { glt = R_GetTexture( fa->texinfo->texture->gl_texturenum ); tex = fa->texinfo->texture; @@ -304,8 +301,12 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) lnumverts = fa->numedges; vertpage = 0; - // draw texture - poly = Mem_Alloc( mod->mempool, sizeof( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof( float )); + // detach if already created, reconstruct again + poly = fa->polys; + fa->polys = NULL; + + // quake simple models (healthkits etc) need to be reconstructed their polys because LM coords has changed after the map change + poly = Mem_Realloc( mod->mempool, poly, sizeof( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof( float )); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; @@ -1294,7 +1295,7 @@ void R_DrawAlphaTextureChains( void ) GL_ResetFogColor(); R_BlendLightmaps(); RI.currententity->curstate.rendermode = kRenderNormal; // restore world rendermode - pglAlphaFunc( GL_GREATER, 0.0f ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); } /* @@ -1564,9 +1565,10 @@ void R_DrawBrushModel( cl_entity_t *e ) e->curstate.rendermode = old_rendermode; pglDisable( GL_ALPHA_TEST ); - pglAlphaFunc( GL_GREATER, 0.0f ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); pglDisable( GL_BLEND ); pglDepthMask( GL_TRUE ); + R_DrawModelHull(); // draw before restore R_LoadIdentity(); // restore worldmatrix } @@ -1904,6 +1906,8 @@ void R_DrawWorld( void ) skychain = NULL; R_DrawTriangleOutlines (); + + R_DrawWorldHull(); } /* @@ -1996,8 +2000,10 @@ void GL_CreateSurfaceLightmap( msurface_t *surf ) mextrasurf_t *info = surf->info; byte *base; - if( !cl.worldmodel->lightdata ) return; - if( surf->flags & SURF_DRAWTILED ) + if( !loadmodel->lightdata ) + return; + + if( FBitSet( surf->flags, SURF_DRAWTILED )) return; sample_size = Mod_SampleSizeForFace( surf ); diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index 0bb57f5b..5532400e 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -78,7 +78,7 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t } // setup frame description - pspriteframe = Mem_Alloc( mod->mempool, sizeof( mspriteframe_t )); + pspriteframe = Mem_Malloc( mod->mempool, sizeof( mspriteframe_t )); pspriteframe->width = pinframe->width; pspriteframe->height = pinframe->height; pspriteframe->up = pinframe->origin[1]; @@ -111,12 +111,12 @@ static dframetype_t *R_SpriteLoadGroup( model_t *mod, void *pin, mspriteframe_t numframes = pingroup->numframes; groupsize = sizeof( mspritegroup_t ) + (numframes - 1) * sizeof( pspritegroup->frames[0] ); - pspritegroup = Mem_Alloc( mod->mempool, groupsize ); + pspritegroup = Mem_Calloc( mod->mempool, groupsize ); pspritegroup->numframes = numframes; *ppframe = (mspriteframe_t *)pspritegroup; pin_intervals = (dspriteinterval_t *)(pingroup + 1); - poutintervals = Mem_Alloc( mod->mempool, numframes * sizeof( float )); + poutintervals = Mem_Calloc( mod->mempool, numframes * sizeof( float )); pspritegroup->intervals = poutintervals; for( i = 0; i < numframes; i++ ) @@ -179,7 +179,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui { pinq1 = (dsprite_q1_t *)buffer; size = sizeof( msprite_t ) + ( pinq1->numframes - 1 ) * sizeof( psprite->frames ); - psprite = Mem_Alloc( mod->mempool, size ); + psprite = Mem_Calloc( mod->mempool, size ); mod->cache.data = psprite; // make link to extradata psprite->type = pinq1->type; @@ -199,7 +199,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui { pinhl = (dsprite_hl_t *)buffer; size = sizeof( msprite_t ) + ( pinhl->numframes - 1 ) * sizeof( psprite->frames ); - psprite = Mem_Alloc( mod->mempool, size ); + psprite = Mem_Calloc( mod->mempool, size ); mod->cache.data = psprite; // make link to extradata psprite->type = pinhl->type; @@ -347,7 +347,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean // determine how many frames we needs numframes = (pix->width * pix->height) / (w * h); mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name )); - psprite = Mem_Alloc( mod->mempool, sizeof( msprite_t ) + ( numframes - 1 ) * sizeof( psprite->frames )); + psprite = Mem_Calloc( mod->mempool, sizeof( msprite_t ) + ( numframes - 1 ) * sizeof( psprite->frames )); mod->cache.data = psprite; // make link to extradata psprite->type = SPR_FWD_PARALLEL_ORIENTED; @@ -367,7 +367,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean temp.type = pix->type; temp.flags = pix->flags; temp.size = w * h * PFDesc[temp.type].bpp; - temp.buffer = Mem_Alloc( r_temppool, temp.size ); + temp.buffer = Mem_Malloc( r_temppool, temp.size ); temp.palette = NULL; // chop the image and upload into video memory @@ -392,7 +392,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean // build uinque frame name Q_snprintf( texname, sizeof( texname ), "#MAP/%s_%i%i.spr", mod->name, i / 10, i % 10 ); - psprite->frames[i].frameptr = Mem_Alloc( mod->mempool, sizeof( mspriteframe_t )); + psprite->frames[i].frameptr = Mem_Calloc( mod->mempool, sizeof( mspriteframe_t )); pspriteframe = psprite->frames[i].frameptr; pspriteframe->width = w; pspriteframe->height = h; @@ -979,7 +979,7 @@ void R_DrawSpriteModel( cl_entity_t *e ) color2[1] = (float)lightColor.g * ( 1.0f / 255.0f ); color2[2] = (float)lightColor.b * ( 1.0f / 255.0f ); // NOTE: sprites with 'lightmap' looks ugly when alpha func is GL_GREATER 0.0 - pglAlphaFunc( GL_GREATER, 0.25f ); + pglAlphaFunc( GL_GREATER, 0.5f ); } if( R_SpriteAllowLerping( e, psprite )) @@ -1073,7 +1073,7 @@ void R_DrawSpriteModel( cl_entity_t *e ) pglColor4f( color2[0], color2[1], color2[2], tr.blend ); GL_Bind( GL_TEXTURE0, tr.whiteTexture ); R_DrawSpriteQuad( frame, origin, v_right, v_up, scale ); - pglAlphaFunc( GL_GREATER, 0.0f ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); pglDepthFunc( GL_LEQUAL ); } diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 7a0cbfba..4a5862d1 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -139,7 +139,7 @@ void R_StudioInit( void ) Matrix3x4_LoadIdentity( g_studio.rotationmatrix ); Cvar_RegisterVariable( &r_glowshellfreq ); -// g-cont. especially not registered + // g-cont. cvar disabled by Valve // Cvar_RegisterVariable( &r_shadows ); g_studio.interpolate = true; @@ -739,7 +739,7 @@ void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstud if( paSequences == NULL ) { - paSequences = (cache_user_t *)Mem_Alloc( com_studiocache, MAXSTUDIOGROUPS * sizeof( cache_user_t )); + paSequences = (cache_user_t *)Mem_Calloc( com_studiocache, MAXSTUDIOGROUPS * sizeof( cache_user_t )); m_pSubModel->submodels = (void *)paSequences; } @@ -760,7 +760,7 @@ void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstud Con_Printf( "loading: %s\n", filepath ); - paSequences[pseqdesc->seqgroup].data = Mem_Alloc( com_studiocache, filesize ); + paSequences[pseqdesc->seqgroup].data = Mem_Calloc( com_studiocache, filesize ); memcpy( paSequences[pseqdesc->seqgroup].data, buf, filesize ); Mem_Free( buf ); } @@ -1688,40 +1688,44 @@ void R_StudioDynamicLight( cl_entity_t *ent, alight_t *plight ) VectorScale( lightDir, 2048.0f, vecEnd ); VectorAdd( vecEnd, vecSrc, vecEnd ); - light = R_LightVec( vecSrc, vecEnd, g_studio.lightspot ); + light = R_LightVec( vecSrc, vecEnd, g_studio.lightspot, g_studio.lightvec ); - VectorScale( lightDir, 2048.0f, vecEnd ); - VectorAdd( vecEnd, vecSrc, vecEnd ); + if( VectorIsNull( g_studio.lightvec )) + { + vecSrc[0] -= 16.0f; + vecSrc[1] -= 16.0f; + vecEnd[0] -= 16.0f; + vecEnd[1] -= 16.0f; - vecSrc[0] -= 16.0f; - vecSrc[1] -= 16.0f; - vecEnd[0] -= 16.0f; - vecEnd[1] -= 16.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[0] += 32.0f; + vecEnd[0] += 32.0f; - vecSrc[0] += 32.0f; - vecEnd[0] += 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[1] += 32.0f; + vecEnd[1] += 32.0f; - vecSrc[1] += 32.0f; - vecEnd[1] += 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; + vecSrc[0] -= 32.0f; + vecEnd[0] -= 32.0f; - vecSrc[0] -= 32.0f; - vecEnd[0] -= 32.0f; + gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL ); + grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - gcolor = R_LightVec( vecSrc, vecEnd, NULL ); - grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f; - - lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; - lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; - VectorNormalize( lightDir ); + lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3]; + lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3]; + VectorNormalize( lightDir ); + } + else + { + VectorCopy( g_studio.lightvec, lightDir ); + } } VectorSet( finalLight, light.r, light.g, light.b ); @@ -2427,7 +2431,7 @@ static void R_StudioDrawPoints( void ) if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED )) { - pglAlphaFunc( GL_GREATER, 0.0f ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); pglDisable( GL_ALPHA_TEST ); } else if( FBitSet( g_nFaceFlags, STUDIO_NF_ADDITIVE ) && R_ModelOpaque( RI.currententity->curstate.rendermode )) @@ -2531,6 +2535,7 @@ static void R_StudioDrawAbsBBox( void ) TriVertex3fv( p[boxpnt[i][3]] ); } TriEnd(); + TriRenderMode( kRenderNormal ); } /* @@ -3126,6 +3131,13 @@ void R_StudioRenderFinal( void ) pglVertex3fv( g_studio.lightspot ); pglEnd(); + pglBegin( GL_LINES ); + pglColor3f( 0, 0.5, 1 ); + VectorMA( g_studio.lightspot, -64.0f, g_studio.lightvec, origin ); + pglVertex3fv( g_studio.lightspot ); + pglVertex3fv( origin ); + pglEnd(); + pglPointSize( 5.0f ); pglColor3f( 1, 0, 0 ); pglBegin( GL_POINTS ); @@ -3631,10 +3643,10 @@ void R_RunViewmodelEvents( void ) /* ================= -R_DrawViewModel +R_GatherPlayerLight ================= */ -void R_DrawViewModel( void ) +void R_GatherPlayerLight( void ) { cl_entity_t *view = &clgame.viewent; colorVec c; @@ -3643,6 +3655,18 @@ void R_DrawViewModel( void ) c = R_LightPoint( view->origin ); tr.ignore_lightgamma = false; cl.local.light_level = (c.r + c.g + c.b) / 3; +} + +/* +================= +R_DrawViewModel +================= +*/ +void R_DrawViewModel( void ) +{ + cl_entity_t *view = &clgame.viewent; + + R_GatherPlayerLight(); if( r_drawviewmodel->value == 0 ) return; @@ -3674,6 +3698,10 @@ void R_DrawViewModel( void ) pglFrontFace( GL_CW ); } + // FIXME: viewmodel is invisible when alpha to coverage is enabled + if( glConfig.max_multisamples > 1 && gl_msaa->value > 1.0f ) + pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); + switch( RI.currententity->model->type ) { case mod_alias: @@ -3685,6 +3713,9 @@ void R_DrawViewModel( void ) break; } + if( glConfig.max_multisamples > 1 && gl_msaa->value > 1.0f ) + pglEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); + // restore depth range pglDepthRange( gldepthmin, gldepthmax ); @@ -3724,7 +3755,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture i = mod->numtextures; mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* )); size = ptexture->width * ptexture->height + 768; - tx = Mem_Alloc( mod->mempool, sizeof( *tx ) + size ); + tx = Mem_Calloc( mod->mempool, sizeof( *tx ) + size ); mod->textures[i] = tx; // store ranges into anim_min, anim_max etc diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index a7335ee5..59cd2c52 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -32,11 +32,11 @@ convar_t *gl_texture_anisotropy; convar_t *gl_texture_lodbias; convar_t *gl_texture_nearest; convar_t *gl_lightmap_nearest; +convar_t *gl_wgl_msaa_samples; convar_t *gl_keeptjunctions; convar_t *gl_showtextures; convar_t *gl_detailscale; convar_t *gl_check_errors; -convar_t *gl_enable_msaa; convar_t *gl_round_down; convar_t *gl_polyoffset; convar_t *gl_wireframe; @@ -45,6 +45,7 @@ convar_t *gl_nosort; convar_t *gl_vsync; convar_t *gl_clear; convar_t *gl_test; +convar_t *gl_msaa; convar_t *window_xpos; convar_t *window_ypos; @@ -356,8 +357,6 @@ static void CALLBACK GL_DebugOutput( GLuint source, GLuint type, GLuint id, GLui Con_Printf( S_OPENGL_WARN "%s\n", message ); break; case GL_DEBUG_TYPE_PERFORMANCE_ARB: - if( host_developer.value < DEV_EXTENDED ) - return; Con_Printf( S_OPENGL_NOTE "%s\n", message ); break; case GL_DEBUG_TYPE_OTHER_ARB: @@ -531,7 +530,7 @@ static void GL_SetDefaultState( void ) memset( &glState, 0, sizeof( glState )); GL_SetDefaultTexState (); - if( Sys_CheckParm( "-gldebug" ) && host_developer.value ) + if( Sys_CheckParm( "-gldebug" )) debug_context = true; else debug_context = false; @@ -698,9 +697,9 @@ static int VID_ChoosePFD( PIXELFORMATDESCRIPTOR *pfd, int colorBits, int alphaBi attribs[16] = WGL_STENCIL_BITS_ARB; attribs[17] = stencilBits; attribs[18] = WGL_SAMPLE_BUFFERS_ARB; - attribs[19] = 1; + attribs[19] = TRUE; attribs[20] = WGL_SAMPLES_ARB; - attribs[21] = bound( 2, (int)gl_enable_msaa->value, 16 ); + attribs[21] = bound( 1, (int)gl_wgl_msaa_samples->value, 16 ); attribs[22] = 0; attribs[23] = 0; @@ -832,7 +831,7 @@ void VID_CreateFakeWindow( void ) int pixelFormat; // MSAA disabled - if( !gl_enable_msaa->value ) + if( !CVAR_TO_BOOL( gl_wgl_msaa_samples )) return; memset( &wndClass, 0, sizeof( WNDCLASSEX )); @@ -1442,7 +1441,7 @@ qboolean R_Init_OpenGL( void ) if( !opengl_dll.link ) return false; - if( debug_context || gl_enable_msaa->value ) + if( debug_context || CVAR_TO_BOOL( gl_wgl_msaa_samples )) GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS ); return VID_SetMode(); @@ -1499,7 +1498,7 @@ static void GL_SetDefaults( void ) pglDisable( GL_BLEND ); pglDisable( GL_ALPHA_TEST ); pglDisable( GL_POLYGON_OFFSET_FILL ); - pglAlphaFunc( GL_GREATER, 0.0f ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); pglEnable( GL_TEXTURE_2D ); pglShadeModel( GL_SMOOTH ); pglFrontFace( GL_CCW ); @@ -1590,7 +1589,7 @@ void GL_InitCommands( void ) window_ypos = Cvar_Get( "_window_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" ); gl_extensions = Cvar_Get( "gl_allow_extensions", "1", FCVAR_GLCONFIG, "allow gl_extensions" ); - gl_enable_msaa = Cvar_Get( "gl_enable_msaa", "4", FCVAR_GLCONFIG, "enable multisample anti-aliasing" ); + gl_wgl_msaa_samples = Cvar_Get( "gl_wgl_msaa_samples", "4", FCVAR_GLCONFIG, "enable multisample anti-aliasing" ); gl_texture_nearest = Cvar_Get( "gl_texture_nearest", "0", FCVAR_ARCHIVE, "disable texture filter" ); gl_lightmap_nearest = Cvar_Get( "gl_lightmap_nearest", "0", FCVAR_ARCHIVE, "disable lightmap filter" ); gl_check_errors = Cvar_Get( "gl_check_errors", "1", FCVAR_ARCHIVE, "ignore video engine errors" ); @@ -1606,6 +1605,7 @@ void GL_InitCommands( void ) gl_test = Cvar_Get( "gl_test", "0", 0, "engine developer cvar for quick testing new features" ); gl_wireframe = Cvar_Get( "gl_wireframe", "0", FCVAR_ARCHIVE|FCVAR_SPONLY, "show wireframe overlay" ); gl_round_down = Cvar_Get( "gl_round_down", "2", FCVAR_RENDERINFO, "round texture sizes to nearest POT value" ); + gl_msaa = Cvar_Get( "gl_msaa", "2", FCVAR_ARCHIVE, "enable multi sample anti-aliasing" ); // these cvar not used by engine but some mods requires this gl_polyoffset = Cvar_Get( "gl_polyoffset", "2.0", FCVAR_ARCHIVE, "polygon offset for decals" ); @@ -1680,7 +1680,7 @@ void GL_InitExtensions( void ) else glConfig.hardware_type = GLHW_GENERIC; // initalize until base opengl functions loaded (old-context) - if( !debug_context && !gl_enable_msaa->value ) + if( !debug_context && !CVAR_TO_BOOL( gl_wgl_msaa_samples )) GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS ); // windows-specific extensions @@ -1761,7 +1761,6 @@ void GL_InitExtensions( void ) GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, "gl_texture_float", GL_ARB_DEPTH_FLOAT_EXT ); GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, NULL, GL_EXT_GPU_SHADER4 ); // don't confuse users GL_CheckExtension( "GL_ARB_shading_language_100", NULL, NULL, GL_SHADER_GLSL100_EXT ); -// GL_CheckExtension( "GL_ARB_texture_rg", NULL, "gl_arb_texture_rg", GL_ARB_TEXTURE_RG ); // this won't work without extended context if( glw_state.extended ) @@ -1779,7 +1778,7 @@ void GL_InitExtensions( void ) pglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.max_vertex_uniforms ); pglGetIntegerv( GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.max_vertex_attribs ); - if( glConfig.hardware_type == GLHW_RADEON ) + if( glConfig.hardware_type == GLHW_RADEON && glConfig.max_vertex_uniforms > 512 ) glConfig.max_vertex_uniforms /= 4; // radeon returns not correct info } else @@ -1814,8 +1813,7 @@ void GL_InitExtensions( void ) pglEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB ); // enable all the low priority messages - if( host_developer.value >= DEV_EXTENDED ) - pglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, true ); + pglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, true ); } tr.framecount = tr.visframecount = 1; diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index d9ba91c8..15a6ed77 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -679,7 +679,7 @@ void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) // make an average value for the back to avoid // a fringe on the top level - trans = Mem_Alloc( r_temppool, r_sky->height * r_sky->height * sizeof( *trans )); + trans = Mem_Malloc( r_temppool, r_sky->height * r_sky->height * sizeof( *trans )); r = g = b = 0; for( i = 0; i < r_sky->width >> 1; i++ ) diff --git a/engine/client/s_dsp.c b/engine/client/s_dsp.c index f0e25270..b6144719 100644 --- a/engine/client/s_dsp.c +++ b/engine/client/s_dsp.c @@ -273,7 +273,7 @@ int DLY_Init( int idelay, float delay ) cur = &rgsxdly[idelay]; cur->cdelaysamplesmax = ((int)(delay * idsp_dma_speed) << sxhires) + 1; - cur->lpdelayline = (int *)Z_Malloc( cur->cdelaysamplesmax * sizeof( int )); + cur->lpdelayline = (int *)Z_Calloc( cur->cdelaysamplesmax * sizeof( int )); cur->xfade = 0; // init modulation diff --git a/engine/client/s_load.c b/engine/client/s_load.c index 86a12556..409909c8 100644 --- a/engine/client/s_load.c +++ b/engine/client/s_load.c @@ -104,7 +104,7 @@ static wavdata_t *S_CreateDefaultSound( void ) { wavdata_t *sc; - sc = Mem_Alloc( sndpool, sizeof( wavdata_t )); + sc = Mem_Calloc( sndpool, sizeof( wavdata_t )); sc->width = 2; sc->channels = 1; @@ -112,7 +112,7 @@ static wavdata_t *S_CreateDefaultSound( void ) sc->rate = SOUND_DMA_SPEED; sc->samples = SOUND_DMA_SPEED; sc->size = sc->samples * sc->width * sc->channels; - sc->buffer = Mem_Alloc( sndpool, sc->size ); + sc->buffer = Mem_Calloc( sndpool, sc->size ); return sc; } diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 6d3eb713..3e37cdfc 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1454,7 +1454,7 @@ rawchan_t *S_FindRawChannel( int entnum, qboolean create ) if( !raw_channels[best] ) { raw_samples = MAX_RAW_SAMPLES; - raw_channels[best] = Mem_Alloc( sndpool, sizeof( *ch ) + sizeof( portable_samplepair_t ) * ( raw_samples - 1 )); + raw_channels[best] = Mem_Calloc( sndpool, sizeof( *ch ) + sizeof( portable_samplepair_t ) * ( raw_samples - 1 )); } ch = raw_channels[best]; diff --git a/engine/client/s_vox.c b/engine/client/s_vox.c index f36ef5a5..4082f69a 100644 --- a/engine/client/s_vox.c +++ b/engine/client/s_vox.c @@ -617,6 +617,12 @@ void VOX_ReadSentenceFile( const char *psentenceFileName ) while( pch < pchlast ) { + if( g_numSentences >= MAX_SENTENCES ) + { + Con_Printf( S_ERROR "VOX_Init: too many sentences specified\n" ); + break; + } + // only process this pass on sentences pSentenceData = NULL; diff --git a/engine/client/vgui/vgui_surf.cpp b/engine/client/vgui/vgui_surf.cpp index 9e28a9c8..26f00430 100644 --- a/engine/client/vgui/vgui_surf.cpp +++ b/engine/client/vgui/vgui_surf.cpp @@ -42,7 +42,7 @@ static Font* staticFont = NULL; static FontInfo* staticFontInfo; static Dar staticFontInfoDar; static PaintStack paintStack[MAX_PAINT_STACK]; -static staticPaintStackPos = 0; +static int staticPaintStackPos = 0; CEngineSurface :: CEngineSurface( Panel *embeddedPanel ):SurfaceBase( embeddedPanel ) { diff --git a/engine/client/vox.h b/engine/client/vox.h index d043dd9a..d7622c53 100644 --- a/engine/client/vox.h +++ b/engine/client/vox.h @@ -18,7 +18,7 @@ GNU General Public License for more details. #define CVOXWORDMAX 64 #define CVOXZEROSCANMAX 255 // scan up to this many samples for next zero crossing -#define MAX_SENTENCES 2048 +#define MAX_SENTENCES 4096 #define SENTENCE_INDEX -99999 // unique sentence index typedef struct voxword_s diff --git a/engine/common/avikit.c b/engine/common/avikit.c index 1fb6ead2..deca2199 100644 --- a/engine/common/avikit.c +++ b/engine/common/avikit.c @@ -205,8 +205,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) return false; } - Avi->cpa_srcbuffer = (byte *)Mem_Alloc( cls.mempool, Avi->cpa_blockalign ); - Avi->cpa_dstbuffer = (byte *)Mem_Alloc( cls.mempool, dest_length ); // maintained buffer for raw data + Avi->cpa_srcbuffer = (byte *)Mem_Malloc( cls.mempool, Avi->cpa_blockalign ); + Avi->cpa_dstbuffer = (byte *)Mem_Malloc( cls.mempool, dest_length ); // maintained buffer for raw data // prep the headers! Avi->cpa_conversion_header.cbStruct = sizeof( ACMSTREAMHEADER ); @@ -532,7 +532,7 @@ void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audi // read the audio header pAVIStreamReadFormat( Avi->audio_stream, pAVIStreamStart( Avi->audio_stream ), 0, &size ); - Avi->audio_header = (WAVEFORMAT *)Mem_Alloc( cls.mempool, size ); + Avi->audio_header = (WAVEFORMAT *)Mem_Malloc( cls.mempool, size ); pAVIStreamReadFormat( Avi->audio_stream, pAVIStreamStart( Avi->audio_stream ), Avi->audio_header, &size ); Avi->audio_header_size = size; Avi->audio_codec = Avi->audio_header->wFormatTag; @@ -634,7 +634,7 @@ movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio ) return NULL; } - Avi = Mem_Alloc( cls.mempool, sizeof( movie_state_t )); + Avi = Mem_Malloc( cls.mempool, sizeof( movie_state_t )); AVI_OpenVideo( Avi, fullpath, load_audio, false ); if( !AVI_IsActive( Avi )) diff --git a/engine/common/build.c b/engine/common/build.c index b7f5df34..0bd5ad9a 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -23,7 +23,7 @@ static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int Q_buildnum( void ) { // do not touch this! Only author of Xash3D can increase buildnumbers! -#if 1 +#if 0 int m = 0, d = 0, y = 0; static int b = 0; @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 3847; + return 4140; #endif -} \ No newline at end of file +} diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 319a8394..8e9faff8 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -585,6 +585,7 @@ void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_ cmd->name = copystring( cmd_name ); cmd->desc = copystring( cmd_desc ); cmd->function = function; + cmd->flags = 0; // insert it at the right alphanumeric position for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next ); diff --git a/engine/common/common.h b/engine/common/common.h index d654ab1d..ae187ff6 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -805,7 +805,8 @@ float pfnTime( void ); ============================================================== */ -#define Z_Malloc( size ) Mem_Alloc( host.mempool, size ) +#define Z_Malloc( size ) Mem_Malloc( host.mempool, size ) +#define Z_Calloc( size ) Mem_Calloc( host.mempool, size ) #define Z_Realloc( ptr, size ) Mem_Realloc( host.mempool, ptr, size ) #define Z_Free( ptr ) if( ptr != NULL ) Mem_Free( ptr ) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 624f22c1..710667b1 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -102,7 +102,7 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) if( !ents && lumplen >= 10 ) { FS_Seek( f, lumpofs, SEEK_SET ); - ents = (char *)Mem_Alloc( host.mempool, lumplen + 1 ); + ents = (char *)Mem_Calloc( host.mempool, lumplen + 1 ); FS_Read( f, ents, lumplen ); } @@ -692,7 +692,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) return false; } - buffer = Mem_Alloc( host.mempool, t->numfilenames * 2 * sizeof( result )); + buffer = Mem_Calloc( host.mempool, t->numfilenames * 2 * sizeof( result )); for( i = 0; i < t->numfilenames; i++ ) { @@ -734,7 +734,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) if( !ents && lumplen >= 10 ) { FS_Seek( f, lumpofs, SEEK_SET ); - ents = Z_Malloc( lumplen + 1 ); + ents = Z_Calloc( lumplen + 1 ); FS_Read( f, ents, lumplen ); } diff --git a/engine/common/console.c b/engine/common/console.c index 796e7562..b0b0dee3 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -945,9 +945,9 @@ void Con_Init( void ) // init the console buffer con.bufsize = CON_TEXTSIZE; - con.buffer = (char *)Z_Malloc( con.bufsize ); + con.buffer = (char *)Z_Calloc( con.bufsize ); con.maxlines = CON_MAXLINES; - con.lines = (con_lineinfo_t *)Z_Malloc( con.maxlines * sizeof( *con.lines )); + con.lines = (con_lineinfo_t *)Z_Calloc( con.maxlines * sizeof( *con.lines )); con.lines_first = con.lines_count = 0; con.num_times = CON_TIMES; // default as 4 diff --git a/engine/common/crtlib.c b/engine/common/crtlib.c index c258bde3..0f01795d 100644 --- a/engine/common/crtlib.c +++ b/engine/common/crtlib.c @@ -183,7 +183,7 @@ char *_copystring( byte *mempool, const char *s, const char *filename, int filel if( !s ) return NULL; if( !mempool ) mempool = host.mempool; - b = _Mem_Alloc( mempool, Q_strlen( s ) + 1, filename, fileline ); + b = _Mem_Alloc( mempool, Q_strlen( s ) + 1, false, filename, fileline ); Q_strcpy( b, s ); return b; diff --git a/engine/common/crtlib.h b/engine/common/crtlib.h index f622010c..a01ee578 100644 --- a/engine/common/crtlib.h +++ b/engine/common/crtlib.h @@ -107,8 +107,8 @@ char *va( const char *format, ... ); // zone.c // void Memory_Init( void ); -void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, const char *filename, int fileline ); -void *_Mem_Alloc( byte *poolptr, size_t size, const char *filename, int fileline ); +void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline ); +void *_Mem_Alloc( byte *poolptr, size_t size, qboolean clear, const char *filename, int fileline ); byte *_Mem_AllocPool( const char *name, const char *filename, int fileline ); void _Mem_FreePool( byte **poolptr, const char *filename, int fileline ); void _Mem_EmptyPool( byte *poolptr, const char *filename, int fileline ); @@ -118,8 +118,9 @@ qboolean Mem_IsAllocatedExt( byte *poolptr, void *data ); void Mem_PrintList( size_t minallocationsize ); void Mem_PrintStats( void ); -#define Mem_Alloc( pool, size ) _Mem_Alloc( pool, size, __FILE__, __LINE__ ) -#define Mem_Realloc( pool, ptr, size ) _Mem_Realloc( pool, ptr, size, __FILE__, __LINE__ ) +#define Mem_Malloc( pool, size ) _Mem_Alloc( pool, size, false, __FILE__, __LINE__ ) +#define Mem_Calloc( pool, size ) _Mem_Alloc( pool, size, true, __FILE__, __LINE__ ) +#define Mem_Realloc( pool, ptr, size ) _Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ ) #define Mem_Free( mem ) _Mem_Free( mem, __FILE__, __LINE__ ) #define Mem_AllocPool( name ) _Mem_AllocPool( name, __FILE__, __LINE__ ) #define Mem_FreePool( pool ) _Mem_FreePool( pool, __FILE__, __LINE__ ) diff --git a/engine/common/custom.c b/engine/common/custom.c index 7b4b15c5..68f478d8 100644 --- a/engine/common/custom.c +++ b/engine/common/custom.c @@ -66,7 +66,7 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou if( pOut ) *pOut = NULL; - pCust = Z_Malloc( sizeof( customization_t )); + pCust = Z_Calloc( sizeof( customization_t )); pCust->resource = *pResource; if( pResource->nDownloadSize <= 0 ) diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index fdb6b1d2..bc84b86a 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -26,7 +26,7 @@ GNU General Public License for more details. #include "protocol.h" #define FILE_COPY_SIZE (1024 * 1024) -#define FILE_BUFF_SIZE (65535) +#define FILE_BUFF_SIZE (2048) // PAK errors #define PAK_LOAD_OK 0 @@ -218,7 +218,7 @@ static void stringlistappend( stringlist_t *list, char *text ) } textlen = Q_strlen( text ) + 1; - list->strings[list->numstrings] = Mem_Alloc( fs_mempool, textlen ); + list->strings[list->numstrings] = Mem_Calloc( fs_mempool, textlen ); memcpy( list->strings[list->numstrings], text, textlen ); list->numstrings++; } @@ -455,7 +455,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) return NULL; } - info = (dpackfile_t *)Mem_Alloc( fs_mempool, sizeof( *info ) * numpackfiles ); + info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles ); lseek( packhandle, header.dirofs, SEEK_SET ); if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) @@ -467,9 +467,9 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) return NULL; } - pack = (pack_t *)Mem_Alloc( fs_mempool, sizeof( pack_t )); + pack = (pack_t *)Mem_Calloc( fs_mempool, sizeof( pack_t )); Q_strncpy( pack->filename, packfile, sizeof( pack->filename )); - pack->files = (dpackfile_t *)Mem_Alloc( fs_mempool, numpackfiles * sizeof( dpackfile_t )); + pack->files = (dpackfile_t *)Mem_Calloc( fs_mempool, numpackfiles * sizeof( dpackfile_t )); pack->filetime = FS_SysFileTime( packfile ); pack->handle = packhandle; pack->numfiles = 0; @@ -511,7 +511,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade if( wad ) { - search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t )); + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); search->wad = wad; search->next = fs_searchpaths; search->flags |= flags; @@ -567,7 +567,7 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade { string fullpath; - search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t )); + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); search->pack = pak; search->next = fs_searchpaths; search->flags |= flags; @@ -644,7 +644,7 @@ void FS_AddGameDirectory( const char *dir, int flags ) // add the directory to the search path // (unpacked files have the priority over packed files) - search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t )); + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); Q_strncpy( search->filename, dir, sizeof ( search->filename )); search->next = fs_searchpaths; search->flags = flags; @@ -1352,64 +1352,60 @@ void FS_Init( void ) Cmd_AddCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); // ignore commandlineoption "-game" for other stuff - if( host.type == HOST_NORMAL || host.type == HOST_DEDICATED ) - { - stringlistinit( &dirs ); - listdirectory( &dirs, "./" ); - stringlistsort( &dirs ); - SI.numgames = 0; + stringlistinit( &dirs ); + listdirectory( &dirs, "./" ); + stringlistsort( &dirs ); + SI.numgames = 0; - Q_strncpy( fs_basedir, SI.basedirName, sizeof( fs_basedir )); // default dir + Q_strncpy( fs_basedir, SI.basedirName, sizeof( fs_basedir )); // default dir - if( !Sys_GetParmFromCmdLine( "-game", fs_gamedir )) - Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // gamedir == basedir + if( !Sys_GetParmFromCmdLine( "-game", fs_gamedir )) + Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // gamedir == basedir - if( FS_CheckNastyPath( fs_basedir, true )) - { - // this is completely fatal... - Sys_Error( "invalid base directory \"%s\"\n", fs_basedir ); - } + if( FS_CheckNastyPath( fs_basedir, true )) + { + // this is completely fatal... + Sys_Error( "invalid base directory \"%s\"\n", fs_basedir ); + } - if( FS_CheckNastyPath( fs_gamedir, true )) - { - Con_Printf( S_ERROR "invalid game directory \"%s\"\n", fs_gamedir ); - Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // default dir - } + if( FS_CheckNastyPath( fs_gamedir, true )) + { + Con_Printf( S_ERROR "invalid game directory \"%s\"\n", fs_gamedir ); + Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // default dir + } - // validate directories - for( i = 0; i < dirs.numstrings; i++ ) - { - if( !Q_stricmp( fs_basedir, dirs.strings[i] )) - hasBaseDir = true; + // validate directories + for( i = 0; i < dirs.numstrings; i++ ) + { + if( !Q_stricmp( fs_basedir, dirs.strings[i] )) + hasBaseDir = true; - if( !Q_stricmp( fs_gamedir, dirs.strings[i] )) - hasGameDir = true; - } + if( !Q_stricmp( fs_gamedir, dirs.strings[i] )) + hasGameDir = true; + } - if( !hasGameDir ) - { - Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir ); - if( hasBaseDir ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); - } + if( !hasGameDir ) + { + Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir ); + if( hasBaseDir ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); + } - // build list of game directories here - FS_AddGameDirectory( "./", 0 ); + // build list of game directories here + FS_AddGameDirectory( "./", 0 ); - for( i = 0; i < dirs.numstrings; i++ ) - { - if( !FS_SysFolderExists( dirs.strings[i] ) || ( !Q_stricmp( dirs.strings[i], ".." ) && !fs_ext_path )) - continue; + for( i = 0; i < dirs.numstrings; i++ ) + { + if( !FS_SysFolderExists( dirs.strings[i] ) || ( !Q_stricmp( dirs.strings[i], ".." ) && !fs_ext_path )) + continue; - if( SI.games[SI.numgames] == NULL ) - SI.games[SI.numgames] = (gameinfo_t *)Mem_Alloc( fs_mempool, sizeof( gameinfo_t )); + if( SI.games[SI.numgames] == NULL ) + SI.games[SI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t )); - if( FS_ParseGameInfo( dirs.strings[i], SI.games[SI.numgames] )) - SI.numgames++; // added - } - - stringlistfreecontents( &dirs ); - } + if( FS_ParseGameInfo( dirs.strings[i], SI.games[SI.numgames] )) + SI.numgames++; // added + } + stringlistfreecontents( &dirs ); Con_Reportf( "FS_Init: done\n" ); } @@ -1507,7 +1503,7 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) } } - file = (file_t *)Mem_Alloc( fs_mempool, sizeof( *file )); + file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); file->filetime = FS_SysFileTime( filepath ); file->ungetc = EOF; @@ -1551,7 +1547,7 @@ file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ) if( dup_handle < 0 ) return NULL; - file = (file_t *)Mem_Alloc( fs_mempool, sizeof( *file )); + file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); file->handle = dup_handle; file->real_length = pfile->filelen; file->offset = pfile->filepos; @@ -2003,7 +1999,7 @@ int FS_VPrintf( file_t *file, const char *format, va_list ap ) while( 1 ) { - tempbuff = (char *)Mem_Alloc( fs_mempool, buff_size ); + tempbuff = (char *)Mem_Malloc( fs_mempool, buff_size ); len = Q_vsprintf( tempbuff, format, ap ); if( len >= 0 && len < buff_size ) @@ -2191,7 +2187,7 @@ byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ) if( file ) { filesize = file->real_length; - buf = (byte *)Mem_Alloc( fs_mempool, filesize + 1 ); + buf = (byte *)Mem_Malloc( fs_mempool, filesize + 1 ); buf[filesize] = '\0'; FS_Read( file, buf, filesize ); FS_Close( file ); @@ -2349,7 +2345,7 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) } // all done, create dll_user_t struct - hInst = Mem_Alloc( host.mempool, sizeof( dll_user_t )); + hInst = Mem_Calloc( host.mempool, sizeof( dll_user_t )); // save dllname for debug purposes Q_strncpy( hInst->dllName, dllname, sizeof( hInst->dllName )); @@ -2499,7 +2495,7 @@ FS_FileCopy */ qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ) { - char *buf = Mem_Alloc( fs_mempool, FILE_COPY_SIZE ); + char *buf = Mem_Malloc( fs_mempool, FILE_COPY_SIZE ); int size, readSize; qboolean done = true; @@ -2557,7 +2553,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) separator = max( slash, backslash ); separator = max( separator, colon ); basepathlength = separator ? (separator + 1 - pattern) : 0; - basepath = Mem_Alloc( fs_mempool, basepathlength + 1 ); + basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); if( basepathlength ) memcpy( basepath, pattern, basepathlength ); basepath[basepathlength] = 0; @@ -2717,7 +2713,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ ) numchars += (int)Q_strlen( resultlist.strings[resultlistindex]) + 1; - search = Mem_Alloc( fs_mempool, sizeof(search_t) + numchars + numfiles * sizeof( char* )); + search = Mem_Calloc( fs_mempool, sizeof(search_t) + numchars + numfiles * sizeof( char* )); search->filenames = (char **)((char *)search + sizeof( search_t )); search->filenamesbuffer = (char *)((char *)search + sizeof( search_t ) + numfiles * sizeof( char* )); search->numfilenames = (int)numfiles; @@ -3013,7 +3009,7 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr ) return NULL; } - buf = (byte *)Mem_Alloc( wad->mempool, lump->disksize ); + buf = (byte *)Mem_Malloc( wad->mempool, lump->disksize ); size = FS_Read( wad->handle, buf, lump->disksize ); if( size < lump->disksize ) @@ -3046,7 +3042,7 @@ open the wad for reading & writing */ wfile_t *W_Open( const char *filename, int *error ) { - wfile_t *wad = (wfile_t *)Mem_Alloc( fs_mempool, sizeof( wfile_t )); + wfile_t *wad = (wfile_t *)Mem_Calloc( fs_mempool, sizeof( wfile_t )); int i, lumpcount; dlumpinfo_t *srclumps; size_t lat_size; @@ -3114,7 +3110,7 @@ wfile_t *W_Open( const char *filename, int *error ) lat_size = lumpcount * sizeof( dlumpinfo_t ); // NOTE: lumps table can be reallocated for O_APPEND mode - srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); + srclumps = (dlumpinfo_t *)Mem_Malloc( wad->mempool, lat_size ); if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size ) { @@ -3126,7 +3122,7 @@ wfile_t *W_Open( const char *filename, int *error ) } // starting to add lumps - wad->lumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); + wad->lumps = (dlumpinfo_t *)Mem_Calloc( wad->mempool, lat_size ); wad->numlumps = 0; // sort lumps for binary search diff --git a/engine/common/host.c b/engine/common/host.c index f3604972..4d7f45e8 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -236,7 +236,7 @@ void Host_Exec_f( void ) host.config_executed = true; // adds \n\0 at end of the file - txt = Z_Malloc( len + 2 ); + txt = Z_Calloc( len + 2 ); memcpy( txt, f, len ); Q_strncat( txt, "\n", len + 2 ); Mem_Free( f ); diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index 6524c90e..c4640367 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -136,7 +136,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) if( Image_CheckFlag( IL_KEEP_8BIT ) && bhdr.bitsPerPixel == 8 ) { - pixbuf = image.palette = Mem_Alloc( host.imagepool, 1024 ); + pixbuf = image.palette = Mem_Malloc( host.imagepool, 1024 ); // bmp have a reversed palette colors for( i = 0; i < bhdr.colors; i++ ) @@ -157,7 +157,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) buf_p += cbPalBytes; image.size = image.width * image.height * bpp; - image.rgba = Mem_Alloc( host.imagepool, image.size ); + image.rgba = Mem_Malloc( host.imagepool, image.size ); bps = image.width * (bhdr.bitsPerPixel >> 3); switch( bhdr.bitsPerPixel ) @@ -403,7 +403,7 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix ) FS_Write( pfile, &bmih, sizeof( bmih )); } - pbBmpBits = Mem_Alloc( host.imagepool, cbBmpBits ); + pbBmpBits = Mem_Malloc( host.imagepool, cbBmpBits ); if( pixel_size == 1 ) { diff --git a/engine/common/imagelib/img_dds.c b/engine/common/imagelib/img_dds.c index 79f5ed74..3b6c3b3a 100644 --- a/engine/common/imagelib/img_dds.c +++ b/engine/common/imagelib/img_dds.c @@ -322,7 +322,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) } // dds files will be uncompressed on a render. requires minimal of info for set this - image.rgba = Mem_Alloc( host.imagepool, image.size ); + image.rgba = Mem_Malloc( host.imagepool, image.size ); memcpy( image.rgba, fin, image.size ); image.flags |= IMAGE_DDS_FORMAT; diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 69c48f97..9fdf95d5 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -120,7 +120,7 @@ void Image_Reset( void ) rgbdata_t *ImagePack( void ) { - rgbdata_t *pack = Mem_Alloc( host.imagepool, sizeof( rgbdata_t )); + rgbdata_t *pack = Mem_Calloc( host.imagepool, sizeof( rgbdata_t )); // clear any force flags image.force_flags = 0; @@ -479,7 +479,7 @@ rgbdata_t *FS_CopyImage( rgbdata_t *in ) if( !in ) return NULL; - out = Mem_Alloc( host.imagepool, sizeof( rgbdata_t )); + out = Mem_Malloc( host.imagepool, sizeof( rgbdata_t )); *out = *in; switch( in->type ) @@ -494,13 +494,13 @@ rgbdata_t *FS_CopyImage( rgbdata_t *in ) if( palSize ) { - out->palette = Mem_Alloc( host.imagepool, palSize ); + out->palette = Mem_Malloc( host.imagepool, palSize ); memcpy( out->palette, in->palette, palSize ); } if( in->size ) { - out->buffer = Mem_Alloc( host.imagepool, in->size ); + out->buffer = Mem_Malloc( host.imagepool, in->size ); memcpy( out->buffer, in->buffer, in->size ); } diff --git a/engine/common/imagelib/img_quant.c b/engine/common/imagelib/img_quant.c index 2ad8f7df..c44569b4 100644 --- a/engine/common/imagelib/img_quant.c +++ b/engine/common/imagelib/img_quant.c @@ -447,7 +447,7 @@ rgbdata_t *Image_Quantize( rgbdata_t *pic ) learn(); unbiasnet(); - pic->palette = Mem_Alloc( host.imagepool, netsize * 3 ); + pic->palette = Mem_Malloc( host.imagepool, netsize * 3 ); for( i = 0; i < netsize; i++ ) { diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index ec5511b7..e336ef32 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -122,7 +122,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) rows = targa_header.height; image.size = image.width * image.height * 4; - targa_rgba = image.rgba = Mem_Alloc( host.imagepool, image.size ); + targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top if( !Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) @@ -235,7 +235,7 @@ qboolean Image_SaveTGA( const char *name, rgbdata_t *pix ) outsize = pix->width * pix->height * 4 + 18 + Q_strlen( comment ); else outsize = pix->width * pix->height * 3 + 18 + Q_strlen( comment ); - buffer = (byte *)Mem_Alloc( host.imagepool, outsize ); + buffer = (byte *)Mem_Calloc( host.imagepool, outsize ); // prepare header buffer[0] = Q_strlen( comment ); // tga comment length diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index a8a2a7ba..640e584a 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -194,7 +194,7 @@ byte *Image_Copy( size_t size ) { byte *out; - out = Mem_Alloc( host.imagepool, size ); + out = Mem_Malloc( host.imagepool, size ); memcpy( out, image.tempbuffer, size ); return out; @@ -284,9 +284,9 @@ int Image_ComparePalette( const byte *pal ) void Image_SetPalette( const byte *pal, uint *d_table ) { - int i; byte rgba[4]; - + int i; + // setup palette switch( image.d_rendermode ) { @@ -309,6 +309,7 @@ void Image_SetPalette( const byte *pal, uint *d_table ) rgba[3] = i; d_table[i] = *(uint *)rgba; } +// d_table[0] = 0x00808080; break; case LUMP_MASKED: for( i = 0; i < 255; i++ ) @@ -408,7 +409,7 @@ static void Image_ConvertPalTo24bit( rgbdata_t *pic ) if( pic->type == PF_INDEXED_24 ) return; // does nothing - pal24 = converted = Mem_Alloc( host.imagepool, 768 ); + pal24 = converted = Mem_Malloc( host.imagepool, 768 ); pal32 = pic->palette; for( i = 0; i < 256; i++, pal24 += 3, pal32 += 4 ) @@ -426,7 +427,7 @@ static void Image_ConvertPalTo24bit( rgbdata_t *pic ) void Image_CopyPalette32bit( void ) { if( image.palette ) return; // already created ? - image.palette = Mem_Alloc( host.imagepool, 1024 ); + image.palette = Mem_Malloc( host.imagepool, 1024 ); memcpy( image.palette, image.d_currentpal, 1024 ); } @@ -722,7 +723,7 @@ void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void * fstep = (int)(inheight * 65536.0f / outheight); - resamplerow1 = (byte *)Mem_Alloc( host.imagepool, outwidth * 4 * 2); + resamplerow1 = (byte *)Mem_Malloc( host.imagepool, outwidth * 4 * 2); resamplerow2 = resamplerow1 + outwidth * 4; inrow = (const byte *)indata; @@ -869,7 +870,7 @@ void Image_Resample24Lerp( const void *indata, int inwidth, int inheight, void * fstep = (int)(inheight * 65536.0f / outheight); - resamplerow1 = (byte *)Mem_Alloc( host.imagepool, outwidth * 3 * 2 ); + resamplerow1 = (byte *)Mem_Malloc( host.imagepool, outwidth * 3 * 2 ); resamplerow2 = resamplerow1 + outwidth*3; inrow = (const byte *)indata; @@ -1206,7 +1207,7 @@ qboolean Image_AddIndexedImageToPack( const byte *in, int width, int height ) else Image_CopyPalette32bit(); // reallocate image buffer - image.rgba = Mem_Alloc( host.imagepool, image.size ); + image.rgba = Mem_Malloc( host.imagepool, image.size ); if( !expand_to_rgba ) memcpy( image.rgba, in, image.size ); else if( !Image_Copy8bitRGBA( in, image.rgba, mipsize )) return false; // probably pallette not installed diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 95896b4e..e64c304b 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -241,7 +241,7 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, size_t filesize ) { // spr32 support image.size = image.width * image.height * 4; - image.rgba = Mem_Alloc( host.imagepool, image.size ); + image.rgba = Mem_Malloc( host.imagepool, image.size ); memcpy( image.rgba, (byte *)(pin + 1), image.size ); SetBits( image.flags, IMAGE_HAS_COLOR ); // Color. True Color! return true; diff --git a/engine/common/input.h b/engine/common/input.h index ea7e53e9..ae582c8a 100644 --- a/engine/common/input.h +++ b/engine/common/input.h @@ -26,8 +26,11 @@ INPUT #include "keydefs.h" -#define WHEEL_DELTA 120 // Default value for rolling one notch +#ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL ( WM_MOUSELAST + 1 )// message that will be supported by the OS +#endif + +#define WHEEL_DELTA 120 // Default value for rolling one notch #define MK_XBUTTON1 0x0020 #define MK_XBUTTON2 0x0040 #define MK_XBUTTON3 0x0080 @@ -36,6 +39,7 @@ INPUT #define WM_XBUTTONUP 0x020C #define WM_XBUTTONDOWN 0x020B + // // input.c // diff --git a/engine/common/library.c b/engine/common/library.c index 80bc2e02..67d67fad 100644 --- a/engine/common/library.c +++ b/engine/common/library.c @@ -671,7 +671,7 @@ qboolean LibraryLoadSymbols( dll_user_t *hInst ) goto table_error; } - hInst->ordinals = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( word )); + hInst->ordinals = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( word )); if( FS_Read( f, hInst->ordinals, hInst->num_ordinals * sizeof( word )) != (hInst->num_ordinals * sizeof( word ))) { @@ -687,7 +687,7 @@ qboolean LibraryLoadSymbols( dll_user_t *hInst ) goto table_error; } - hInst->funcs = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( dword )); + hInst->funcs = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( dword )); if( FS_Read( f, hInst->funcs, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword ))) { @@ -703,7 +703,7 @@ qboolean LibraryLoadSymbols( dll_user_t *hInst ) goto table_error; } - p_Names = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( dword )); + p_Names = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( dword )); if( FS_Read( f, p_Names, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword ))) { diff --git a/engine/common/library.h b/engine/common/library.h index c6eec583..7c72d5ac 100644 --- a/engine/common/library.h +++ b/engine/common/library.h @@ -21,6 +21,10 @@ GNU General Public License for more details. #define NUMBER_OF_DIRECTORY_ENTRIES 16 #define MAX_LIBRARY_EXPORTS 4096 +#ifndef IMAGE_SIZEOF_BASE_RELOCATION +#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION )) +#endif + typedef struct { // dos .exe header diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 09a09f31..598eadb5 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -41,6 +41,8 @@ GNU General Public License for more details. #define NUMVERTEXNORMALS 162 +#define BOGUS_RANGE ((vec_t)114032.64) // world.size * 1.74 + #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 5780075e..782ebacd 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -362,7 +362,7 @@ static int Mod_ArrayUsage( const char *szItem, int items, int maxitems, int item Con_Printf( "%-12s %7i/%-7i %8i/%-8i (%4.1f%%) ", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); - if( percentage > 99.9f ) + if( percentage > 99.99f ) Con_Printf( "^1SIZE OVERFLOW!!!^7\n" ); else if( percentage > 95.0f ) Con_Printf( "^3SIZE DANGER!^7\n" ); @@ -384,7 +384,7 @@ static int Mod_GlobUsage( const char *szItem, int itemstorage, int maxstorage ) Con_Printf( "%-15s %-12s %8i/%-8i (%4.1f%%) ", szItem, "[variable]", itemstorage, maxstorage, percentage ); - if( percentage > 99.9f ) + if( percentage > 99.99f ) Con_Printf( "^1SIZE OVERFLOW!!!^7\n" ); else if( percentage > 95.0f ) Con_Printf( "^3SIZE DANGER!^7\n" ); @@ -1162,7 +1162,7 @@ static void Mod_MakeHull0( void ) int i, j; hull = &loadmodel->hulls[0]; - hull->clipnodes = out = Mem_Alloc( loadmodel->mempool, loadmodel->numnodes * sizeof( *out )); + hull->clipnodes = out = Mem_Malloc( loadmodel->mempool, loadmodel->numnodes * sizeof( *out )); in = loadmodel->nodes; hull->firstclipnode = 0; @@ -1230,7 +1230,7 @@ static void Mod_SetupHull( dbspmodel_t *bmod, model_t *mod, byte *mempool, int h count = hull->lastclipnode; // fit array to real count - hull->clipnodes = (mclipnode_t *)Mem_Alloc( mempool, sizeof( mclipnode_t ) * hull->lastclipnode ); + hull->clipnodes = (mclipnode_t *)Mem_Malloc( mempool, sizeof( mclipnode_t ) * hull->lastclipnode ); hull->planes = mod->planes; // share planes hull->lastclipnode = 0; // restart counting @@ -1281,7 +1281,7 @@ static qboolean Mod_LoadColoredLighting( dbspmodel_t *bmod ) return false; } - loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, litdatasize ); + loadmodel->lightdata = Mem_Malloc( loadmodel->mempool, litdatasize ); memcpy( loadmodel->lightdata, in + 8, litdatasize ); SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); bmod->lightdatasize = litdatasize; @@ -1336,7 +1336,7 @@ static void Mod_LoadDeluxemap( dbspmodel_t *bmod ) return; } - bmod->deluxedata_out = Mem_Alloc( loadmodel->mempool, deluxdatasize ); + bmod->deluxedata_out = Mem_Malloc( loadmodel->mempool, deluxdatasize ); memcpy( bmod->deluxedata_out, in + 8, deluxdatasize ); bmod->deluxdatasize = deluxdatasize; Mem_Free( in ); @@ -1435,7 +1435,8 @@ static void Mod_SetupSubmodels( dbspmodel_t *bmod ) } } - Mem_Free( bmod->clipnodes_out ); + if( bmod->clipnodes_out != NULL ) + Mem_Free( bmod->clipnodes_out ); } /* @@ -1457,7 +1458,7 @@ static void Mod_LoadSubmodels( dbspmodel_t *bmod ) int i, j; // allocate extradata for each dmodel_t - out = Mem_Alloc( loadmodel->mempool, bmod->numsubmodels * sizeof( *out )); + out = Mem_Malloc( loadmodel->mempool, bmod->numsubmodels * sizeof( *out )); loadmodel->numsubmodels = bmod->numsubmodels; loadmodel->submodels = out; @@ -1540,7 +1541,7 @@ static void Mod_LoadEntities( dbspmodel_t *bmod ) } // make sure what we really has terminator - loadmodel->entities = Mem_Alloc( loadmodel->mempool, bmod->entdatasize + 1 ); + loadmodel->entities = Mem_Calloc( loadmodel->mempool, bmod->entdatasize + 1 ); memcpy( loadmodel->entities, bmod->entdata, bmod->entdatasize ); // moving to private model pool if( entpatch ) Mem_Free( entpatch ); // release entpatch if present if( !bmod->isworld ) return; @@ -1623,11 +1624,12 @@ static void Mod_LoadPlanes( dbspmodel_t *bmod ) int i, j; in = bmod->planes; - loadmodel->planes = out = Mem_Alloc( loadmodel->mempool, bmod->numplanes * sizeof( *out )); + loadmodel->planes = out = Mem_Malloc( loadmodel->mempool, bmod->numplanes * sizeof( *out )); loadmodel->numplanes = bmod->numplanes; for( i = 0; i < bmod->numplanes; i++, in++, out++ ) { + out->signbits = 0; for( j = 0; j < 3; j++ ) { out->normal[j] = in->normal[j]; @@ -1656,7 +1658,7 @@ static void Mod_LoadVertexes( dbspmodel_t *bmod ) int i; in = bmod->vertexes; - out = loadmodel->vertexes = Mem_Alloc( loadmodel->mempool, bmod->numvertexes * sizeof( mvertex_t )); + out = loadmodel->vertexes = Mem_Malloc( loadmodel->mempool, bmod->numvertexes * sizeof( mvertex_t )); loadmodel->numvertexes = bmod->numvertexes; if( bmod->isworld ) ClearBounds( world.mins, world.maxs ); @@ -1690,7 +1692,7 @@ static void Mod_LoadEdges( dbspmodel_t *bmod ) medge_t *out; int i; - loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, bmod->numedges * sizeof( medge_t )); + loadmodel->edges = out = Mem_Malloc( loadmodel->mempool, bmod->numedges * sizeof( medge_t )); loadmodel->numedges = bmod->numedges; if( bmod->version == QBSP2_VERSION ) @@ -1722,7 +1724,7 @@ Mod_LoadSurfEdges */ static void Mod_LoadSurfEdges( dbspmodel_t *bmod ) { - loadmodel->surfedges = Mem_Alloc( loadmodel->mempool, bmod->numsurfedges * sizeof( dsurfedge_t )); + loadmodel->surfedges = Mem_Malloc( loadmodel->mempool, bmod->numsurfedges * sizeof( dsurfedge_t )); memcpy( loadmodel->surfedges, bmod->surfedges, bmod->numsurfedges * sizeof( dsurfedge_t )); loadmodel->numsurfedges = bmod->numsurfedges; } @@ -1737,7 +1739,7 @@ static void Mod_LoadMarkSurfaces( dbspmodel_t *bmod ) msurface_t **out; int i; - loadmodel->marksurfaces = out = Mem_Alloc( loadmodel->mempool, bmod->nummarkfaces * sizeof( *out )); + loadmodel->marksurfaces = out = Mem_Malloc( loadmodel->mempool, bmod->nummarkfaces * sizeof( *out )); loadmodel->nummarksurfaces = bmod->nummarkfaces; if( bmod->version == QBSP2_VERSION ) @@ -1799,7 +1801,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) } in = bmod->textures; - loadmodel->textures = (texture_t **)Mem_Alloc( loadmodel->mempool, in->nummiptex * sizeof( texture_t* )); + loadmodel->textures = (texture_t **)Mem_Calloc( loadmodel->mempool, in->nummiptex * sizeof( texture_t* )); loadmodel->numtextures = in->nummiptex; for( i = 0; i < loadmodel->numtextures; i++ ) @@ -1807,7 +1809,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( in->dataofs[i] == -1 ) { // create default texture (some mods requires this) - tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); + tx = Mem_Calloc( loadmodel->mempool, sizeof( *tx )); loadmodel->textures[i] = tx; Q_strncpy( tx->name, "*default", sizeof( tx->name )); @@ -1824,7 +1826,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) Q_snprintf( mt->name, sizeof( mt->name ), "miptex_%i", i ); } - tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); + tx = Mem_Calloc( loadmodel->mempool, sizeof( *tx )); loadmodel->textures[i] = tx; // convert to lowercase @@ -2067,7 +2069,7 @@ static void Mod_LoadTexInfo( dbspmodel_t *bmod ) dtexinfo_t *in; // trying to load faceinfo - faceinfo = fout = Mem_Alloc( loadmodel->mempool, bmod->numfaceinfo * sizeof( *fout )); + faceinfo = fout = Mem_Calloc( loadmodel->mempool, bmod->numfaceinfo * sizeof( *fout )); fin = bmod->faceinfo; for( i = 0; i < bmod->numfaceinfo; i++, fin++, fout++ ) @@ -2078,7 +2080,7 @@ static void Mod_LoadTexInfo( dbspmodel_t *bmod ) fout->groupid = fin->groupid; } - loadmodel->texinfo = out = Mem_Alloc( loadmodel->mempool, bmod->numtexinfo * sizeof( *out )); + loadmodel->texinfo = out = Mem_Calloc( loadmodel->mempool, bmod->numtexinfo * sizeof( *out )); loadmodel->numtexinfo = bmod->numtexinfo; in = bmod->texinfo; @@ -2117,8 +2119,8 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) mextrasurf_t *info; msurface_t *out; - loadmodel->surfaces = out = Mem_Alloc( loadmodel->mempool, bmod->numsurfaces * sizeof( msurface_t )); - info = Mem_Alloc( loadmodel->mempool, bmod->numsurfaces * sizeof( mextrasurf_t )); + loadmodel->surfaces = out = Mem_Calloc( loadmodel->mempool, bmod->numsurfaces * sizeof( msurface_t )); + info = Mem_Calloc( loadmodel->mempool, bmod->numsurfaces * sizeof( mextrasurf_t )); loadmodel->numsurfaces = bmod->numsurfaces; // predict samplecount based on bspversion @@ -2265,7 +2267,7 @@ static void Mod_LoadNodes( dbspmodel_t *bmod ) mnode_t *out; int i, j, p; - loadmodel->nodes = out = (mnode_t *)Mem_Alloc( loadmodel->mempool, bmod->numnodes * sizeof( *out )); + loadmodel->nodes = out = (mnode_t *)Mem_Calloc( loadmodel->mempool, bmod->numnodes * sizeof( *out )); loadmodel->numnodes = bmod->numnodes; for( i = 0; i < loadmodel->numnodes; i++, out++ ) @@ -2330,7 +2332,7 @@ static void Mod_LoadLeafs( dbspmodel_t *bmod ) mleaf_t *out; int i, j, p; - loadmodel->leafs = out = (mleaf_t *)Mem_Alloc( loadmodel->mempool, bmod->numleafs * sizeof( *out )); + loadmodel->leafs = out = (mleaf_t *)Mem_Calloc( loadmodel->mempool, bmod->numleafs * sizeof( *out )); loadmodel->numleafs = bmod->numleafs; if( bmod->isworld ) @@ -2338,7 +2340,7 @@ static void Mod_LoadLeafs( dbspmodel_t *bmod ) // get visleafs from the submodel data world.visclusters = loadmodel->submodels[0].visleafs; world.visbytes = (world.visclusters + 7) >> 3; - world.visdata = (byte *)Mem_Alloc( loadmodel->mempool, world.visclusters * world.visbytes ); + world.visdata = (byte *)Mem_Malloc( loadmodel->mempool, world.visclusters * world.visbytes ); world.fatbytes = (world.visclusters + 31) >> 3; // enable full visibility as default @@ -2442,7 +2444,7 @@ static void Mod_LoadClipnodes( dbspmodel_t *bmod ) dclipnode32_t *out; int i; - bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Alloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out )); + bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Malloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out )); if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES )) { @@ -2485,7 +2487,7 @@ Mod_LoadVisibility */ static void Mod_LoadVisibility( dbspmodel_t *bmod ) { - loadmodel->visdata = Mem_Alloc( loadmodel->mempool, bmod->visdatasize ); + loadmodel->visdata = Mem_Malloc( loadmodel->mempool, bmod->visdatasize ); memcpy( loadmodel->visdata, bmod->visdata, bmod->visdatasize ); } @@ -2504,7 +2506,7 @@ static void Mod_LoadLightVecs( dbspmodel_t *bmod ) return; } - bmod->deluxedata_out = Mem_Alloc( loadmodel->mempool, bmod->deluxdatasize ); + bmod->deluxedata_out = Mem_Malloc( loadmodel->mempool, bmod->deluxdatasize ); memcpy( bmod->deluxedata_out, bmod->deluxdata, bmod->deluxdatasize ); } @@ -2522,7 +2524,7 @@ static void Mod_LoadShadowmap( dbspmodel_t *bmod ) return; } - bmod->shadowdata_out = Mem_Alloc( loadmodel->mempool, bmod->shadowdatasize ); + bmod->shadowdata_out = Mem_Malloc( loadmodel->mempool, bmod->shadowdatasize ); memcpy( bmod->shadowdata_out, bmod->shadowdata, bmod->shadowdatasize ); } @@ -2546,7 +2548,7 @@ static void Mod_LoadLighting( dbspmodel_t *bmod ) case 1: if( !Mod_LoadColoredLighting( bmod )) { - loadmodel->lightdata = out = (color24 *)Mem_Alloc( loadmodel->mempool, bmod->lightdatasize * sizeof( color24 )); + loadmodel->lightdata = out = (color24 *)Mem_Malloc( loadmodel->mempool, bmod->lightdatasize * sizeof( color24 )); in = bmod->lightdata; // expand the white lighting data @@ -2555,7 +2557,7 @@ static void Mod_LoadLighting( dbspmodel_t *bmod ) } break; case 3: // load colored lighting - loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, bmod->lightdatasize ); + loadmodel->lightdata = Mem_Malloc( loadmodel->mempool, bmod->lightdatasize ); memcpy( loadmodel->lightdata, bmod->lightdata, bmod->lightdatasize ); SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); break; @@ -2614,6 +2616,7 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) dheader_t *header = (dheader_t *)mod_base; dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)mod_base + sizeof( dheader_t )); dbspmodel_t *bmod = &srcmodel; + model_t *mod = loadmodel; char wadvalue[2048]; int i; @@ -2684,6 +2687,12 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) Mod_MakeHull0 (); Mod_SetupSubmodels( bmod ); + if( isworld ) + { + loadmodel = mod; // restore pointer to world + Mod_InitDebugHulls(); // FIXME: build hulls for separate bmodels (shells, medkits etc) + } + for( i = 0; i < bmod->wadlist.count; i++ ) Q_strncat( wadvalue, va( "%s.wad; ", bmod->wadlist.wadnames[i] ), sizeof( wadvalue )); diff --git a/engine/common/mod_dbghulls.c b/engine/common/mod_dbghulls.c new file mode 100644 index 00000000..2c57b3f5 --- /dev/null +++ b/engine/common/mod_dbghulls.c @@ -0,0 +1,777 @@ +/* +mod_bmodel.c - loading & handling world and brushmodels +Copyright (C) 2016 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "mod_local.h" +#include "mathlib.h" +#include "world.h" +#include "gl_local.h" +#include "client.h" + +#define MAX_CLIPNODE_DEPTH 256 // should never exceeds + +#define list_entry( ptr, type, member ) \ + ((type *)((char *)(ptr) - (size_t)(&((type *)0)->member))) + +// iterate over each entry in the list +#define list_for_each_entry( pos, head, member ) \ + for( pos = list_entry( (head)->next, winding_t, member ); \ + &pos->member != (head); \ + pos = list_entry( pos->member.next, winding_t, member )) + +// iterate over the list, safe for removal of entries +#define list_for_each_entry_safe( pos, n, head, member ) \ + for( pos = list_entry( (head)->next, winding_t, member ), \ + n = list_entry( pos->member.next, winding_t, member ); \ + &pos->member != (head); \ + pos = n, n = list_entry( n->member.next, winding_t, member )) + +#define LIST_HEAD_INIT( name ) { &(name), &(name) } + +static _inline void list_add__( hullnode_t *new, hullnode_t *prev, hullnode_t *next ) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +// add the new entry after the give list entry +static _inline void list_add( hullnode_t *newobj, hullnode_t *head ) +{ + list_add__( newobj, head, head->next ); +} + +// add the new entry before the given list entry (list is circular) +static _inline void list_add_tail( hullnode_t *newobj, hullnode_t *head ) +{ + list_add__( newobj, head->prev, head ); +} + +static _inline void list_del( hullnode_t *entry ) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +static winding_t * winding_alloc( uint numpoints ) +{ + return (winding_t *)malloc( (int)((winding_t *)0)->p[numpoints] ); +} + +static void free_winding( winding_t *w ) +{ + // simple sentinel by Carmack + if( *(unsigned *)w == 0xDEADC0DE ) + Host_Error( "free_winding: freed a freed winding\n" ); + *(unsigned *)w = 0xDEADC0DE; + free( w ); +} + +static winding_t *winding_copy( winding_t *w ) +{ + winding_t *neww; + + neww = winding_alloc( w->numpoints ); + memcpy( neww, w, (int)((winding_t *)0)->p[w->numpoints] ); + + return neww; +} + +static void winding_reverse( winding_t *w ) +{ + vec3_t point; + int i; + + for( i = 0; i < w->numpoints / 2; i++ ) + { + VectorCopy( w->p[i], point ); + VectorCopy( w->p[w->numpoints - i - 1], w->p[i] ); + VectorCopy( point, w->p[w->numpoints - i - 1] ); + } +} + +/* + * winding_shrink + * + * Takes an over-allocated winding and allocates a new winding with just the + * required number of points. The input winding is freed. + */ +static winding_t *winding_shrink( winding_t *w ) +{ + winding_t *neww = winding_alloc( w->numpoints ); + memcpy( neww, w, (int)((winding_t *)0)->p[w->numpoints] ); + free_winding( w ); + + return neww; +} + +/* +==================== +winding_for_plane +==================== +*/ +static winding_t *winding_for_plane( const mplane_t *p ) +{ + vec3_t org, vright, vup; + int i, axis; + vec_t max, v; + winding_t *w; + + // find the major axis + max = -BOGUS_RANGE; + axis = -1; + + for( i = 0; i < 3; i++ ) + { + v = fabs( p->normal[i] ); + if( v > max ) + { + axis = i; + max = v; + } + } + + VectorClear( vup ); + switch( axis ) + { + case 0: + case 1: + vup[2] = 1; + break; + case 2: + vup[0] = 1; + break; + default: + Host_Error( "BaseWindingForPlane: no axis found\n" ); + return NULL; + } + + v = DotProduct( vup, p->normal ); + VectorMA( vup, -v, p->normal, vup ); + VectorNormalize( vup ); + VectorScale( p->normal, p->dist, org ); + CrossProduct( vup, p->normal, vright ); + VectorScale( vup, BOGUS_RANGE, vup ); + VectorScale( vright, BOGUS_RANGE, vright ); + + // project a really big axis aligned box onto the plane + w = winding_alloc( 4 ); + memset( w->p, 0, sizeof( vec3_t ) * 4 ); + w->numpoints = 4; + w->plane = p; + + VectorSubtract( org, vright, w->p[0] ); + VectorAdd( w->p[0], vup, w->p[0] ); + VectorAdd( org, vright, w->p[1] ); + VectorAdd( w->p[1], vup, w->p[1] ); + VectorAdd( org, vright, w->p[2] ); + VectorSubtract( w->p[2], vup, w->p[2] ); + VectorSubtract( org, vright, w->p[3] ); + VectorSubtract( w->p[3], vup, w->p[3] ); + + return w; +} + +/* + * =========================== + * Helper for for the clipping functions + * (winding_clip, winding_split) + * =========================== + */ +static void CalcSides( const winding_t *in, const mplane_t *split, int *sides, vec_t *dists, int counts[3], vec_t epsilon ) +{ + const vec_t *p; + int i; + + counts[0] = counts[1] = counts[2] = 0; + + switch( split->type ) + { + case PLANE_X: + case PLANE_Y: + case PLANE_Z: + p = in->p[0] + split->type; + for( i = 0; i < in->numpoints; i++, p += 3 ) + { + const vec_t dot = *p - split->dist; + + dists[i] = dot; + if( dot > epsilon ) + sides[i] = SIDE_FRONT; + else if( dot < -epsilon ) + sides[i] = SIDE_BACK; + else sides[i] = SIDE_ON; + counts[sides[i]]++; + } + break; + default: + p = in->p[0]; + for( i = 0; i < in->numpoints; i++, p += 3 ) + { + const vec_t dot = DotProduct( split->normal, p ) - split->dist; + + dists[i] = dot; + if( dot > epsilon ) + sides[i] = SIDE_FRONT; + else if( dot < -epsilon ) + sides[i] = SIDE_BACK; + else sides[i] = SIDE_ON; + counts[sides[i]]++; + } + break; + } + + sides[i] = sides[0]; + dists[i] = dists[0]; +} + +static void PushToPlaneAxis( vec_t *v, const mplane_t *p ) +{ + const int t = p->type % 3; + + v[t] = (p->dist - p->normal[(t + 1) % 3] * v[(t + 1) % 3] - p->normal[(t + 2) % 3] * v[(t + 2) % 3]) / p->normal[t]; +} + +/* +================== +winding_clip + +Clips the winding to the plane, returning the new winding on 'side'. +Frees the input winding. +If keepon is true, an exactly on-plane winding will be saved, otherwise + it will be clipped away. +================== +*/ +static winding_t *winding_clip( winding_t *in, const mplane_t *split, qboolean keepon, int side, vec_t epsilon ) +{ + vec_t *dists; + int *sides; + int counts[3]; + vec_t dot; + int i, j; + winding_t *neww; + vec_t *p1, *p2, *mid; + int maxpts; + + dists = (vec_t *)malloc(( in->numpoints + 1 ) * sizeof( vec_t )); + sides = (int *)malloc(( in->numpoints + 1 ) * sizeof( int )); + CalcSides( in, split, sides, dists, counts, epsilon ); + + if( keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK] ) + { + neww = in; + goto out_free; + } + + if( !counts[side] ) + { + free_winding( in ); + neww = NULL; + goto out_free; + } + + if( !counts[side ^ 1] ) + { + neww = in; + goto out_free; + } + + maxpts = in->numpoints + 4; + neww = winding_alloc( maxpts ); + neww->numpoints = 0; + neww->plane = in->plane; + + for( i = 0; i < in->numpoints; i++ ) + { + p1 = in->p[i]; + + if( sides[i] == SIDE_ON ) + { + VectorCopy( p1, neww->p[neww->numpoints] ); + neww->numpoints++; + continue; + } + + if( sides[i] == side ) + { + VectorCopy( p1, neww->p[neww->numpoints] ); + neww->numpoints++; + } + + if( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) + continue; + + // generate a split point + p2 = in->p[(i + 1) % in->numpoints]; + mid = neww->p[neww->numpoints++]; + + dot = dists[i] / (dists[i] - dists[i + 1]); + for( j = 0; j < 3; j++ ) + { + // avoid round off error when possible + if( in->plane->normal[j] == 1.0 ) + mid[j] = in->plane->dist; + else if( in->plane->normal[j] == -1.0 ) + mid[j] = -in->plane->dist; + else if( split->normal[j] == 1.0 ) + mid[j] = split->dist; + else if( split->normal[j] == -1.0 ) + mid[j] = -split->dist; + else mid[j] = p1[j] + dot * (p2[j] - p1[j]); + } + + if( in->plane->type < 3 ) + PushToPlaneAxis( mid, in->plane ); + } + + // free the original winding + free_winding( in ); + + // Shrink the winding back to just what it needs... + neww = winding_shrink(neww); +out_free: + free( dists ); + free( sides ); + + return neww; +} + +/* +================== +winding_split + +Splits a winding by a plane, producing one or two windings. The +original winding is not damaged or freed. If only on one side, the +returned winding will be the input winding. If on both sides, two +new windings will be created. +================== +*/ +static void winding_split( winding_t *in, const mplane_t *split, winding_t **pfront, winding_t **pback ) +{ + vec_t *dists; + int *sides; + int counts[3]; + vec_t dot; + int i, j; + winding_t *front, *back; + vec_t *p1, *p2, *mid; + int maxpts; + + dists = (vec_t *)malloc(( in->numpoints + 1 ) * sizeof( vec_t )); + sides = (int *)malloc(( in->numpoints + 1 ) * sizeof( int )); + CalcSides(in, split, sides, dists, counts, 0.04f ); + + if( !counts[0] && !counts[1] ) + { + // winding on the split plane - return copies on both sides + *pfront = winding_copy( in ); + *pback = winding_copy( in ); + goto out_free; + } + + if( !counts[0] ) + { + *pfront = NULL; + *pback = in; + goto out_free; + } + + if( !counts[1] ) + { + *pfront = in; + *pback = NULL; + goto out_free; + } + + maxpts = in->numpoints + 4; + front = winding_alloc( maxpts ); + front->numpoints = 0; + front->plane = in->plane; + back = winding_alloc( maxpts ); + back->numpoints = 0; + back->plane = in->plane; + + for( i = 0; i < in->numpoints; i++ ) + { + p1 = in->p[i]; + + if( sides[i] == SIDE_ON ) + { + VectorCopy( p1, front->p[front->numpoints] ); + VectorCopy( p1, back->p[back->numpoints] ); + front->numpoints++; + back->numpoints++; + continue; + } + + if( sides[i] == SIDE_FRONT ) + { + VectorCopy( p1, front->p[front->numpoints] ); + front->numpoints++; + } + else if( sides[i] == SIDE_BACK ) + { + VectorCopy( p1, back->p[back->numpoints] ); + back->numpoints++; + } + + if( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) + continue; + + // generate a split point + p2 = in->p[(i + 1) % in->numpoints]; + mid = front->p[front->numpoints++]; + + dot = dists[i] / (dists[i] - dists[i + 1]); + for( j = 0; j < 3; j++ ) + { + // avoid round off error when possible + if( in->plane->normal[j] == 1.0 ) + mid[j] = in->plane->dist; + else if( in->plane->normal[j] == -1.0 ) + mid[j] = -in->plane->dist; + else if( split->normal[j] == 1.0 ) + mid[j] = split->dist; + else if( split->normal[j] == -1.0 ) + mid[j] = -split->dist; + else mid[j] = p1[j] + dot * (p2[j] - p1[j]); + } + + if( in->plane->type < 3 ) + PushToPlaneAxis( mid, in->plane ); + VectorCopy( mid, back->p[back->numpoints] ); + back->numpoints++; + } + + *pfront = winding_shrink( front ); + *pback = winding_shrink( back ); +out_free: + free( dists ); + free( sides ); +} + +/* ------------------------------------------------------------------------- */ + +/* + * This is a stack of the clipnodes we have traversed + * "sides" indicates which side we went down each time + */ +static mclipnode_t *node_stack[MAX_CLIPNODE_DEPTH]; +static int side_stack[MAX_CLIPNODE_DEPTH]; +static uint node_stack_depth; + +static void push_node( mclipnode_t *node, int side ) +{ + if( node_stack_depth == MAX_CLIPNODE_DEPTH ) + Host_Error( "node stack overflow\n" ); + + node_stack[node_stack_depth] = node; + side_stack[node_stack_depth] = side; + node_stack_depth++; +} + +static void pop_node( void ) +{ + if( !node_stack_depth ) + Host_Error( "node stack underflow\n" ); + node_stack_depth--; +} + +static void free_hull_polys( hullnode_t *hull_polys ) +{ + winding_t *w, *next; + + list_for_each_entry_safe( w, next, hull_polys, chain ) + { + list_del( &w->chain ); + free_winding( w ); + } +} + +static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys, hull_model_t *model ); + +static void do_hull_recursion( hull_t *hull, mclipnode_t *node, int side, hullnode_t *polys, hull_model_t *model ) +{ + winding_t *w, *next; + + if( node->children[side] >= 0 ) + { + mclipnode_t *child = hull->clipnodes + node->children[side]; + push_node( node, side ); + hull_windings_r( hull, child, polys, model ); + pop_node(); + } + else + { + switch( node->children[side] ) + { + case CONTENTS_EMPTY: + case CONTENTS_WATER: + case CONTENTS_SLIME: + case CONTENTS_LAVA: + list_for_each_entry_safe( w, next, polys, chain ) + { + list_del( &w->chain ); + list_add( &w->chain, &model->polys ); + } + break; + case CONTENTS_SOLID: + case CONTENTS_SKY: + // throw away polys... + list_for_each_entry_safe( w, next, polys, chain ) + { + if( w->pair ) + w->pair->pair = NULL; + list_del( &w->chain ); + free_winding( w ); + model->num_polys--; + } + break; + default: + Host_Error( "bad contents: %i\n", node->children[side] ); + break; + } + } +} + +static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys, hull_model_t *model ) +{ + mplane_t *plane = hull->planes + node->planenum; + hullnode_t frontlist = LIST_HEAD_INIT( frontlist ); + hullnode_t backlist = LIST_HEAD_INIT( backlist ); + winding_t *w, *next, *front, *back; + int i; + + list_for_each_entry_safe( w, next, polys, chain ) + { + // PARANIOA - PAIR CHECK + ASSERT( !w->pair || w->pair->pair == w ); + + list_del( &w->chain ); + winding_split( w, plane, &front, &back ); + if( front ) list_add( &front->chain, &frontlist ); + if( back ) list_add( &back->chain, &backlist ); + + if( front && back ) + { + if( w->pair ) + { + // split the paired poly, preserve pairing + winding_t *front2, *back2; + + winding_split( w->pair, plane, &front2, &back2 ); + + front2->pair = front; + front->pair = front2; + back2->pair = back; + back->pair = back2; + + list_add( &front2->chain, &w->pair->chain ); + list_add( &back2->chain, &w->pair->chain ); + list_del( &w->pair->chain ); + free_winding( w->pair ); + model->num_polys++; + } + else + { + front->pair = NULL; + back->pair = NULL; + } + + model->num_polys++; + free_winding( w ); + } + } + + w = winding_for_plane(plane); + + for( i = 0; w && i < node_stack_depth; i++ ) + { + mplane_t *p = hull->planes + node_stack[i]->planenum; + w = winding_clip( w, p, false, side_stack[i], 0.00001 ); + } + + if( w ) + { + winding_t *tmp = winding_copy( w ); + winding_reverse( tmp ); + + w->pair = tmp; + tmp->pair = w; + + list_add( &w->chain, &frontlist ); + list_add( &tmp->chain, &backlist ); + + // PARANIOA - PAIR CHECK + ASSERT( !w->pair || w->pair->pair == w ); + model->num_polys += 2; + } + else + { + Con_Printf( S_WARN "new winding was clipped away!\n" ); + } + + do_hull_recursion( hull, node, 0, &frontlist, model ); + do_hull_recursion( hull, node, 1, &backlist, model ); +} + +static void remove_paired_polys( hull_model_t *model ) +{ + winding_t *w, *next; + + list_for_each_entry_safe( w, next, &model->polys, chain ) + { + if( w->pair ) + { + list_del( &w->chain ); + free_winding( w ); + model->num_polys--; + } + } +} + +static void make_hull_windings( hull_t *hull, hull_model_t *model ) +{ + hullnode_t head = LIST_HEAD_INIT( head ); + + Con_Reportf( "%i clipnodes...\n", hull->lastclipnode - hull->firstclipnode ); + + node_stack_depth = 0; + model->num_polys = 0; + + if( hull->planes != NULL ) + { + hull_windings_r( hull, hull->clipnodes + hull->firstclipnode, &head, model ); + remove_paired_polys( model ); + } + Con_Reportf( "%i hull polys\n", model->num_polys ); +} + +void Mod_InitDebugHulls( void ) +{ + int i; + + world.hull_models = Mem_Calloc( loadmodel->mempool, sizeof( hull_model_t ) * loadmodel->numsubmodels ); + world.num_hull_models = loadmodel->numsubmodels; + + // initialize list + for( i = 0; i < world.num_hull_models; i++ ) + { + hullnode_t *poly = &world.hull_models[i].polys; + poly->next = poly; + poly->prev = poly; + } +} + +void Mod_CreatePolygonsForHull( int hullnum ) +{ + model_t *mod = cl.worldmodel; + double start, end; + char name[8]; + int i; + + if( hullnum < 1 || hullnum > 3 ) + return; + + Con_Printf( "generating polygons for hull %u...\n", hullnum ); + start = Sys_DoubleTime(); + + // rebuild hulls list + for( i = 0; i < world.num_hull_models; i++ ) + { + hull_model_t *model = &world.hull_models[i]; + free_hull_polys( &model->polys ); + make_hull_windings( &mod->hulls[hullnum], model ); + Q_snprintf( name, sizeof( name ), "*%i", i + 1 ); + mod = Mod_FindName( name, false ); + } + end = Sys_DoubleTime(); + Con_Printf( "build time %.3f secs\n", end - start ); +} + +void Mod_ReleaseHullPolygons( void ) +{ + int i; + + // release ploygons + for( i = 0; i < world.num_hull_models; i++ ) + { + hull_model_t *model = &world.hull_models[i]; + free_hull_polys( &model->polys ); + } +} + +void R_DrawWorldHull( void ) +{ + hull_model_t *hull = &world.hull_models[0]; + winding_t *poly; + int i; + + if( FBitSet( r_showhull->flags, FCVAR_CHANGED )) + { + int val = bound( 0, (int)r_showhull->value, 3 ); + if( val ) Mod_CreatePolygonsForHull( val ); + ClearBits( r_showhull->flags, FCVAR_CHANGED ); + } + + if( !CVAR_TO_BOOL( r_showhull )) + return; + pglDisable( GL_TEXTURE_2D ); + + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned long)poly); + pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); + pglBegin( GL_POLYGON ); + for( i = 0; i < poly->numpoints; i++ ) + pglVertex3fv( poly->p[i] ); + pglEnd(); + } + pglEnable( GL_TEXTURE_2D ); +} + +void R_DrawModelHull( void ) +{ + hull_model_t *hull; + winding_t *poly; + int i; + + if( !CVAR_TO_BOOL( r_showhull )) + return; + + if( !RI.currentmodel || RI.currentmodel->name[0] != '*' ) + return; + + i = atoi( RI.currentmodel->name + 1 ); + if( i < 1 || i >= world.num_hull_models ) + return; + + hull = &world.hull_models[i]; + + pglPolygonOffset( 1.0f, 2.0 ); + pglEnable( GL_POLYGON_OFFSET_FILL ); + pglDisable( GL_TEXTURE_2D ); + list_for_each_entry( poly, &hull->polys, chain ) + { + srand((unsigned long)poly); + pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); + pglBegin( GL_POLYGON ); + for( i = 0; i < poly->numpoints; i++ ) + pglVertex3fv( poly->p[i] ); + pglEnd(); + } + pglEnable( GL_TEXTURE_2D ); + pglDisable( GL_POLYGON_OFFSET_FILL ); +} diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 6b90872e..ef2150ef 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -95,6 +95,27 @@ typedef struct #define NL_NEEDS_LOADED 1 #define NL_PRESENT 2 +typedef struct hullnode_s +{ + struct hullnode_s *next; + struct hullnode_s *prev; +} hullnode_t; + +typedef struct winding_s +{ + const mplane_t *plane; + struct winding_s *pair; + hullnode_t chain; + int numpoints; + vec3_t p[4]; // variable sized +} winding_t; + +typedef struct +{ + hullnode_t polys; + uint num_polys; +} hull_model_t; + typedef struct { msurface_t *surf; @@ -115,6 +136,9 @@ typedef struct sortedface_t *draw_surfaces; // used for sorting translucent surfaces int max_surfaces; // max surfaces per submodel (for all models) + hull_model_t *hull_models; + int num_hull_models; + // visibility info byte *visdata; // uncompressed visdata size_t visbytes; // cluster size @@ -132,6 +156,7 @@ extern byte *com_studiocache; extern model_t *loadmodel; extern convar_t *mod_studiocache; extern convar_t *r_wadtextures; +extern convar_t *r_showhull; // // model.c @@ -171,6 +196,13 @@ byte *Mod_GetPVSForPoint( const vec3_t p ); void Mod_UnloadBrushModel( model_t *mod ); void Mod_PrintWorldStats_f( void ); +// +// mod_dbghulls.c +// +void Mod_InitDebugHulls( void ); +void Mod_CreatePolygonsForHull( int hullnum ); +void Mod_ReleaseHullPolygons( void ); + // // mod_studio.c // diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 37bd8e86..7414a86c 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -828,7 +828,7 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) // give space for textures and skinrefs size1 = thdr->numtextures * sizeof( mstudiotexture_t ); size2 = thdr->numskinfamilies * thdr->numskinref * sizeof( short ); - mod->cache.data = Mem_Alloc( loadmodel->mempool, phdr->length + size1 + size2 ); + mod->cache.data = Mem_Calloc( loadmodel->mempool, phdr->length + size1 + size2 ); memcpy( loadmodel->cache.data, buffer, phdr->length ); // copy main mdl buffer phdr = (studiohdr_t *)loadmodel->cache.data; // get the new pointer on studiohdr phdr->numskinfamilies = thdr->numskinfamilies; @@ -847,7 +847,7 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) else { // NOTE: don't modify source buffer because it's used for CRC computing - loadmodel->cache.data = Mem_Alloc( loadmodel->mempool, phdr->length ); + loadmodel->cache.data = Mem_Calloc( loadmodel->mempool, phdr->length ); memcpy( loadmodel->cache.data, buffer, phdr->length ); phdr = (studiohdr_t *)loadmodel->cache.data; // get the new pointer on studiohdr Mod_StudioLoadTextures( mod, phdr ); diff --git a/engine/common/model.c b/engine/common/model.c index 5e88fbc7..4f20f69f 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -31,6 +31,7 @@ static int mod_numknown = 0; byte *com_studiocache; // cache for submodels convar_t *mod_studiocache; convar_t *r_wadtextures; +convar_t *r_showhull; model_t *loadmodel; /* @@ -141,6 +142,7 @@ void Mod_Init( void ) com_studiocache = Mem_AllocPool( "Studio Cache" ); mod_studiocache = Cvar_Get( "r_studiocache", "1", FCVAR_ARCHIVE, "enables studio cache for speedup tracing hitboxes" ); r_wadtextures = Cvar_Get( "r_wadtextures", "0", 0, "completely ignore textures in the bsp-file if enabled" ); + r_showhull = Cvar_Get( "r_showhull", "0", 0, "draw collision hulls 1-3" ); Cmd_AddCommand( "mapstats", Mod_PrintWorldStats_f, "show stats for currently loaded map" ); Cmd_AddCommand( "modellist", Mod_Modellist_f, "display loaded models list" ); @@ -158,6 +160,7 @@ void Mod_FreeAll( void ) { int i; + Mod_ReleaseHullPolygons(); for( i = 0; i < mod_numknown; i++ ) Mod_FreeModel( &mod_known[i] ); mod_numknown = 0; @@ -400,6 +403,10 @@ static void Mod_PurgeStudioCache( void ) { int i; + // refresh hull data + SetBits( r_showhull->flags, FCVAR_CHANGED ); + Mod_ReleaseHullPolygons(); + // release previois map Mod_FreeModel( mod_known ); // world is stuck on slot #0 always @@ -485,7 +492,7 @@ void *Mod_Calloc( int number, size_t size ) cache_user_t *cu; if( number <= 0 || size <= 0 ) return NULL; - cu = (cache_user_t *)Mem_Alloc( com_studiocache, sizeof( cache_user_t ) + number * size ); + cu = (cache_user_t *)Mem_Calloc( com_studiocache, sizeof( cache_user_t ) + number * size ); cu->data = (void *)cu; // make sure what cu->data is not NULL return cu; @@ -524,7 +531,7 @@ void Mod_LoadCacheFile( const char *filename, cache_user_t *cu ) buf = FS_LoadFile( modname, &size, false ); if( !buf || !size ) Host_Error( "LoadCacheFile: ^1can't load %s^7\n", filename ); - cu->data = Mem_Alloc( com_studiocache, size ); + cu->data = Mem_Malloc( com_studiocache, size ); memcpy( cu->data, buf, size ); Mem_Free( buf ); } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index b471eb11..1d0a5346 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -406,7 +406,7 @@ fragbuf_t *Netchan_AllocFragbuf( void ) { fragbuf_t *buf; - buf = (fragbuf_t *)Mem_Alloc( net_mempool, sizeof( fragbuf_t )); + buf = (fragbuf_t *)Mem_Calloc( net_mempool, sizeof( fragbuf_t )); MSG_Init( &buf->frag_message, "Frag Message", buf->frag_message_buf, sizeof( buf->frag_message_buf )); return buf; @@ -578,7 +578,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg ) chunksize = chan->pfnBlockSize( chan->client ); else chunksize = FRAGMENT_MAX_SIZE; // fallback - wait = (fragbufwaiting_t *)Mem_Alloc( net_mempool, sizeof( fragbufwaiting_t )); + wait = (fragbufwaiting_t *)Mem_Calloc( net_mempool, sizeof( fragbufwaiting_t )); if( !LZSS_IsCompressed( MSG_GetData( msg ))) { @@ -588,7 +588,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg ) if( pbOut && uCompressedSize > 0 && uCompressedSize < uSourceSize ) { - Con_DPrintf( "Compressing split packet (%d -> %d bytes)\n", uSourceSize, uCompressedSize ); + Con_Reportf( "Compressing split packet (%d -> %d bytes)\n", uSourceSize, uCompressedSize ); memcpy( msg->pData, pbOut, uCompressedSize ); MSG_SeekToBit( msg, uCompressedSize << 3, SEEK_SET ); } @@ -756,7 +756,7 @@ void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, char *filename, byt if( pbOut ) free( pbOut ); } - wait = (fragbufwaiting_t *)Mem_Alloc( net_mempool, sizeof( fragbufwaiting_t )); + wait = (fragbufwaiting_t *)Mem_Calloc( net_mempool, sizeof( fragbufwaiting_t )); remaining = size; pos = 0; @@ -871,7 +871,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename ) Mem_Free( uncompressed ); } - wait = (fragbufwaiting_t *)Mem_Alloc( net_mempool, sizeof( fragbufwaiting_t )); + wait = (fragbufwaiting_t *)Mem_Calloc( net_mempool, sizeof( fragbufwaiting_t )); remaining = filesize; pos = 0; @@ -1077,7 +1077,7 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ) p = p->next; } - buffer = Mem_Alloc( net_mempool, nsize + 1 ); + buffer = Mem_Calloc( net_mempool, nsize + 1 ); p = chan->incomingbufs[FRAG_FILE_STREAM]; pos = 0; @@ -1110,7 +1110,7 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ) if( LZSS_IsCompressed( buffer )) { uint uncompressedSize = LZSS_GetActualSize( buffer ) + 1; - byte *uncompressedBuffer = Mem_Alloc( net_mempool, uncompressedSize ); + byte *uncompressedBuffer = Mem_Calloc( net_mempool, uncompressedSize ); nsize = LZSS_Decompress( buffer, uncompressedBuffer ); Mem_Free( buffer ); diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index abfd9100..73c8da19 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -682,7 +682,7 @@ void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char *encode const delta_field_t *pInfo; // allocate the delta-structures - if( !dt->pFields ) dt->pFields = (delta_t *)Z_Malloc( dt->maxFields * sizeof( delta_t )); + if( !dt->pFields ) dt->pFields = (delta_t *)Z_Calloc( dt->maxFields * sizeof( delta_t )); pField = dt->pFields; pInfo = dt->pInfo; diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index 3eb5704e..415fd7f0 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -32,7 +32,7 @@ void Sound_Reset( void ) wavdata_t *SoundPack( void ) { - wavdata_t *pack = Mem_Alloc( host.soundpool, sizeof( wavdata_t )); + wavdata_t *pack = Mem_Calloc( host.soundpool, sizeof( wavdata_t )); pack->buffer = sound.wav; pack->width = sound.width; diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index 8b72b17b..b6c27a5b 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -102,7 +102,7 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) } sound.type = WF_PCMDATA; - sound.wav = (byte *)Mem_Alloc( host.soundpool, sound.size ); + sound.wav = (byte *)Mem_Malloc( host.soundpool, sound.size ); // decompress mpg into pcm wav format while( bytesWrite < sound.size ) @@ -155,7 +155,7 @@ stream_t *Stream_OpenMPG( const char *filename ) if( !file ) return NULL; // at this point we have valid stream - stream = Mem_Alloc( host.soundpool, sizeof( stream_t )); + stream = Mem_Calloc( host.soundpool, sizeof( stream_t )); stream->file = file; stream->pos = 0; diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index 382752e7..8add19be 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -87,7 +87,7 @@ byte *Sound_Copy( size_t size ) { byte *out; - out = Mem_Alloc( host.soundpool, size ); + out = Mem_Malloc( host.soundpool, size ); memcpy( out, sound.tempbuffer, size ); return out; diff --git a/engine/common/soundlib/snd_wav.c b/engine/common/soundlib/snd_wav.c index e99b2919..7757813b 100644 --- a/engine/common/soundlib/snd_wav.c +++ b/engine/common/soundlib/snd_wav.c @@ -279,7 +279,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) // Load the data sound.size = sound.samples * sound.width * sound.channels; - sound.wav = Mem_Alloc( host.soundpool, sound.size ); + sound.wav = Mem_Malloc( host.soundpool, sound.size ); memcpy( sound.wav, buffer + (iff_dataPtr - buffer), sound.size ); @@ -384,7 +384,7 @@ stream_t *Stream_OpenWAV( const char *filename ) sound.samples = ( sound.samples / sound.width ) / sound.channels; // at this point we have valid stream - stream = Mem_Alloc( host.soundpool, sizeof( stream_t )); + stream = Mem_Calloc( host.soundpool, sizeof( stream_t )); stream->file = file; stream->size = sound.samples * sound.width * sound.channels; stream->buffsize = FS_Tell( file ); // header length diff --git a/engine/common/titles.c b/engine/common/titles.c index 0a318d6b..8d77b9ee 100644 --- a/engine/common/titles.c +++ b/engine/common/titles.c @@ -317,7 +317,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) } // must malloc because we need to be able to clear it after initialization - clgame.titles = (client_textmessage_t *)Mem_Alloc( cls.mempool, textHeapSize + nameHeapSize + messageSize ); + clgame.titles = (client_textmessage_t *)Mem_Calloc( cls.mempool, textHeapSize + nameHeapSize + messageSize ); // copy table over memcpy( clgame.titles, textMessages, messageSize ); diff --git a/engine/common/zone.c b/engine/common/zone.c index 5d12ad09..11282529 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -15,12 +15,6 @@ GNU General Public License for more details. #include "common.h" -#define MEMUNIT 8 // smallest unit we care about is this many bytes -#define MEMCLUMPSIZE (65536 - 1536) // give malloc padding so we can't waste most of a page at the end -#define MEMBITS (MEMCLUMPSIZE / MEMUNIT) -#define MEMBITINTS (MEMBITS / 32) - -#define MEMCLUMP_SENTINEL 0xABADCAFE #define MEMHEADER_SENTINEL1 0xDEADF00D #define MEMHEADER_SENTINEL2 0xDF @@ -29,7 +23,6 @@ typedef struct memheader_s struct memheader_s *next; // next and previous memheaders in chain belonging to pool struct memheader_s *prev; struct mempool_s *pool; // pool this memheader belongs to - struct memclump_s *clump; // clump this memheader lives in, NULL if not in a clump size_t size; // size of the memory after the header (excluding header and sentinel2) const char *filename; // file name and line where Mem_Alloc was called uint fileline; @@ -38,22 +31,10 @@ typedef struct memheader_s // immediately followed by data, which is followed by a MEMHEADER_SENTINEL2 byte } memheader_t; -typedef struct memclump_s -{ - byte block[MEMCLUMPSIZE];// contents of the clump - uint sentinel1; // should always be MEMCLUMP_SENTINEL - int bits[MEMBITINTS]; // if a bit is on, it means that the MEMUNIT bytes it represents are allocated, otherwise free - uint sentinel2; // should always be MEMCLUMP_SENTINEL - size_t blocksinuse; // if this drops to 0, the clump is freed - size_t largestavailable; // largest block of memory available - struct memclump_s *chain; // next clump in the chain -} memclump_t; - typedef struct mempool_s { uint sentinel1; // should always be MEMHEADER_SENTINEL1 struct memheader_s *chain; // chain of individual memory allocations - struct memclump_s *clumpchain; // chain of clumps (if any) size_t totalsize; // total memory allocated in this pool (inside memheaders) size_t realsize; // total memory allocated in this pool (actual malloc total) size_t lastchecksize; // updated each time the pool is displayed by memlist @@ -66,10 +47,8 @@ typedef struct mempool_s mempool_t *poolchain = NULL; // critical stuff -void *_Mem_Alloc( byte *poolptr, size_t size, const char *filename, int fileline ) +void *_Mem_Alloc( byte *poolptr, size_t size, qboolean clear, const char *filename, int fileline ) { - int i, j, k, needed, endbit, largest; - memclump_t *clump, **clumpchainpointer; memheader_t *mem; mempool_t *pool = (mempool_t *)poolptr; @@ -77,68 +56,10 @@ void *_Mem_Alloc( byte *poolptr, size_t size, const char *filename, int fileline if( poolptr == NULL ) Sys_Error( "Mem_Alloc: pool == NULL (alloc at %s:%i)\n", filename, fileline ); pool->totalsize += size; - if( size < 4096 ) - { - // clumping - needed = ( sizeof( memheader_t ) + size + sizeof( int ) + (MEMUNIT - 1)) / MEMUNIT; - endbit = MEMBITS - needed; - for( clumpchainpointer = &pool->clumpchain; *clumpchainpointer; clumpchainpointer = &(*clumpchainpointer)->chain ) - { - clump = *clumpchainpointer; - if( clump->sentinel1 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_Alloc: trashed clump sentinel 1 (alloc at %s:%d)\n", filename, fileline ); - if( clump->sentinel2 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_Alloc: trashed clump sentinel 2 (alloc at %s:%d)\n", filename, fileline ); - if( clump->largestavailable >= needed ) - { - largest = 0; - for( i = 0; i < endbit; i++ ) - { - if( clump->bits[i>>5] & (1 << (i & 31))) - continue; - k = i + needed; - for( j = i; i < k; i++ ) - if( clump->bits[i>>5] & (1 << (i & 31))) - goto loopcontinue; - goto choseclump; -loopcontinue:; - if( largest < j - i ) - largest = j - i; - } - // since clump falsely advertised enough space (nothing wrong - // with that), update largest count to avoid wasting time in - // later allocations - clump->largestavailable = largest; - } - } - - pool->realsize += sizeof( memclump_t ); - clump = malloc( sizeof( memclump_t )); - if( clump == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); - memset( clump, 0, sizeof( memclump_t )); - *clumpchainpointer = clump; - clump->sentinel1 = MEMCLUMP_SENTINEL; - clump->sentinel2 = MEMCLUMP_SENTINEL; - clump->chain = NULL; - clump->blocksinuse = 0; - clump->largestavailable = MEMBITS - needed; - j = 0; -choseclump: - mem = (memheader_t *)((byte *)clump->block + j * MEMUNIT ); - mem->clump = clump; - clump->blocksinuse += needed; - - for( i = j + needed; j < i; j++ ) - clump->bits[j >> 5] |= (1 << (j & 31)); - } - else - { - // big allocations are not clumped - pool->realsize += sizeof( memheader_t ) + size + sizeof( int ); - mem = (memheader_t *)malloc( sizeof( memheader_t ) + size + sizeof( int )); - if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); - mem->clump = NULL; - } + // big allocations are not clumped + pool->realsize += sizeof( memheader_t ) + size + sizeof( int ); + mem = (memheader_t *)malloc( sizeof( memheader_t ) + size + sizeof( int )); + if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); mem->filename = filename; mem->fileline = fileline; @@ -153,7 +74,7 @@ choseclump: mem->prev = NULL; pool->chain = mem; if( mem->next ) mem->next->prev = mem; - memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); + if( clear ) memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); return (void *)((byte *)mem + sizeof( memheader_t )); } @@ -167,7 +88,7 @@ static const char *Mem_CheckFilename( const char *filename ) if( !COM_CheckString( out )) return dummy; - for( i = 0; i < 128; i++, out++ ) + for( i = 0; i < MAX_OSPATH; i++, out++ ) { if( *out == '\0' ) return filename; // valid name @@ -178,8 +99,6 @@ static const char *Mem_CheckFilename( const char *filename ) static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline ) { - int i, firstblock, endblock; - memclump_t *clump, **clumpchainpointer; mempool_t *pool; if( mem->sentinel1 != MEMHEADER_SENTINEL1 ) @@ -208,50 +127,8 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline // memheader has been unlinked, do the actual free now pool->totalsize -= mem->size; - if(( clump = mem->clump ) != NULL ) - { - if( clump->sentinel1 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_Free: trashed clump sentinel 1 (free at %s:%i)\n", filename, fileline ); - if( clump->sentinel2 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_Free: trashed clump sentinel 2 (free at %s:%i)\n", filename, fileline ); - firstblock = ((byte *)mem - (byte *)clump->block ); - if( firstblock & ( MEMUNIT - 1 )) - Sys_Error( "Mem_Free: address not valid in clump (free at %s:%i)\n", filename, fileline ); - firstblock /= MEMUNIT; - endblock = firstblock + ((sizeof( memheader_t ) + mem->size + sizeof( int ) + (MEMUNIT - 1)) / MEMUNIT ); - clump->blocksinuse -= endblock - firstblock; - - // could use &, but we know the bit is set - for( i = firstblock; i < endblock; i++ ) - clump->bits[i >> 5] -= (1 << (i & 31)); - if( clump->blocksinuse <= 0 ) - { - // unlink from chain - for( clumpchainpointer = &pool->clumpchain; *clumpchainpointer; clumpchainpointer = &(*clumpchainpointer)->chain ) - { - if (*clumpchainpointer == clump) - { - *clumpchainpointer = clump->chain; - break; - } - } - - pool->realsize -= sizeof( memclump_t ); - memset( clump, 0xBF, sizeof( memclump_t )); - free( clump ); - } - else - { - // clump still has some allocations - // force re-check of largest available space on next alloc - clump->largestavailable = MEMBITS - clump->blocksinuse; - } - } - else - { - pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int ); - free( mem ); - } + pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int ); + free( mem ); } void _Mem_Free( void *data, const char *filename, int fileline ) @@ -260,7 +137,7 @@ void _Mem_Free( void *data, const char *filename, int fileline ) Mem_FreeBlock((memheader_t *)((byte *)data - sizeof( memheader_t )), filename, fileline ); } -void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, const char *filename, int fileline ) +void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline ) { memheader_t *memhdr = NULL; char *nb; @@ -273,7 +150,7 @@ void *_Mem_Realloc( byte *poolptr, void *memptr, size_t size, const char *filena if( size == memhdr->size ) return memptr; } - nb = _Mem_Alloc( poolptr, size, filename, fileline ); + nb = _Mem_Alloc( poolptr, size, clear, filename, fileline ); if( memptr ) // first allocate? { @@ -399,20 +276,10 @@ void Mem_CheckHeaderSentinels( void *data, const char *filename, int fileline ) } } -static void Mem_CheckClumpSentinels( memclump_t *clump, const char *filename, int fileline ) -{ - // this isn't really very useful - if( clump->sentinel1 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_CheckClumpSentinels: trashed sentinel 1 (sentinel check at %s:%i)\n", filename, fileline ); - if( clump->sentinel2 != MEMCLUMP_SENTINEL ) - Sys_Error( "Mem_CheckClumpSentinels: trashed sentinel 2 (sentinel check at %s:%i)\n", filename, fileline ); -} - void _Mem_Check( const char *filename, int fileline ) { memheader_t *mem; mempool_t *pool; - memclump_t *clump; for( pool = poolchain; pool; pool = pool->next ) { @@ -425,10 +292,6 @@ void _Mem_Check( const char *filename, int fileline ) for( pool = poolchain; pool; pool = pool->next ) for( mem = pool->chain; mem; mem = mem->next ) Mem_CheckHeaderSentinels((void *)((byte *) mem + sizeof(memheader_t)), filename, fileline ); - - for( pool = poolchain; pool; pool = pool->next ) - for( clump = pool->clumpchain; clump; clump = clump->chain ) - Mem_CheckClumpSentinels( clump, filename, fileline ); } void Mem_PrintStats( void ) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 1cdcb87b..17640ebd 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -340,7 +340,7 @@ void SV_ConnectClient( netadr_t from ) sv.current_client = newcl; newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 ); newcl->challenge = challenge; // save challenge for checksumming - newcl->frames = (client_frame_t *)Z_Malloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP ); + newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP ); newcl->userid = g_userid++; // create unique userid newcl->state = cs_connected; @@ -2252,7 +2252,7 @@ void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg ) for( i = 0; i < total; i++ ) { - resource = Z_Malloc( sizeof( resource_t ) ); + resource = Z_Calloc( sizeof( resource_t ) ); Q_strncpy( resource->szFileName, MSG_ReadString( msg ), sizeof( resource->szFileName )); resource->type = MSG_ReadByte( msg ); resource->nIndex = MSG_ReadShort( msg ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 07b9454d..0de8d171 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -609,7 +609,7 @@ void SV_RestartDecals( void ) if( !SV_Active( )) return; // g-cont. add space for studiodecals if present - host.decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); + host.decalList = (decallist_t *)Z_Calloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); host.numdecals = R_CreateDecalList( host.decalList ); // remove decals from map @@ -692,7 +692,7 @@ void SV_WriteEntityPatch( const char *filename ) char *entities = NULL; FS_Seek( f, lumpofs, SEEK_SET ); - entities = (char *)Z_Malloc( lumplen + 1 ); + entities = (char *)Z_Calloc( lumplen + 1 ); FS_Read( f, entities, lumplen ); FS_WriteFile( va( "maps/%s.ent", filename ), entities, lumplen ); Con_Printf( "Write 'maps/%s.ent'\n", filename ); @@ -760,7 +760,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) if( !ents && lumplen >= 32 ) { FS_Seek( f, lumpofs, SEEK_SET ); - ents = Z_Malloc( lumplen + 1 ); + ents = Z_Calloc( lumplen + 1 ); FS_Read( f, ents, lumplen ); } FS_Close( f ); // all done @@ -2011,8 +2011,14 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample if( sample[0] == '!' && Q_isdigit( sample + 1 )) { - SetBits( flags, SND_SENTENCE ); sound_idx = Q_atoi( sample + 1 ); + + if( sound_idx >= MAX_SOUNDS ) + { + SetBits( flags, SND_SENTENCE|SND_SEQUENCE ); + sound_idx -= MAX_SOUNDS; + } + else SetBits( flags, SND_SENTENCE ); } else if( sample[0] == '#' && Q_isdigit( sample + 1 )) { @@ -2922,7 +2928,7 @@ void *pfnPvAllocEntPrivateData( edict_t *pEdict, long cb ) if( cb > 0 ) { // a poke646 have memory corrupt in somewhere - this is trashed last sixteen bytes :( - pEdict->pvPrivateData = Mem_Alloc( svgame.mempool, (cb + 15) & ~15 ); + pEdict->pvPrivateData = Mem_Calloc( svgame.mempool, (cb + 15) & ~15 ); } return pEdict->pvPrivateData; @@ -2962,7 +2968,7 @@ string_t SV_AllocString( const char *szString ) l = Q_strlen( szString ) + 1; - out = out_p = Mem_Alloc( svgame.stringspool, l ); + out = out_p = Mem_Calloc( svgame.stringspool, l ); for( i = 0; i < l; i++ ) { if( szString[i] == '\\' && i < l - 1 ) @@ -4094,7 +4100,7 @@ void pfnEndSection( const char *pszSection ) { if( !Q_stricmp( "oem_end_credits", pszSection )) Host_Credits (); - else Cbuf_AddText( va( "endgame \"%s\"\n", pszSection )); + else Cbuf_AddText( "\ndisconnect\n" ); } /* @@ -4860,8 +4866,8 @@ qboolean SV_LoadProgs( const char *name ) svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = svs.maxclients; - svgame.edicts = Mem_Alloc( svgame.mempool, sizeof( edict_t ) * GI->max_edicts ); - svs.baselines = Z_Malloc( sizeof( entity_state_t ) * GI->max_edicts ); + svgame.edicts = Mem_Calloc( svgame.mempool, sizeof( edict_t ) * GI->max_edicts ); + svs.baselines = Z_Calloc( sizeof( entity_state_t ) * GI->max_edicts ); svgame.numEntities = svs.maxclients + 1; // clients + world for( i = 0, e = svgame.edicts; i < GI->max_edicts; i++, e++ ) diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 27bd96d6..a3bcade7 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1943,7 +1943,7 @@ static char **pfnGetFilesList( const char *pattern, int *numFiles, int gamediron static void *pfnMem_Alloc( size_t cb, const char *filename, const int fileline ) { - return _Mem_Alloc( svgame.mempool, cb, filename, fileline ); + return _Mem_Alloc( svgame.mempool, cb, true, filename, fileline ); } static void pfnMem_Free( void *mem, const char *filename, const int fileline ) @@ -1979,7 +1979,7 @@ const byte *pfnLoadImagePixels( const char *filename, int *width, int *height ) if( !pic ) return NULL; - buffer = Mem_Alloc( svgame.mempool, pic->size ); + buffer = Mem_Malloc( svgame.mempool, pic->size ); if( buffer ) memcpy( buffer, pic->buffer, pic->size ); if( width ) *width = pic->width; if( height ) *height = pic->height; diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 7577bd6e..5f166a7b 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -177,7 +177,7 @@ void SV_GetTrueOrigin( sv_client_t *cl, int edictnum, vec3_t origin ) return; if( svgame.interp[edictnum-1].active && svgame.interp[edictnum-1].moving ) - VectorCopy( svgame.interp[edictnum-1].newpos, origin ); + VectorCopy( svgame.interp[edictnum-1].oldpos, origin ); } void SV_GetTrueMinMax( sv_client_t *cl, int edictnum, vec3_t mins, vec3_t maxs ) diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index a8d9c045..14b1f076 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -266,7 +266,7 @@ static void InitEntityTable( SAVERESTOREDATA *pSaveData, int entityCount ) ENTITYTABLE *pTable; int i; - pSaveData->pTable = Mem_Alloc( host.mempool, sizeof( ENTITYTABLE ) * entityCount ); + pSaveData->pTable = Mem_Calloc( host.mempool, sizeof( ENTITYTABLE ) * entityCount ); pSaveData->tableCount = entityCount; // setup entitytable @@ -604,8 +604,8 @@ static SAVERESTOREDATA *SaveInit( int size, int tokenCount ) { SAVERESTOREDATA *pSaveData; - pSaveData = Mem_Alloc( host.mempool, sizeof( SAVERESTOREDATA ) + size ); - pSaveData->pTokens = (char **)Mem_Alloc( host.mempool, tokenCount * sizeof( char* )); + pSaveData = Mem_Calloc( host.mempool, sizeof( SAVERESTOREDATA ) + size ); + pSaveData->pTokens = (char **)Mem_Calloc( host.mempool, tokenCount * sizeof( char* )); pSaveData->tokenCount = tokenCount; pSaveData->pBaseData = (char *)(pSaveData + 1); // skip the save structure); @@ -1108,7 +1108,7 @@ static void SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int memset( &header, 0, sizeof( header )); // g-cont. add space for studiodecals if present - decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); + decalList = (decallist_t *)Z_Calloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); // initialize client header header.decalCount = R_CreateDecalList( decalList ); @@ -2220,14 +2220,14 @@ qboolean SV_GetSaveComment( const char *savename, char *comment ) return 0; } - pSaveData = (char *)Mem_Alloc( host.mempool, size ); + pSaveData = (char *)Mem_Malloc( host.mempool, size ); FS_Read( f, pSaveData, size ); pData = pSaveData; // allocate a table for the strings, and parse the table if( tokenSize > 0 ) { - pTokenList = Mem_Alloc( host.mempool, tokenCount * sizeof( char* )); + pTokenList = Mem_Calloc( host.mempool, tokenCount * sizeof( char* )); // make sure the token strings pointed to by the pToken hashtable. for( i = 0; i < tokenCount; i++ ) From a539384a76672bbf682643fa23f30f58f638573f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 12 Jun 2018 12:14:56 +0300 Subject: [PATCH 016/205] Apply 4143 update --- engine/client/cl_game.c | 4 +++- engine/client/cl_main.c | 7 ++++--- engine/client/cl_pmove.c | 6 +++--- engine/client/cl_scrn.c | 10 +++++----- engine/client/gl_beams.c | 8 ++++---- engine/client/gl_rlight.c | 4 ++-- engine/client/gl_vidnt.c | 2 +- engine/client/s_backend.c | 3 --- engine/common/build.c | 2 +- engine/common/com_strings.h | 4 ++++ engine/common/host.c | 4 ++++ engine/common/host_state.c | 2 +- engine/common/mod_bmodel.c | 35 ++++++++++++++++++++++++++++------- engine/common/mod_dbghulls.c | 1 + engine/common/pm_trace.c | 2 +- engine/server/sv_client.c | 4 ++-- engine/server/sv_cmds.c | 2 +- engine/server/sv_game.c | 13 +++++++++---- engine/server/sv_init.c | 2 +- 19 files changed, 75 insertions(+), 40 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index cda41ad0..1f701166 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1211,6 +1211,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, } else { + Con_Printf( S_ERROR "%s couldn't load\n", szSpriteName ); Mod_UnloadSpriteModel( m_pSprite ); return false; } @@ -1865,7 +1866,8 @@ int pfnDrawConsoleString( int x, int y, char *string ) { int drawLen; - if( !string || !*string ) return 0; // silent ignore + if( !COM_CheckString( string )) + return 0; // silent ignore Con_SetFont( con_fontsize->value ); clgame.ds.adjust_size = true; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 0de9f5b6..21901591 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -208,7 +208,7 @@ An svc_signonnum has been received, perform a client side setup void CL_SignonReply( void ) { // g-cont. my favorite message :-) - Con_DPrintf( "CL_SignonReply: %i\n", cls.signon ); + Con_Reportf( "CL_SignonReply: %i\n", cls.signon ); switch( cls.signon ) { @@ -1745,7 +1745,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) Cmd_TokenizeString( args ); c = Cmd_Argv( 0 ); - MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c ); + Con_Reportf( "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c ); // server connection if( !Q_strcmp( c, "client_connect" )) @@ -2387,7 +2387,7 @@ qboolean CL_PrecacheResources( void ) if( cl.models[pRes->nIndex] == NULL ) { - MsgDev( D_ERROR, "submodel %s not found\n", pRes->szFileName ); + Con_Printf( S_ERROR "submodel %s not found\n", pRes->szFileName ); if( FBitSet( pRes->ucFlags, RES_FATALIFMISSING )) { @@ -2414,6 +2414,7 @@ qboolean CL_PrecacheResources( void ) { if( FBitSet( pRes->ucFlags, RES_WASMISSING )) { + Con_Printf( S_ERROR "%s%s couldn't load\n", DEFAULT_SOUNDPATH, pRes->szFileName ); cl.sound_precache[pRes->nIndex][0] = 0; cl.sound_index[pRes->nIndex] = 0; } diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 5dcbdb17..8afa0df2 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -481,9 +481,6 @@ void CL_AddLinksToPmove( frame_t *frame ) if( VectorIsNull( state->mins ) && VectorIsNull( state->maxs )) continue; - if ( !model->hulls[1].lastclipnode && model->type != mod_studio ) - continue; - if( state->solid == SOLID_NOT && state->skin < CONTENTS_EMPTY ) { if( clgame.pmove->nummoveent >= MAX_MOVEENTS ) @@ -495,6 +492,9 @@ void CL_AddLinksToPmove( frame_t *frame ) } else { + if( !model->hulls[1].lastclipnode && model->type != mod_studio ) + continue; + // reserve slots for all the clients if( clgame.pmove->numphysent >= ( MAX_PHYSENTS - cl.maxclients )) continue; diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 41f9c88e..4f067930 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -586,8 +586,8 @@ void SCR_InstallParticlePalette( void ) int i; // first check 'palette.lmp' then 'palette.pal' - pic = FS_LoadImage( "gfx/palette.lmp", NULL, 0 ); - if( !pic ) pic = FS_LoadImage( "gfx/palette.pal", NULL, 0 ); + pic = FS_LoadImage( DEFAULT_INTERNAL_PALETTE, NULL, 0 ); + if( !pic ) pic = FS_LoadImage( DEFAULT_EXTERNAL_PALETTE, NULL, 0 ); // NOTE: imagelib required this fakebuffer for loading internal palette if( !pic ) pic = FS_LoadImage( "#valve.pal", (byte *)&i, 768 ); @@ -604,13 +604,13 @@ void SCR_InstallParticlePalette( void ) } else { + // someone deleted internal palette from code... for( i = 0; i < 256; i++ ) { clgame.palette[i].r = i; clgame.palette[i].g = i; clgame.palette[i].b = i; } - MsgDev( D_WARN, "CL_InstallParticlePalette: failed. Force to grayscale\n" ); } } @@ -731,12 +731,12 @@ void SCR_Init( void ) host.allow_console = true; // we need console, because menu is missing } + SCR_VidInit(); SCR_LoadCreditsFont (); - SCR_InstallParticlePalette (); SCR_RegisterTextures (); + SCR_InstallParticlePalette (); SCR_InitCinematic(); CL_InitNetgraph(); - SCR_VidInit(); if( host.allow_console && Sys_CheckParm( "-toconsole" )) Cbuf_AddText( "toggleconsole\n" ); diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index d88a5714..f73b53d7 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -1897,14 +1897,14 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType ) startFrame = MSG_ReadByte( msg ); frameRate = (float)(MSG_ReadByte( msg )); life = (float)(MSG_ReadByte( msg ) * 0.1f); - width = (float)(MSG_ReadByte( msg ) * 0.1f); - noise = (float)(MSG_ReadByte( msg ) * 0.01f); + width = (float)(MSG_ReadByte( msg )); + noise = (float)(MSG_ReadByte( msg ) * 0.1f); r = (float)MSG_ReadByte( msg ) / 255.0f; g = (float)MSG_ReadByte( msg ) / 255.0f; b = (float)MSG_ReadByte( msg ) / 255.0f; a = (float)MSG_ReadByte( msg ) / 255.0f; - speed = (float)MSG_ReadByte( msg ); - R_BeamCirclePoints( beamType, start, end, modelIndex, life, width, noise, a, speed / 10.0f, startFrame, frameRate, r, g, b ); + speed = (float)(MSG_ReadByte( msg ) / 0.1f); + R_BeamCirclePoints( beamType, start, end, modelIndex, life, width, noise, a, speed, startFrame, frameRate, r, g, b ); break; case TE_BEAMFOLLOW: startEnt = MSG_ReadShort( msg ); diff --git a/engine/client/gl_rlight.c b/engine/client/gl_rlight.c index 91fe10e7..223abdf9 100644 --- a/engine/client/gl_rlight.c +++ b/engine/client/gl_rlight.c @@ -355,9 +355,9 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, if( dm != NULL ) { vec3_t srcNormal, lightNormal; - float f = (1.0f / 255.0f); + float f = (1.0f / 128.0f); - VectorSet( srcNormal, (dm->r * f) * 2.0f - 1.0f, (dm->g * f) * 2.0f - 1.0f, (dm->b * f) * 2.0f - 1.0f ); + VectorSet( srcNormal, ((float)dm->r - 128.0f) * f, ((float)dm->g - 128.0f) * f, ((float)dm->b - 128.0f) * f ); Matrix3x4_VectorIRotate( tbn, srcNormal, lightNormal ); // turn to world space VectorScale( lightNormal, (float)scale * -1.0f, lightNormal ); // turn direction from light VectorAdd( g_trace_lightvec, lightNormal, g_trace_lightvec ); diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 59cd2c52..41c3a9e3 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -1657,7 +1657,7 @@ void GL_InitExtensions( void ) glConfig.renderer_string = pglGetString( GL_RENDERER ); glConfig.version_string = pglGetString( GL_VERSION ); glConfig.extensions_string = pglGetString( GL_EXTENSIONS ); - Con_Printf( "Video: %s\n", glConfig.renderer_string ); + Con_Printf( "^3Video:^7 %s\n", glConfig.renderer_string ); // intialize wrapper type glConfig.context = CONTEXT_TYPE_GL; diff --git a/engine/client/s_backend.c b/engine/client/s_backend.c index d234be55..73061669 100644 --- a/engine/client/s_backend.c +++ b/engine/client/s_backend.c @@ -311,9 +311,6 @@ int SNDDMA_Init( void *hInst ) // init DirectSound if( SNDDMA_InitDirect( hInst ) != SIS_SUCCESS ) return false; - - if( snd_firsttime ) - Con_Printf( "Audio: DirectSound\n" ); dma.initialized = true; snd_firsttime = false; diff --git a/engine/common/build.c b/engine/common/build.c index 0bd5ad9a..04ffdb2e 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 4140; + return 4143; #endif } diff --git a/engine/common/com_strings.h b/engine/common/com_strings.h index 1817fb8a..50a92d4a 100644 --- a/engine/common/com_strings.h +++ b/engine/common/com_strings.h @@ -41,6 +41,10 @@ GNU General Public License for more details. // debug beams #define DEFAULT_LASERBEAM_PATH "sprites/laserbeam.spr" +#define DEFAULT_INTERNAL_PALETTE "gfx/palette.lmp" + +#define DEFAULT_EXTERNAL_PALETTE "gfx/palette.pal" + // path to folders where placed all sounds #define DEFAULT_SOUNDPATH "sound/" diff --git a/engine/common/host.c b/engine/common/host.c index 4d7f45e8..82048720 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -346,6 +346,10 @@ void Host_InitDecals( void ) int i, num_decals = 0; search_t *t; + // NOTE: only once resource without which engine can't continue work + if( !FS_FileExists( "gfx/conchars", false )) + Sys_Error( "W_LoadWadFile: couldn't load gfx.wad\n" ); + memset( host.draw_decals, 0, sizeof( host.draw_decals )); // lookup all the decals in decals.wad (basedir, gamedir, falldir) diff --git a/engine/common/host_state.c b/engine/common/host_state.c index dfbc7ed2..d76551a6 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -149,7 +149,7 @@ void COM_Frame( float time ) { int oldState = GameState->curstate; - // execute the current state (and transition to the next state if not in HS_RUN) + // execute the current state (and transition to the next state if not in STATE_RUNFRAME) switch( GameState->curstate ) { case STATE_LOAD_LEVEL: diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 782ebacd..3bf58bc7 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -28,6 +28,7 @@ GNU General Public License for more details. typedef struct wadlist_s { char wadnames[MAX_MAP_WADS][32]; + int wadusage[MAX_MAP_WADS]; int count; } wadlist_t; @@ -334,11 +335,10 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, loadstat.numerrors++; return; } - else + else if( !FBitSet( flags, LUMP_SILENT )) { // just throw warning - if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_WARN, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); + MsgDev( D_WARN, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); loadstat.numwarnings++; } } @@ -1595,6 +1595,7 @@ static void Mod_LoadEntities( dbspmodel_t *bmod ) { int num = bmod->wadlist.count++; Q_strncpy( bmod->wadlist.wadnames[num], token, sizeof( bmod->wadlist.wadnames[0] )); + bmod->wadlist.wadusage[num] = 0; } if( bmod->wadlist.count >= MAX_MAP_WADS ) @@ -1886,6 +1887,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( FS_FileExists( texpath, false )) { tx->gl_texturenum = GL_LoadTexture( texpath, NULL, 0, 0, filter ); + bmod->wadlist.wadusage[j]++; // this wad are really used break; } } @@ -1942,6 +1944,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( FS_FileExists( texpath, false )) { src = FS_LoadFile( texpath, &srcSize, false ); + bmod->wadlist.wadusage[j]++; // this wad are really used break; } } @@ -2694,11 +2697,15 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) } for( i = 0; i < bmod->wadlist.count; i++ ) + { + if( !bmod->wadlist.wadusage[i] ) + continue; Q_strncat( wadvalue, va( "%s.wad; ", bmod->wadlist.wadnames[i] ), sizeof( wadvalue )); + } if( COM_CheckString( wadvalue )) { - wadvalue[Q_strlen( wadvalue ) - 2] = '\0'; + wadvalue[Q_strlen( wadvalue ) - 2] = '\0'; // kill the last semicolon Con_DPrintf( "Wad files required to run the map: \"%s\"\n", wadvalue ); } @@ -2974,17 +2981,30 @@ only empty lumps is allows */ int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize ) { - file_t *f = FS_Open( filename, "e+b", true ); byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; size_t prefetch_size = sizeof( buffer ); + int result, dummy = lumpsize; dextrahdr_t *extrahdr; dheader_t *header; - - if( !f ) return LUMP_SAVE_COULDNT_OPEN; + file_t *f; if( !lumpdata || lumpsize <= 0 ) return LUMP_SAVE_NO_DATA; + // make sure what .bsp is placed into gamedir and not in pak + if( !FS_GetDiskPath( filename, true )) + return LUMP_SAVE_COULDNT_OPEN; + + // first we should sure what we allow to rewrite this .bsp + result = Mod_CheckLump( filename, lump, &dummy ); + + if( result != LUMP_LOAD_NOT_EXIST ) + return result; + + f = FS_Open( filename, "e+b", true ); + + if( !f ) return LUMP_SAVE_COULDNT_OPEN; + if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) { FS_Close( f ); @@ -2993,6 +3013,7 @@ int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lump header = (dheader_t *)buffer; + // these checks below are redundant if( header->version != HLBSP_VERSION ) { FS_Close( f ); diff --git a/engine/common/mod_dbghulls.c b/engine/common/mod_dbghulls.c index 2c57b3f5..ffa96a1e 100644 --- a/engine/common/mod_dbghulls.c +++ b/engine/common/mod_dbghulls.c @@ -711,6 +711,7 @@ void Mod_ReleaseHullPolygons( void ) hull_model_t *model = &world.hull_models[i]; free_hull_polys( &model->polys ); } + world.num_hull_models = 0; } void R_DrawWorldHull( void ) diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 6820b2b8..d3afdcda 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -299,7 +299,7 @@ loc0: { trace->fraction = midf; VectorCopy( mid, trace->endpos ); - MsgDev( D_WARN, "trace backed up past 0.0\n" ); + Con_Reportf( S_WARN "trace backed up past 0.0\n" ); return false; } diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 17640ebd..f305c8b2 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -329,7 +329,7 @@ void SV_ConnectClient( netadr_t from ) } else { - MsgDev( D_INFO, "%s:reconnect\n", NET_AdrToString( from )); + Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from )); } // find a client slot @@ -2077,7 +2077,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) Cmd_TokenizeString( args ); pcmd = Cmd_Argv( 0 ); - Con_DPrintf( "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), pcmd ); + Con_Reportf( "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), pcmd ); if( !Q_strcmp( pcmd, "ping" )) SV_Ping( from ); else if( !Q_strcmp( pcmd, "ack" )) SV_Ack( from ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index e9a18b28..5498c279 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -315,7 +315,7 @@ SV_Load_f */ void SV_Load_f( void ) { - string path; + char path[MAX_QPATH]; if( Cmd_Argc() != 2 ) { diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 0de8d171..a49a752b 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -1318,6 +1318,7 @@ void pfnChangeLevel( const char *level, const char *landmark ) COM_StripExtension( mapname ); landname[0] ='\0'; +#ifdef HACKS_RELATED_HLMODS // g-cont. some level-designers wrote landmark name with space // and Cmd_TokenizeString separating all the after space as next argument // emulate this bug for compatibility @@ -1326,9 +1327,13 @@ void pfnChangeLevel( const char *level, const char *landmark ) text = (char *)landname; while( *landmark && ((byte)*landmark) != ' ' ) *text++ = *landmark++; - smooth = true; *text = '\0'; } +#else + Q_strncpy( landname, landmark, sizeof( landname )); +#endif + if( COM_CheckString( landname )) + smooth = true; // determine spawn entity classname if( svs.maxclients == 1 ) @@ -2399,7 +2404,7 @@ void pfnServerExecute( void ) Cbuf_Execute(); if( host.sv_cvars_restored > 0 ) - Con_DPrintf( "server executing ^2config.cfg^7 (%i cvars)\n", host.sv_cvars_restored ); + Con_Reportf( "server executing ^2config.cfg^7 (%i cvars)\n", host.sv_cvars_restored ); host.apply_game_config = false; svgame.config_executed = true; @@ -3406,7 +3411,7 @@ OBSOLETE, UNUSED */ uint pfnGetPlayerWONId( edict_t *e ) { - return -1; + return (uint)-1; } /* @@ -4841,7 +4846,7 @@ qboolean SV_LoadProgs( const char *name ) return false; } } - else Con_DPrintf( "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version ); + else Con_Reportf( "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version ); } else if( !GetEntityAPI( &svgame.dllFuncs, version )) { diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 92d8c683..a76bc358 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -725,7 +725,7 @@ void SV_SetupClients( void ) svs.clients = Z_Realloc( svs.clients, sizeof( sv_client_t ) * svs.maxclients ); svs.num_client_entities = svs.maxclients * SV_UPDATE_BACKUP * NUM_PACKET_ENTITIES; svs.packet_entities = Z_Realloc( svs.packet_entities, sizeof( entity_state_t ) * svs.num_client_entities ); - Con_DPrintf( "%s alloced by server packet entities\n", Q_memprint( sizeof( entity_state_t ) * svs.num_client_entities )); + Con_Reportf( "%s alloced by server packet entities\n", Q_memprint( sizeof( entity_state_t ) * svs.num_client_entities )); // init network stuff NET_Config(( svs.maxclients > 1 )); From b5c621ae36808d69535dfe4363798a3c7c1d73ff Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 19:27:06 +0300 Subject: [PATCH 017/205] Fix mainui submodule --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index b82d68cc..a61e1845 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit b82d68cc40b144a0114a76fa5dfabbc8e3fddf29 +Subproject commit a61e18458446f54fca061b5040628aae407144c1 From d0ff201da23326a95f4d70bbc8eb8708da5ce7a7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 20:31:46 +0300 Subject: [PATCH 018/205] Fix dedicated compiling errors --- engine/{common => client}/mod_dbghulls.c | 0 engine/common/dedicated.c | 4 ++-- engine/common/mod_bmodel.c | 2 ++ engine/common/mod_studio.c | 2 +- engine/common/model.c | 5 ++++- engine/common/net_ws.c | 2 +- engine/common/system.c | 6 +++++- 7 files changed, 15 insertions(+), 6 deletions(-) rename engine/{common => client}/mod_dbghulls.c (100%) diff --git a/engine/common/mod_dbghulls.c b/engine/client/mod_dbghulls.c similarity index 100% rename from engine/common/mod_dbghulls.c rename to engine/client/mod_dbghulls.c diff --git a/engine/common/dedicated.c b/engine/common/dedicated.c index 4ffe506d..66326411 100644 --- a/engine/common/dedicated.c +++ b/engine/common/dedicated.c @@ -351,7 +351,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui { pinq1 = (dsprite_q1_t *)buffer; size = sizeof( msprite_t ) + ( pinq1->numframes - 1 ) * sizeof( psprite->frames ); - psprite = Mem_Alloc( mod->mempool, size ); + psprite = Mem_Calloc( mod->mempool, size ); mod->cache.data = psprite; // make link to extradata psprite->type = pinq1->type; @@ -371,7 +371,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui { pinhl = (dsprite_hl_t *)buffer; size = sizeof( msprite_t ) + ( pinhl->numframes - 1 ) * sizeof( psprite->frames ); - psprite = Mem_Alloc( mod->mempool, size ); + psprite = Mem_Calloc( mod->mempool, size ); mod->cache.data = psprite; // make link to extradata psprite->type = pinhl->type; diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index f647c153..5f78c5b1 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2701,7 +2701,9 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) if( isworld ) { loadmodel = mod; // restore pointer to world +#ifndef XASH_DEDICATED Mod_InitDebugHulls(); // FIXME: build hulls for separate bmodels (shells, medkits etc) +#endif // XASH_DEDICATED } for( i = 0; i < bmod->wadlist.count; i++ ) diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 1cc8a4cf..ccf011ed 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -1101,7 +1101,7 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) } #else // just copy model into memory - loadmodel->cache.data = Mem_Alloc( loadmodel->mempool, phdr->length ); + loadmodel->cache.data = Mem_Calloc( loadmodel->mempool, phdr->length ); memcpy( loadmodel->cache.data, buffer, phdr->length ); phdr = loadmodel->cache.data; diff --git a/engine/common/model.c b/engine/common/model.c index 59cfe5a9..faab403d 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -164,7 +164,9 @@ void Mod_FreeAll( void ) { int i; +#ifndef XASH_DEDICATED Mod_ReleaseHullPolygons(); +#endif for( i = 0; i < mod_numknown; i++ ) Mod_FreeModel( &mod_known[i] ); mod_numknown = 0; @@ -414,8 +416,9 @@ static void Mod_PurgeStudioCache( void ) // refresh hull data SetBits( r_showhull->flags, FCVAR_CHANGED ); +#ifndef XASH_DEDICATED Mod_ReleaseHullPolygons(); - +#endif // release previois map Mod_FreeModel( mod_known ); // world is stuck on slot #0 always diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 6d6eec87..a7b8e67c 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -436,7 +436,7 @@ int NET_GetHostByName( const char *hostname ) return ip; #else struct hostent *h; - if(!( h = pGetHostByName( copy ))) + if(!( h = pGetHostByName( hostname ))) return 0; return *(int *)h->h_addr_list[0]; #endif diff --git a/engine/common/system.c b/engine/common/system.c index 9c096dea..4ca7fa51 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -668,8 +668,10 @@ print into window console */ void Sys_Print( const char *pMsg ) { +#ifndef XASH_DEDICATED if( !Host_IsDedicated() ) Con_Print( pMsg ); +#endif #ifdef _WIN32 { @@ -680,8 +682,10 @@ void Sys_Print( const char *pMsg ) char *c = logbuf; int i = 0; - if( host.type == HOST_NORMAL ) +#ifndef XASH_DEDICATED + if( !Host_IsDedicated() ) Con_Print( pMsg ); +#endif // if the message is REALLY long, use just the last portion of it if( Q_strlen( pMsg ) > sizeof( buffer ) - 1 ) From 473810fc0cd59590c0cf3aa970166cff6b358e81 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 20:32:31 +0300 Subject: [PATCH 019/205] MSVC & Win32 related fixes --- common/port.h | 1 + engine/common/lib_common.c | 2 +- engine/eiface.h | 1 + engine/platform/win32/win_lib.c | 2 +- game_launch/game.cpp | 1 + 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/common/port.h b/common/port.h index 2be6a948..fc3b5b44 100644 --- a/common/port.h +++ b/common/port.h @@ -118,6 +118,7 @@ GNU General Public License for more details. #define strncasecmp _strnicmp #define open _open #define read _read + #define alloca _alloca // shut-up compiler warnings #pragma warning(disable : 4244) // MIPS diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index bf3e9412..cbf0048b 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -48,7 +48,7 @@ void *COM_FunctionFromName_SR( void *hInstance, const char *pName ) const char *COM_OffsetNameForFunction( void *function ) { static string sname; - Q_snprintf( sname, MAX_STRING, "ofs:%d", (int)(void*)(function - (void*)svgame.dllFuncs.pfnGameInit) ); + Q_snprintf( sname, MAX_STRING, "ofs:%d", (size_t)((byte*)function - (byte*)svgame.dllFuncs.pfnGameInit) ); MsgDev( D_NOTE, "COM_OffsetNameForFunction %s\n", sname ); return sname; } diff --git a/engine/eiface.h b/engine/eiface.h index 0fc110cb..2e456814 100644 --- a/engine/eiface.h +++ b/engine/eiface.h @@ -390,6 +390,7 @@ typedef struct short flags; } TYPEDESCRIPTION; +#undef ARRAYSIZE #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) typedef struct playermove_s playermove_t; diff --git a/engine/platform/win32/win_lib.c b/engine/platform/win32/win_lib.c index 426b4467..a130a687 100644 --- a/engine/platform/win32/win_lib.c +++ b/engine/platform/win32/win_lib.c @@ -950,7 +950,7 @@ const char *COM_NameForFunction( void *hInstance, void *function ) { index = hInst->ordinals[i]; - if(( function - hInst->funcBase ) == hInst->funcs[index] ) + if(( (char*)function - (char*)hInst->funcBase ) == hInst->funcs[index] ) return hInst->names[i]; } diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 4715e8e5..2cc788a0 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -36,6 +36,7 @@ GNU General Public License for more details. #else #define XASHLIB "xash_dedicated.dll" #endif + #define dlerror() GetStringLastError() #include #endif From 572705bc08598c445f8335341c8ce8d1e3f72ad5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 20:34:51 +0300 Subject: [PATCH 020/205] MSVC & Win32 wscript fixes --- engine/wscript | 21 ++++++++++++--- game_launch/wscript | 35 ++++++++++++++---------- wscript | 66 ++++++++++++++++++++++++--------------------- 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/engine/wscript b/engine/wscript index c53d26c8..9c7584b8 100644 --- a/engine/wscript +++ b/engine/wscript @@ -14,7 +14,8 @@ def options(opt): def configure(conf): # check for dedicated server build if conf.options.DEDICATED: - conf.check( lib='rt' ) + if(conf.env.DEST_OS == 'linux'): + conf.check( lib='rt' ) conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') else: @@ -30,6 +31,12 @@ def configure(conf): conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') + if conf.env.DEST_OS == 'win32': + conf.check( lib='USER32' ) + conf.check( lib='SHELL32' ) + conf.check( lib='GDI32' ) + conf.check( lib='ADVAPI32' ) + def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) @@ -37,11 +44,17 @@ def build(bld): bld.load_envs() bld.env = bld.all_envs[get_subproject_name(bld)] + libs = [] + source = [] + # basic build: dedicated only, no dependencies if bld.env.DEST_OS != 'win32': - libs = [ 'DL', 'M', 'PTHREAD' ] - - source = bld.path.ant_glob([ + libs += [ 'DL', 'M', 'PTHREAD' ] + else: + libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32'] + source += bld.path.ant_glob(['platform/win32/*.c']) + + source += bld.path.ant_glob([ 'common/*.c', 'common/imagelib/*.c', 'common/soundlib/*.c', diff --git a/game_launch/wscript b/game_launch/wscript index 38e58914..9983dea6 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -16,27 +16,33 @@ def configure(conf): return # check for dedicated server build - if conf.env.DEST_OS != 'win32' and not conf.env.DEDICATED: - # TODO: add way to specify SDL2 path, move to separate function - try: - conf.check_cfg( - path='sdl2-config', - args='--cflags --libs', - package='', - msg='Checking for SDL2', - uselib_store='SDL2') - except conf.errors.ConfigurationError: - conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') - conf.env.append_unique('DEFINES', 'XASH_SDL') + if not conf.env.DEDICATED: + if conf.env.DEST_OS != 'win32': # We need SDL2 for showing messagebox in case launcher has failed + # TODO: add way to specify SDL2 path, move to separate function + try: + conf.check_cfg( + path='sdl2-config', + args='--cflags --libs', + package='', + msg='Checking for SDL2', + uselib_store='SDL2') + except conf.errors.ConfigurationError: + conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + conf.env.append_unique('DEFINES', 'XASH_SDL') + else: + conf.check(lib='USER32') def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) def build(bld): + if bld.env.SINGLE_BINARY: + return + bld.load_envs() bld.env = bld.all_envs[get_subproject_name(bld)] - source = 'game.cpp' + source = ['game.cpp'] includes = '. ../common' libs = [] @@ -47,7 +53,8 @@ def build(bld): else: # compile resource on Windows bld.load('winres') - source += 'game.rc' + libs += ['USER32'] + source += ['game.rc'] bld( source = source, diff --git a/wscript b/wscript index 0aa3d3c7..80c89364 100644 --- a/wscript +++ b/wscript @@ -49,40 +49,46 @@ def options(opt): opt.recurse(SUBDIRS) def configure(conf): + conf.env.MSVC_TARGETS = ['x86'] conf.load('compiler_cxx compiler_c') - conf.check_cc( - fragment=''' - #include - int main( void ) { printf("%ld", sizeof( void * )); return 0; } - ''', - execute = True, - define_ret = True, - uselib_store = 'SIZEOF_VOID_P', - msg = 'Checking sizeof(void*)') - - if(conf.env.SIZEOF_VOID_P != '4' and not conf.options.ALLOW64): - conf.env.append_value('LINKFLAGS', '-m32') - conf.env.append_value('CFLAGS', '-m32') - conf.env.append_value('CXXFLAGS', '-m32') - Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') + if(conf.env.COMPILER_CC != 'msvc'): + conf.check_cc( + fragment=''' + #include + int main( void ) { printf("%ld", sizeof( void * )); return 0; } + ''', + execute = True, + define_ret = True, + uselib_store = 'SIZEOF_VOID_P', + msg = 'Checking sizeof(void*)') else: - Logs.warn('WARNING: 64-bit engine may be unstable') + conf.env.SIZEOF_VOID_P = '4' # TODO: detect target - if(conf.env.COMPILER_CC == 'gcc'): - conf.env.append_value('LINKFLAGS', '-Wl,--no-undefined') + if(int(conf.env.SIZEOF_VOID_P) != 4): + if(not conf.options.ALLOW64): + conf.env.append_value('LINKFLAGS', '-m32') + conf.env.append_value('CFLAGS', '-m32') + conf.env.append_value('CXXFLAGS', '-m32') + Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') + else: + Logs.warn('WARNING: 64-bit engine may be unstable') - if(conf.options.RELEASE): - conf.env.append_unique('CFLAGS', '-O2') - conf.env.append_unique('CXXFLAGS', '-O2') - else: - conf.env.append_unique('CFLAGS', '-Og') - conf.env.append_unique('CFLAGS', '-g') - conf.env.append_unique('CXXFLAGS', '-Og') - conf.env.append_unique('CXXFLAGS', '-g') - - conf.check( lib='dl' ) - conf.check( lib='m' ) - conf.check( lib='pthread' ) + if(conf.env.COMPILER_CC != 'msvc'): + if(conf.env.COMPILER_CC == 'gcc'): + conf.env.append_value('LINKFLAGS', '-Wl,--no-undefined') + if(conf.options.RELEASE): + conf.env.append_unique('CFLAGS', '-O2') + conf.env.append_unique('CXXFLAGS', '-O2') + else: + conf.env.append_unique('CFLAGS', '-Og') + conf.env.append_unique('CFLAGS', '-g') + conf.env.append_unique('CXXFLAGS', '-Og') + conf.env.append_unique('CXXFLAGS', '-g') + + if(conf.env.DEST_OS != 'win32'): + conf.check( lib='dl' ) + conf.check( lib='m' ) + conf.check( lib='pthread' ) conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.DEDICATED From f15e2c2dcf76ccab07a429edd811ed7d7ac1d846 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 21:19:04 +0300 Subject: [PATCH 021/205] Move command autocomplete to common engine files, as it used by Wcon and may be used by curses console in future --- engine/client/console.c | 278 ++------------------------------------ engine/common/common.h | 18 ++- engine/common/con_utils.c | 278 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 272 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 684d42ab..924efae2 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -37,7 +37,6 @@ static qboolean g_utf8 = false; #define COLOR_DEFAULT '7' #define CON_HISTORY 64 #define MAX_DBG_NOTIFY 128 -#define CON_MAXCMDS 4096 // auto-complete intermediate list #define CON_NUMFONTS 3 // maxfonts #define CON_LINES( i ) (con.lines[(con.lines_first + (i)) % con.maxlines]) @@ -60,14 +59,6 @@ rgba_t g_color_table[8] = { 240, 180, 24, 255 }, // default color (can be changed by user) }; -typedef struct -{ - string buffer; - int cursor; - int scroll; - int widthInChars; -} field_t; - typedef struct { string szNotify; @@ -126,14 +117,6 @@ typedef struct notify_t notify[MAX_DBG_NOTIFY]; // for Con_NXPrintf qboolean draw_notify; // true if we have NXPrint message - - // console auto-complete - string shortestMatch; - field_t *completionField; // con.input or dedicated server fake field-line - const char *completionString; - const char *completionBuffer; - char *cmds[CON_MAXCMDS]; - int matchCount; } console_t; static console_t con; @@ -193,18 +176,6 @@ void Con_ClearNotify( void ) CON_LINES( i ).addtime = 0.0; } -/* -================ -Con_ClearField -================ -*/ -void Con_ClearField( field_t *edit ) -{ - memset( edit->buffer, 0, MAX_STRING ); - edit->cursor = 0; - edit->scroll = 0; -} - /* ================ Con_ClearTyping @@ -217,13 +188,7 @@ void Con_ClearTyping( void ) Con_ClearField( &con.input ); con.input.widthInChars = con.linewidth; - // free the old autocomplete list - for( i = 0; i < con.matchCount; i++ ) - { - freestring( con.cmds[i] ); - } - - con.matchCount = 0; + Cmd_AutoCompleteClear(); } /* @@ -1368,216 +1333,15 @@ EDIT FIELDS ============================================================================= */ /* -=============== -Con_AddCommandToList - -=============== +================ +Con_ClearField +================ */ -static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *unused3 ) +void Con_ClearField(field_t *edit) { - if( *s == '@' ) return; // never show system cvars or cmds - if( con.matchCount >= CON_MAXCMDS ) return; // list is full - - if( Q_strnicmp( s, con.completionString, Q_strlen( con.completionString ))) - return; // no match - - con.cmds[con.matchCount++] = copystring( s ); -} - -/* -================= -Con_SortCmds -================= -*/ -static int Con_SortCmds( const char **arg1, const char **arg2 ) -{ - return Q_stricmp( *arg1, *arg2 ); -} - -/* -=============== -Con_PrintCmdMatches -=============== -*/ -static void Con_PrintCmdMatches( const char *s, const char *unused1, const char *m, void *unused2 ) -{ - if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch ))) - { - if( COM_CheckString( m )) Con_Printf( " %s ^3\"%s\"\n", s, m ); - else Con_Printf( " %s\n", s ); // variable or command without description - } -} - -/* -=============== -Con_PrintCvarMatches -=============== -*/ -static void Con_PrintCvarMatches( const char *s, const char *value, const char *m, void *unused2 ) -{ - if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch ))) - { - if( COM_CheckString( m )) Con_Printf( " %s (%s) ^3\"%s\"\n", s, value, m ); - else Con_Printf( " %s (%s)\n", s, value ); // variable or command without description - } -} - -/* -=============== -Con_ConcatRemaining -=============== -*/ -static void Con_ConcatRemaining( const char *src, const char *start ) -{ - const char *arg; - int i; - - arg = Q_strstr( src, start ); - - if( !arg ) - { - for( i = 1; i < Cmd_Argc(); i++ ) - { - Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer )); - arg = Cmd_Argv( i ); - while( *arg ) - { - if( *arg == ' ' ) - { - Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer )); - break; - } - arg++; - } - - Q_strncat( con.completionField->buffer, Cmd_Argv( i ), sizeof( con.completionField->buffer )); - if( *arg == ' ' ) Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer )); - } - return; - } - - arg += Q_strlen( start ); - Q_strncat( con.completionField->buffer, arg, sizeof( con.completionField->buffer )); -} - -/* -=============== -Con_CompleteCommand - -perform Tab expansion -=============== -*/ -void Con_CompleteCommand( field_t *field ) -{ - field_t temp; - string filename; - qboolean nextcmd; - int i; - - // setup the completion field - con.completionField = field; - - // only look at the first token for completion purposes - Cmd_TokenizeString( con.completionField->buffer ); - - nextcmd = ( con.completionField->buffer[Q_strlen( con.completionField->buffer ) - 1] == ' ' ) ? true : false; - - con.completionString = Cmd_Argv( 0 ); - con.completionBuffer = Cmd_Argv( 1 ); - - // skip backslash - while( *con.completionString && ( *con.completionString == '\\' || *con.completionString == '/' )) - con.completionString++; - - // skip backslash - while( *con.completionBuffer && ( *con.completionBuffer == '\\' || *con.completionBuffer == '/' )) - con.completionBuffer++; - - if( !Q_strlen( con.completionString )) - return; - - // free the old autocomplete list - for( i = 0; i < con.matchCount; i++ ) - { - if( con.cmds[i] != NULL ) - { - Mem_Free( con.cmds[i] ); - con.cmds[i] = NULL; - } - } - - con.matchCount = 0; - con.shortestMatch[0] = 0; - - // find matching commands and variables - Cmd_LookupCmds( NULL, NULL, Con_AddCommandToList ); - Cvar_LookupVars( 0, NULL, NULL, Con_AddCommandToList ); - - if( !con.matchCount ) return; // no matches - - memcpy( &temp, con.completionField, sizeof( field_t )); - - // autocomplete second arg - if(( Cmd_Argc() == 2 ) || (( Cmd_Argc() == 1 ) && nextcmd )) - { - if( !Q_strlen( con.completionBuffer )) - return; - - if( Cmd_AutocompleteName( con.completionBuffer, filename, sizeof( filename ))) - { - Q_sprintf( con.completionField->buffer, "%s %s", Cmd_Argv( 0 ), filename ); - con.completionField->cursor = Q_strlen( con.completionField->buffer ); - } - - // don't adjusting cursor pos if we nothing found - return; - } - else if( Cmd_Argc() >= 3 ) - { - // disable autocomplete for all next args - return; - } - - if( con.matchCount == 1 ) - { - Q_sprintf( con.completionField->buffer, "\\%s", con.cmds[0] ); - if( Cmd_Argc() == 1 ) Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer )); - else Con_ConcatRemaining( temp.buffer, con.completionString ); - con.completionField->cursor = Q_strlen( con.completionField->buffer ); - } - else - { - char *first, *last; - int len = 0; - - qsort( con.cmds, con.matchCount, sizeof( char* ), Con_SortCmds ); - - // find the number of matching characters between the first and - // the last element in the list and copy it - first = con.cmds[0]; - last = con.cmds[con.matchCount-1]; - - while( *first && *last && Q_tolower( *first ) == Q_tolower( *last )) - { - first++; - last++; - - con.shortestMatch[len] = con.cmds[0][len]; - len++; - } - con.shortestMatch[len] = 0; - - // multiple matches, complete to shortest - Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch ); - con.completionField->cursor = Q_strlen( con.completionField->buffer ); - Con_ConcatRemaining( temp.buffer, con.completionString ); - - Con_Printf( "]%s\n", con.completionField->buffer ); - - // run through again, printing matches - Cmd_LookupCmds( NULL, NULL, Con_PrintCmdMatches ); - Cvar_LookupVars( 0, NULL, NULL, Con_PrintCvarMatches ); - } + memset(edit->buffer, 0, MAX_STRING); + edit->cursor = 0; + edit->scroll = 0; } /* @@ -2563,32 +2327,6 @@ void Con_InvalidateFonts( void ) con.curFont = con.lastUsedFont = NULL; } -/* -========= -Cmd_AutoComplete - -NOTE: input string must be equal or longer than MAX_STRING -========= -*/ -void Cmd_AutoComplete( char *complete_string ) -{ - field_t input; - - if( !complete_string || !*complete_string ) - return; - - // setup input - Q_strncpy( input.buffer, complete_string, sizeof( input.buffer )); - input.cursor = input.scroll = 0; - - Con_CompleteCommand( &input ); - - // setup output - if( input.buffer[0] == '\\' || input.buffer[0] == '/' ) - Q_strncpy( complete_string, input.buffer + 1, sizeof( input.buffer )); - else Q_strncpy( complete_string, input.buffer, sizeof( input.buffer )); -} - /* ========= Con_FastClose diff --git a/engine/common/common.h b/engine/common/common.h index 969a53f6..3ed50ea1 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -364,6 +364,15 @@ typedef enum #include "net_ws.h" +// console field +typedef struct +{ + string buffer; + int cursor; + int scroll; + int widthInChars; +} field_t; + typedef struct host_redirect_s { rdtype_t target; @@ -892,6 +901,13 @@ void pfnResetTutorMessageDecayData( void ); #define Z_Realloc( ptr, size ) Mem_Realloc( host.mempool, ptr, size ) #define Z_Free( ptr ) if( ptr != NULL ) Mem_Free( ptr ) +// +// con_utils.c +// +qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize ); +void Cmd_AutoComplete( char *complete_string ); +void Cmd_AutoCompleteClear( void ); + // // crclib.c // @@ -1053,8 +1069,6 @@ void Info_WriteVars( file_t *f ); void Info_Print( const char *s ); void Cmd_WriteVariables( file_t *f ); int Cmd_CheckMapsList( int fRefresh ); -qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize ); -void Cmd_AutoComplete( char *complete_string ); void COM_SetRandomSeed( long lSeed ); int COM_RandomLong( int lMin, int lMax ); float COM_RandomFloat( float fMin, float fMax ); diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index f977468f..6541c780 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -20,16 +20,32 @@ GNU General Public License for more details. extern convar_t *con_gamemaps; +#define CON_MAXCMDS 4096 // auto-complete intermediate list + typedef struct autocomplete_list_s { const char *name; qboolean (*func)( const char *s, char *name, int length ); } autocomplete_list_t; +typedef struct +{ + // console auto-complete + string shortestMatch; + field_t *completionField; // con.input or dedicated server fake field-line + const char *completionString; + const char *completionBuffer; + char *cmds[CON_MAXCMDS]; + int matchCount; +} con_autocomplete_t; + +static con_autocomplete_t con; + /* ======================================================================= FILENAME AUTOCOMPLETION + ======================================================================= */ /* @@ -869,6 +885,268 @@ qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize return false; } +/* +=============== +Con_AddCommandToList + +=============== +*/ +static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *unused3 ) +{ + if( *s == '@' ) return; // never show system cvars or cmds + if( con.matchCount >= CON_MAXCMDS ) return; // list is full + + if( Q_strnicmp( s, con.completionString, Q_strlen( con.completionString ) ) ) + return; // no match + + con.cmds[con.matchCount++] = copystring( s ); +} + +/* +================= +Con_SortCmds +================= +*/ +static int Con_SortCmds( const char **arg1, const char **arg2 ) +{ + return Q_stricmp( *arg1, *arg2 ); +} + +/* +=============== +Con_PrintCmdMatches +=============== +*/ +static void Con_PrintCmdMatches( const char *s, const char *unused1, const char *m, void *unused2 ) +{ + if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch ) ) ) + { + if( COM_CheckString( m ) ) Con_Printf( " %s ^3\"%s\"\n", s, m ); + else Con_Printf( " %s\n", s ); // variable or command without description + } +} + +/* +=============== +Con_PrintCvarMatches +=============== +*/ +static void Con_PrintCvarMatches( const char *s, const char *value, const char *m, void *unused2 ) +{ + if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch ) ) ) + { + if( COM_CheckString( m ) ) Con_Printf( " %s (%s) ^3\"%s\"\n", s, value, m ); + else Con_Printf( " %s (%s)\n", s, value ); // variable or command without description + } +} + +/* +=============== +Con_ConcatRemaining +=============== +*/ +static void Con_ConcatRemaining( const char *src, const char *start ) +{ + const char *arg; + int i; + + arg = Q_strstr( src, start ); + + if( !arg ) + { + for( i = 1; i < Cmd_Argc(); i++ ) + { + Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ) ); + arg = Cmd_Argv( i ); + while( *arg ) + { + if( *arg == ' ' ) + { + Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer ) ); + break; + } + arg++; + } + + Q_strncat( con.completionField->buffer, Cmd_Argv( i ), sizeof( con.completionField->buffer ) ); + if( *arg == ' ' ) Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer ) ); + } + return; + } + + arg += Q_strlen( start ); + Q_strncat( con.completionField->buffer, arg, sizeof( con.completionField->buffer ) ); +} + +/* +=============== +Con_CompleteCommand + +perform Tab expansion +=============== +*/ +void Con_CompleteCommand( field_t *field ) +{ + field_t temp; + string filename; + qboolean nextcmd; + int i; + + // setup the completion field + con.completionField = field; + + // only look at the first token for completion purposes + Cmd_TokenizeString( con.completionField->buffer ); + + nextcmd = (con.completionField->buffer[Q_strlen( con.completionField->buffer ) - 1] == ' ') ? true : false; + + con.completionString = Cmd_Argv( 0 ); + con.completionBuffer = Cmd_Argv( 1 ); + + // skip backslash + while( *con.completionString && (*con.completionString == '\\' || *con.completionString == '/') ) + con.completionString++; + + // skip backslash + while( *con.completionBuffer && (*con.completionBuffer == '\\' || *con.completionBuffer == '/') ) + con.completionBuffer++; + + if( !Q_strlen( con.completionString ) ) + return; + + // free the old autocomplete list + for( i = 0; i < con.matchCount; i++ ) + { + if( con.cmds[i] != NULL ) + { + Mem_Free( con.cmds[i] ); + con.cmds[i] = NULL; + } + } + + con.matchCount = 0; + con.shortestMatch[0] = 0; + + // find matching commands and variables + Cmd_LookupCmds( NULL, NULL, Con_AddCommandToList ); + Cvar_LookupVars( 0, NULL, NULL, Con_AddCommandToList ); + + if( !con.matchCount ) return; // no matches + + memcpy( &temp, con.completionField, sizeof( field_t ) ); + + // autocomplete second arg + if( (Cmd_Argc() == 2) || ((Cmd_Argc() == 1) && nextcmd) ) + { + if( !Q_strlen( con.completionBuffer ) ) + return; + + if( Cmd_AutocompleteName( con.completionBuffer, filename, sizeof( filename ) ) ) + { + Q_sprintf( con.completionField->buffer, "%s %s", Cmd_Argv( 0 ), filename ); + con.completionField->cursor = Q_strlen( con.completionField->buffer ); + } + + // don't adjusting cursor pos if we nothing found + return; + } + else if( Cmd_Argc() >= 3 ) + { + // disable autocomplete for all next args + return; + } + + if( con.matchCount == 1 ) + { + Q_sprintf( con.completionField->buffer, "\\%s", con.cmds[0] ); + if( Cmd_Argc() == 1 ) Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ) ); + else Con_ConcatRemaining( temp.buffer, con.completionString ); + con.completionField->cursor = Q_strlen( con.completionField->buffer ); + } + else + { + char *first, *last; + int len = 0; + + qsort( con.cmds, con.matchCount, sizeof( char* ), Con_SortCmds ); + + // find the number of matching characters between the first and + // the last element in the list and copy it + first = con.cmds[0]; + last = con.cmds[con.matchCount - 1]; + + while( *first && *last && Q_tolower( *first ) == Q_tolower( *last ) ) + { + first++; + last++; + + con.shortestMatch[len] = con.cmds[0][len]; + len++; + } + con.shortestMatch[len] = 0; + + // multiple matches, complete to shortest + Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch ); + con.completionField->cursor = Q_strlen( con.completionField->buffer ); + Con_ConcatRemaining( temp.buffer, con.completionString ); + + Con_Printf( "]%s\n", con.completionField->buffer ); + + // run through again, printing matches + Cmd_LookupCmds( NULL, NULL, Con_PrintCmdMatches ); + Cvar_LookupVars( 0, NULL, NULL, Con_PrintCvarMatches ); + } +} + +/* +========= +Cmd_AutoComplete + +NOTE: input string must be equal or longer than MAX_STRING +========= +*/ +void Cmd_AutoComplete( char *complete_string ) +{ + field_t input; + + if( !complete_string || !*complete_string ) + return; + + // setup input + Q_strncpy( input.buffer, complete_string, sizeof( input.buffer ) ); + input.cursor = input.scroll = 0; + + Con_CompleteCommand( &input ); + + // setup output + if( input.buffer[0] == '\\' || input.buffer[0] == '/' ) + Q_strncpy( complete_string, input.buffer + 1, sizeof( input.buffer ) ); + else Q_strncpy( complete_string, input.buffer, sizeof( input.buffer ) ); +} + +/* +============ +Cmd_AutoCompleteClear + +============ +*/ +void Cmd_AutoCompleteClear( void ) +{ + int i; + + // free the old autocomplete list + for( i = 0; i < con.matchCount; i++ ) + { + if( con.cmds[i] != NULL ) + { + Mem_Free( con.cmds[i] ); + con.cmds[i] = NULL; + } + } + + con.matchCount = 0; +} + /* ============ Cmd_WriteVariables From ca28332c6c8116fb6ecc6eddfad48c85a1aa3e79 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 21:22:17 +0300 Subject: [PATCH 022/205] Use msvs.py to generate Visual Studio project. Add debug flags for Visual Studio --- wscript | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index 80c89364..679c8dec 100644 --- a/wscript +++ b/wscript @@ -28,7 +28,7 @@ top = '.' def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': - opt.load('msvc') + opt.load('msvc msvs') opt.add_option( '--dedicated', action = 'store_true', dest = 'DEDICATED', default=False, @@ -84,6 +84,11 @@ def configure(conf): conf.env.append_unique('CFLAGS', '-g') conf.env.append_unique('CXXFLAGS', '-Og') conf.env.append_unique('CXXFLAGS', '-g') + else: + if(not conf.options.RELEASE): + conf.env.append_unique('CFLAGS', '/Z7') + conf.env.append_unique('CXXFLAGS', '/Z7') + conf.env.append_unique('LINKFLAGS', '/DEBUG') if(conf.env.DEST_OS != 'win32'): conf.check( lib='dl' ) From e70dea54c0c303add3905ba37b96da0ac9501c00 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jun 2018 21:23:38 +0300 Subject: [PATCH 023/205] Add waf itself(with included wurf and msvs.py) and waf.bat launcher for Windows --- waf | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ waf.bat | 97 ++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 waf create mode 100644 waf.bat diff --git a/waf b/waf new file mode 100644 index 00000000..9178805e --- /dev/null +++ b/waf @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# encoding: latin-1 +# Thomas Nagy, 2005-2018 +# +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +import os, sys, inspect + +VERSION="2.0.8" +REVISION="1f8dadaddc20912ba4dd275428a78e44" +GIT="80aba755c114c6b14ba334483c2baf30e579fe96" +INSTALL='' +C1='#<' +C2='#;' +C3='#)' +cwd = os.getcwd() +join = os.path.join + + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir, src): + f = open(src,'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ('Tools', 'extras'): + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + try: t.write(txt) + finally: t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + try: + for x in t: t.extract(x) + finally: + t.close() + + for x in ('Tools', 'extras'): + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.remove(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) + base, name = os.path.split(src) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in (INSTALL,'/usr','/usr/local','/opt'): + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir, src) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + import waflib.extras.wurf.waf_entry_point + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SY×C9³îÿÿÿü‡ðÿÿÿÿÿÿÿÿÿÿÿÿßð¢„+20T¸(bx\=Þo¸ðâ#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)ïv}:öšÊ5•ªÂ»¾ïGmk-жÐ6ÙÛg¯¹§´Ùššª}Å;Yh­×¯¼;ßg}Úݶ¹DaÙVTÚ[Ož>žùóÍî2‡å0Ø>÷w]ïs¾ž¸#)>€ #)h£@‡J“_`5]ž¼‹›vÌ#íöw‰©®ûÆñîÝžîw®Ûn8˜ëï½æi³,ñ÷C·»ì÷y§mj=ç¯xë[ë=¯{ã½K;–äW;…ÚÄ×]h|îãÖZ„nË3­·a®”Hom×\žÞæ|#ÝËOM}÷}^î­ÛcO–«Olƒ³Ö“mÎÝ´v­k·jo Ø#)®¯V;¹ãÞðzÙßp÷¶ç¼¨;bŠ#)TØjŠ•U(‘J…žµÙ™1U±Rµ;.š}·}öŸSèöôq;–mÙ¯K{¢#;¸åœ™UíáÞ²¨®÷µì¸: ëk#W«tð#)·[«#)*®‚÷«–çÝ®{äï·Ü|ûhï'[8š2nî9mm¯§öùqññÒæ9Šk§E>mGCm¯Y£©³En»@ïn==³‹ëÙ=Ú;w5­àVÁí½Þë½ÞóÞ7†\š÷y=æu;«¶^ÁUv÷¸QŽåZ ç}o<û!²u5³oq×¼V/p×¾Þt½³]»œ»>7®=3uÙëž¾ûž¾l“6À,U¶¨¡ôé½âínÝìêPæ{ÕvÜj[ÛGfµ$?Æ• 2¢ò#)0@ (:oŠH,‚Ä‹Ý)hR¦(™9áç\ªÍ)ÞñJ¬ˆÆ±Yz¶{¸0äáE”>*fp=+»k¹¢Næ0÷4êŸiUPïªL'ŒDÉQx0‘UQŒ³˜ `7ùW=kçûWt·¦)•&¨Õš©m)£%+º»Z©µI­£mljɶ¬Ím¦mªÉU[تÖõZ©jÛr¢´+¤–¨‘#)CCJ`%$VEB#)2ˆ¨YP‚#;ˆªòˆ4EK#) ŤÄ-e ÑlV6òêí±]×-»«kV­ï­k[QS*-2ÌɘbH-d„d ÌÔ@)M6P$5C"fT$6H R(¥¶M…RL±´P`‰ˆ%+ME¢‰$Ú3Id¤i¡’M¢!$F"DÔ¥€F¥#)i‹*mH¤Ù-Q%,´¥fÔ˜€@… -!i£#)°eS1ŒÅch«&ŠCI²ÂBF¦2P"ChÒËI1F”¶54ØKiZÙV¬3bM™&H”EK&¬ªË-52’²™S[µ-³#;L´™ L™#Dm#))2Í´ÂÆÅDQ Ó#FȘ´"A¨³HÖ4‰B&‹DÁ •2#4¨ˆ5)ˆ(Ø„!A"$¢@ËC$Œ’+H™¬±BÌБ#2˜H†dÙ¨ËFf$@ÐY%,ÍdØÆÅDÃR,É,¤$Y-4¤bJLŠ2ŒšLšRd˜5%D¢,D†$ÚI%"¨±$XŠ&dM¤%…1“b#bÙ!‘b$Å4Ó#A0±¤Ø &0Ø,!, &“bÒF$ÅQERE&Ä3FŠIDÄXL ¡Œ’’#Sš$d¬h´Í4[ R‰©„I1„ÚRÈ I‚Ø€’,³(¤Y’Q6#;"MB2’™ÒHÍ‘0LÕ4¢HF” ³!RA¦ÅE#,lÊ“T„¤SdÌBMHˆ›M(ÀÃF£H„’Ó%J2ƒ“4¤2E%LÔlJ´#<ˆ(’Ê#;&2#¿õuéÐåON•œ¨C¬W¿f#)GG[ 3›¹uKc›l¸û¹ã—DáL®UÓHh¢®mÓ×q£b¢}믔ñX´E,šUIïªLø¡ièaH Ä7iQ¹Ò'ù#;Ö¯æáô}]­u,<<3=í<ɘŠÍ³O®!¶Úm&×áó­{«Ò÷U\¤Ú3ºå8›]§<Ç„##;êQ•¨ÖØF†Ð6ÜMuÊï;±rŽ•Ë™/•K™î¿ QMs‡&…Õˆ"-´ÄTêÈ{qfçŸïKÁ²Q8°¤PŽÅ"(ˆ[BSãÆ¯FµMFZ±`¯²ŠDõÔÓô¸ÓSó=ØËÁJÿ‘AHl”‡=,µ]B¤õÚÿ ^©Zý_[¯‹öWÁ £c<ëçÀN9 c®kå‚ï–Øþã&`Ý,“ŒB0íU¾lˆ"žFšö[ë±É"ƒ"™½g¸ta_™Ùqûì³Äü2œAÉժͼMFp°ƒ²-0l#*S4òþÐæÉ­53ÌgÓ8p®ê÷²˜<* ¥>îŸ]úó/ƒØñ{85Ázs÷m¹¤g«–¾¯³º“YênÖ›¦tj#ן³æ×¦Ó0b’%58•ªÂWAÆ”k«^Ü€`Îåç{.ÍK€l•®kšŠÔyß^õêò!…x»)VM‡½Ü¨Ôk÷²«òÏ÷î/‡v/Ç×·ÀB˜,ñaJ3¥×Ò":ÕPµ)±´XÉWÍ÷W’)-d&Í#<¨¤Y4•N©ë½D8â»t Ý& |ÍN5”–À¦’6 -”ÊbÅ»79:!¯2ƒTF16­lчؒb ƒ4iøÝ?nVfQ²TSí:ÉBy!K»PZI]xUÇ#;A" µ¹].mñxµçW'.˜9[êx=dbFÛë^|=Ï9dQ¨4ج•”Šõì«YøÝ³ÅÑ7×ÇC#Ô+òæë 8“~ø¥­m_ökÛåååXªJLÂŒÈÁ{[´¨tºó¬#;Á©*¦Á§$Eÿ1œó¯~%ö{f{"]/• î›Ô<Ú4þ©'œìÃn’8ÅèÊ1£€ë‡Dé{_²L¿eÌ„¹t÷M˜çÒê|ÜpgüOÅÖä†ì™âq·z+:²ªñZ$ÓvFM4KÆÓðya² 'wÏ-Ç)ßö,!c÷ÌÞ¯ИJ_ÓRà™‘º)ŠXú¾QP竺Àâ·Wß8¤#„ãÂQA"â9-kyM DAJæE"‹{¾€ÏJÊU ëž+’ù§T-LêNùœIõøºGëåØ @É çA÷lÅļ‘ú`x/tLäê}yß}ÒjD46¢È¯FŒh¡úOHÑÄšŒou[¼µºâòdM—©ò¤xÞôÃ<釥 déK‰ë.JƒÊ'ÏãÎn¯jwíÁî_HVñdŒá— ­¹á­»{ËŒ®O½R4?ªc,]°Æðóëq½BªÔká%È[:xUæ«eCšP“.Y–¡tÅú.ûS"m’C÷´'vG^8ª\8è)s¤EWiŸ«ðó£Wz^û´;é’ö|‹+%Áê%#áÁ´ßl#<{ºAq‡guÑèÑçÒ¼9mŽã‘HjQoP…êä/8r” J8ÿZ‹Ûf66Í·mV6¬‚cȶ_§àû¡"U}c™JNm—¨óÚLG»‡?:!|]ÎH3U"&é>é8~Ÿ-l<öú‚°ƒ,M¨xxT–ayµ|"3n#;ƒhvayÐÞ+¯;³ên2¥/µ×2&¨‹¥#õ°àÛÅ&¾»•äÒ­U)TWûm#<Œ’gÎÿouaø¿oÜíbt¯ÎO€–"*/Ìüø£Í}«îaõ%¸ñß]Û~nºñ³êë•û¦ú'°÷^b¿wvÚŸ:«¶¸é½YŠœÑÚгF£µ—Eh£áE–f¤Gx1Çs$´”Íüà0¡JÛGó†Ð¬þ#¸þÎmE~D]~é7Ä':':¥ðÕç=2>rÓ¨„Ù¾ØTÎû¼‹ÖèßlëõžRÜÀ£…ŠŠþTÀÎÙ¸¾§#;ÅàUУ‹#IÛW~\êðÔÖ¡Ë®NÝëRÑᆭø^àƒ ˆvtº”;8ˆÏ^“Õ5UTœO—«î4zN¬­síV±œ¯Î#±mwÿ³µ­êNRÌyxU>yÄÓ #²œ‡ßÊ&_¤[ÏJzeÛ¤ÖÑŠmÔ$É„%Ö¬xm¥Øe[­åò4ˆ«Ä^¢üQ›q’SQû”ÿ–§·<žêõzç¬ïÁä;² v_ ×HÕqiŒ;&ð¶ð¯ÞO—é´ÚtŸÝ#)瓺E/{™–Ý\o-.`O™—¾ ‚¬Úæð¯‚¨!OÈ·w\|¯‡Óëá›Û$B7;# :þYRշªô³|Ê*‚Ž©ã¥a žÈF»µ_+ÑÑþ]¯†MHdò©A¶ÚϾª$yó•‡Hº½äŽ;Ò¢³†)žêx<\øUùç{7y26Ø?8Žãúš82•"#@¦(´V¾¬N|«:'?Ó­8T4`u"Zx°¥Ë)C©ŒêדDM"…¾<óçîê?^ycÝéÔËJ2‘J;þ¾8ÜåÆ%nþ/.º–j2ênt?¥qûzšý©Ðñí:$»p¼^A‰¥Z„ƒpUÏȳ+1*¹ì®Ò¼UMipñ#;  Å#;)ðm–W¯5ýpZêìè^ïH„Èø-1Âæ ïòz™Ã‚ ‘iK‡&±t€Ÿôp½;®wy×DÃí®eO}ã~#;Vyö‡ƒ_Ñ–ø;¿¯¼Þ£F+eq-'”³¶6:©P­)B(÷ä1ø'?­Jm;ÊmÄÛ/4ôíÇG3ô‘à¤r¶Ë„ô¸·tšS$Ïæá=pðÅ%úýcoaÙ׿IjM¥;ŽJ´nû'gæµÚÇÕ¶Âf”ZÒ MBиwo×§ÒÒöÏí‡4á²ÈŒ|ÜG#;’>¼ä8é:ˆm#<“8Ü 1#õ(ˆ»bŒzªR¿Ãû4×ýmGAa=V €"5¯)|üÌŠ¼‚QhxA¤¨vòPƒ©ŒÄºsØw1ˆ>ÓǦcNò¶ß– p‹³Z,ª£Ž.OˆêºUo³‡Cïãž·c{*–puNÔö~~UÐE2t—á€?)ç±×r"P±‘18pÎÓM‡«óaç½q‚écm\p¼H6äSRt+åU0D}nZ<ûÙàî©ôíb6þÎd”‚¢?DvMx[ó³å#ó“Kœˆ6Ü|ä1çÑZhèÆÏ;ø+/ÚéèEé`ÛÌ´¾Þ•xæô™yªÓv6ý¹ñ¾ëCçwņ'“Ëœ¯ÍÜ„ªIk ½sÚ4›¹s¶"×›‡ÄFžJÊkAê›ôTŠNNœÒyn°y¢>©Õ^`¸Þä¶A(f]^IÙq!·3]õç#;³%ÙÛʼ‚0{šMxbÉZâÌ ìÍ—¯ñ}88>F´v³h*1b,þN™·±*ª'­µÅKø´[ª‚Ë¢¾Ô Rm-rhÌ"áŠxv‡×?d?zñõàÊüsÍ-dIH‰B‰õ&ìÆ'&ÞzûÉñæ.EÛîCá1áãg³Š\};vv”…gLm5n¼uÙØjÓ€øøÌJÕ<.jˆ¨`‘…¤èƒu™AîjÙ]Rše³–ÔqwéƒùwÖ­‹[#;‘”T'#àî„Ô"P8! X¬CùÓ1R®¥Ý‹qVË©G³5=)LÅ^pNn¬Ë#<ûšê?'N¹³É‡j``Úé$LêÑFŠÛHœIá#LÇn³1¡fªk»ì§hšèíanýœþ#ÿv—Ê‹tÛ–¶ÈU‚±4HÊ¿•¶éŒƒ­lœ|Ú(éÊFš•BéEÝ"!åÛæÑ*uîD„ µÃúÄ%GŒX¿¹m=æQu‹3¿6h˜tA×ùìnÇ´‘\ [ùZU{#<ü¬(3ªA _q®’þžËdã¹J!»™XŒCU‘ðüfv±ÀI ¹w¡Ï¿ósÎ]¹Ì@_ÙÛi¸â.ºSäãöbèÌì÷k7gÆSçÓ\úw•Ó#낤#;³ë³·9¾,ñu'µ)€¤ø°5BÐÞ¡Ñߌ¡c9ÔÃ|*Ã'¯^½ ÁÉœDRÙøÿϘXânÊRD-/:™ªñèYŠ `6oݾï,è ãQ³¤#;"ëUî™eW‰Áo@}q¦X˜¬mN½éN»ðe6ÈÆ×+;:îÙŒñmìaÃ7?¶í‹*Ù›kR?“+Ä,ŸŽíߤêùf¹Š>=b*<µ[5.#<Nèí•ήZäK³ƒèC˜Åû÷þ!ÁmZ’<8Ú\^ØèîÐ}£2§ü(ô𻟰×>_(ÇŽ'ì~#;]?5é¬ÔþO¦Þº%â²–ˆYAÃøF-á8ºjx"pg#<35K3Û'2ØÃƬh¨ðïl1¹z4^×gr³Ö…×é±³t›â|u¾ÀDyU¤º#<Š+È™ "§Æûß•håÇ\/Uæ¹µñß:ŒbªºžWÔƒÅȯŒÐÌÛq_9ï¿e˜°ñIÚÔ……Ì`@P(/h OðênÿOäOÑ_Xßà¹êÙ'ú¼Þé§ý Øÿ©ÈáŽI9Éþð%껓¡†ÞâŽL¶C ýÝoGÁ?WctM>~#–© H=…ùs<0yAn#;m(ÿ¥»GüÖêp²U®#<*èªÒÕ¤ ñÐé8s^èËgL,˜Pˆ9 dŠ@ˆÀúèÿîQÝðÎÊœã¢A÷ fJâAÌàXd¡8s¥Çàéïä×e5äJ½Ö„2u³Ûr#;mQ€c@¤ÛDÉà:±Óê˜òA &ñî¬à° ²ú@ßÇGMc*9i}È%|áÑ9›4ªÉKÏLWýl#<À¥yQÆwÚ_ú ž“¯71ÕÛhå}Di`çÆex¾Ý*Ïí`—zó}¸#<Âv-ÓJ—jñß5åQŸ¶¹Gm_Tbv¡ø9èt{Wl®B `æã~E®\¶€uŸt2‚>Œ…EÚ¨^£œ0»L]V³SŸ­_H2=1zm×DôÁí#)±š+EÕFñ.™T¼EÿW§å”ºž\äÌ}1›òòôÏ ½5IC«Ã¤pÌQ⦠ۗñž@.¨1P£×;!ªÖ¹.ÅXïVÀˆOU€â(q ‚djdû.Ièåï¿Öu|GÏËÇ©Eèç²;¢o§ï¨ù–ûx:Y`¸ˆXª kaô”ß j(ï5q>­‹s•üNŸE›9.`ýPÛËv_—†~"&l È•Øî(ûì¼,¼²?çÔ©Ã_=xýïñN|¼Š÷‚×ÂC ب -ÉÍaò²†H€¦iŠÝ×õ#€™C t27AýÓ|ªúˆsžn\G¢rãÓ]DÓÐî_°#;ýzÆM¹)Çáýû„ˆH’àfÏ÷¾£sΆ*p±Æ+ÃÅ]uGóÚQ’'a_˜;Šî%yfú!ä<¿;#)”Ä8ëÈQ|ÜŠ6뺉&âÒÒ<^Í~OW·eƒ£Ñ¬#;Åá@D¿*€¢;¹U|Ñ\"=Å:¾Å©'^Åcx´Èv>g²Wr3¸»\@$A !Ú# BªÐÈÄ«P*¯ýËXì¿ö‰¶øEÐJ¡(H1UŠ$€–Òÿñôéêÿjœ´·'gÞìã¢à÷S»5ãŽÙ¹´³2ª³3)*¥u%¨@}ê‘ñ>U¹'Õ’nWŸäI¶dˆC3û»tÁ̾ÿ‹çïŠtã«”ª”ž›»#ŠIELŠ1B3ÈúO¦Ë"@ .~Û#;šÓ¡O¿©¼éGô5¸þÞ³®Ff‰BÒ>=Xø]?OkñøbÜöí4ä(-·$,™$€ôF$o·¦?§¤ÚOxÿ"ÞŸ5òãyµjÝÝÿƒ¨r5&ˆ&À‰„CTtå¼£ø_1™æpy|nL}H¡·2D%ÇlUjô1†¤MªñŒŠ%O?Ïþ;æ!0õ’0_ý:O<˜Ÿ”Ûù„Õ…ÀB(åvpZy€–˜‰áTlÈŽü’ ^È$tOøbçŒÍ°s›äÚ>oâþWüë÷÷ÜoµîZ ”;u‰pAv/¡œ4Óu?EÔGÚ»ó¥ßÄþÝ#) ÔÊNƶkè|°ê\åWž9óáû¶à{v…ŽºÛt ±ª¦®²6áp ä.nçÛãs>õG–‘`$¡6ú_»@6Çò hƒužåQHé‰mÒµ²ݶã¢É×bà1g¸#ƒóTøÜ HÏàuŽS»ƒ¼üP #<9Wm>c«yð3¼#;ë½çà?Só4Ý4 f·‡PʘfwÞ¥æzsï˜]ƒ3ChZqø†“Ögë¬w•*mBò®›áàaÒ`Àž|òÌHðòêÉôß¡²r´Ñ×Ù›Ñ䔵ôwã¤it€,Q§£‰v?ÕünZC›”äÍ=ôÙÂOßåÇè­¨ :z®X»U ˆˆâNw>äØÄÇ„ÒÆóø=“~)œ4m™#)Ã驳;‡Ûäóŧ×è’ÁÊl6ÞÐU1ŸÆzbÂÆ†}dùü¼ñ„‡Ç󾦯ÅUˆ0ì”®øÜqââi_­Oò-`vHõ‰M™ÇHÞØ ¤­Î¯Â,§Ã´úbOti6,##;=ú~7ZóËk[ÁÊ'7#ê]1M?dZ­Èþî‡cMâÉ#;Úd$ŒÄ!MeÁ7ÔüöS-Ô¥$v^Þ¼?+ àl£¼ï´Œ†Ûƒ:F㉄ë ÄÜÃE¡$9M€NO"Á8" °¼€€#)á=’䕹ºE×{lz›y0 aJ~Ѐ £’;‹F„Ó15vR_[ýs{zsN0ʯª‹ Ï×sñzàfrfŒsôóñ¬bMúM7Á0oè²Õ[Û;/#<ª`(©rûã zå>ýRØþ¡§¿K­kájpu¤^Ï®Ëk-xAÒŠQéÚÇ!ÄJ$§ƒL‡aºqáH¶¦ ©mV’KDáUKtÒ”“ÑÊh}¸q±ùíøè¬îç¿$O®Þ¼V c‘0ÑbÁ\☬څ C(Uغ‚c}%-Zˆ*5g^d9WûÇ”nÞ£î¶^ætyõ–¬!*^…Òr)L]p©AÞQäš™ÑQAB4–{³‡‰“Nwkd´BÊ#0+C›9°Rr~¦V\¯…¨#dߢV”³}‰4G$ŠiMeA%±¼ ‘MëÚy~N¦ØYwTã‡éë=õØÄ¶ðFÊd#ËØÅ_(äõ&w¡¡6Ï. uÈe#;PnQ o!(Ä95špºI:µ’(¯ŽÓ0ä¡#¡±-¢çg¨JaÍÖ¡(yÖw†.¼?S˜RæSaHÍÊ Üs£ÝÌ|0–9¯{žŒnѤ$qJÇWaÑæÓì#§¡[y´qŽ8,á!’Þ#<ðÙûÎÃuòßÓsÍza¶íwL^¼Ž·UžqVY6ú²‹'Æ^ðë·gÔÑäYêgÄ32¥S>#4ˆ«»¾#¢D±#;:“HcÉc |!Y¦#;ÒϱOSµDëÙõ¬/óF|¶Oa¾~O±Vµio8Ç'g%È yÎ×Q€ùàôÞ‚.0ä()70 P_z>¤jŸs²»«Ù¯yÝ$aõÝ®ï<˵ť㷮wf1…©B(S%$‚áv¿GM´Øô´#.ýöi›û·Š×+ëR½o 7 }Ë„¤I¡64Ñ#8“Uyñ†YÊf›+жÃR"HÚ3¶vÅÁ·Ê£#½%¤ŸÎIÑc±Þ•íw~rìÕ«ë©V©vOjÃ#;ô^§ppˆw¸CŒUÆØ;í›dØ·µŽH©´¼b¨Ä?3…Þ¡Í-øsÍ (T)‘h7ÎxÉ«ª#Þµ±Kw`P)y÷À;MCÊ^éÍjû¬†Š 8Iú²â€±°œü« ÎGèÍšáˆ^d¿ŒÝ±˜"·þôZ°´œ`{U3``{:N¬ÍB\åjØíf)h¨n”Å6LÂVñB¼'œìT&ɾJÏ&€0Ôe¾ú-¤Òj#;[¶ŠÀ¬Q a‡ÎÑ¡+D@ñ˜¢´º°1âÆ#;!3e#;¦ê\Hp¨v @ĉ˜7®ùÛ‹À¬J?‰çEGèÖùôߨßXRACd’˜,P“Åtµé‹WÍ^{öÛ˜"Ö6RŠŒj4Ñ–½’îÕî¡"ÁdPÊBÚIªp’¦4ÀBfµn±´”ËE#< c „$#)Áݳ=_ÍÓ탌Îá$oùù|2q0Ëc2Àܵj qqÖÜ¥Ù‹#;'6©Úz%#;Tuà|V&ÿg^]Mv:BT——qƒŸì×Ð6í¾·–;óѽ“:Y‡{`0#<íÁ»~èÇÓàzë¼Ü2ìÛãOÝLñ­GVôáºÐ9ÿ7»|E*á“¥¼{#[†B䚙ݏµl³“ÀÑË,ué¥RÌà5|òìîþ.K3¨oò<ص³ï±xûó+³Ä©ÀW® âh{Úµ¸åìõyžÆoxò'vDš8AÙcÚŒÇ-Œ’8àóXnÊ0ùl<®xxþu.[Zœ9 úÂYjëT†‹[ZàÆ [g®.ü^wç‹on9øl ¾/PNs½è±ÊÓS¼Iœ™f×cUÔ0 œÂ£È$Dx<½Î|@žásQçFÇÍõ|ç;æ %¥¥añâo…ŒclJòC¡.K°.ιA5ª„ÔÝOLi•Ä×ZvàëlȦD/³*Ò¸Û[5¿ß’^¥ã¸8JNšá=‹–tž‹‘ÿ‡ƒÀJnÏÊmµï—]Ó0`#;_¿sYž³)$”Mtt¶‹úC5ZJŸ¤XBv1NcGç³Ö ~—foâYe¥QX*PdaÆÃ/#Ã;àœu#WTîcÆOoi„ÿ 6¶¦äpýÊ ‚hñwvôŸåx§é¶AŽ”íHFGs´Væd6ÓmÌ[.PÄ}$…¥;¥ˆ#ˆäpù4>™¨ ^Q@ÔüÅåð/:'$\ÿ/–KwZ(“chCiŒ¢iF€{³$Ë"c(Óte)­ÌŽhdLUŠ-¦#µ’Pȳ IÕÖ‹hN¬ƒikàªDŽQGÆ66¢f!†·ìº5"`†ÒŠ0å–º`S† ,¤&©HÜ ;0Û2QªQ:RÒ±dt(ÂhÜ’3#tT˜j jÃdÆ#<”è0¡@ÊRJ¥De‹l†‰+ ˆbÓJLื¢>žO{­ü5m˜hMìnOñ(¶Äsƒã4É!²ª‹A­’a˜3¼?M²¾:s¨ü^IAVQ¤`íBŒ`l.èÝìÏÞÈ¿#$®®Û>g*>T÷bP`‚ ùã〷¶ÃÿlÇÁn' ‡:JÊñ௶-&#)jÙÙ7û¸}fäJKŒB"È*Æù=ÖÄ]œ>Zí™zsë°Ü‡q¯…àÕð±×#;ŠòNMºV¢YsÒëKÚa©.&ÈZ– φ¦6ÑdÒ³8¥„EˆÉFnZ×M¤‹\Û¦Ô[s[tÕš[»^Îïi¯;Öé!%¥–”ñÖQ`"7Ò5e"ê¸0l#;²PÉkòÍIÛ9ß¶£ hŽžˆZèî R@áE¼ÃÞ2)B%ÛÒ¡â#<-²åQßõâÙéTŠÎ_m>]+wì8ÎuW>³ë> ~n z óŽ ýõ‹8­ß-áï/YÛÖ\ôÒ¼ÿG2•õÆÖG/² Æïsååêà}pË0çRìÆJ5ÙwÄeQjd]#<‡Sà¿ôn€êÝŠ¤‘̪'Ÿà?oÙ¿»k£¶©W¯ÕêQª<Ì SŠJ‚%5ÔòdÒ«‚c"¤)wDQ~ÕIòŸî§aÑ ç!­SœÜîæFæôKçQðöÃz¢Ï³èܘâ2ßg²á¥p;<#;;S{Na,1¨‹#ʸ5 _)Ÿ%8ùŸßâx]+ñ»I@º¼jâ.Ï:ŒRY—ËâýRéîîä #{r#)*‹sý¾ yþÇÏ ·o5¾ûCD#)çs_…ý\÷×ó[ÇÔ5usGëÑð˶¹]’sœö~K€åöG|7›ü<Ÿ‹ß•°ñà.“jí†(¼r·$‡!ñò&ç#)óåvBÑà X[)=ØŸœ3Ï™À6/#<â˜qåóxbn û6GXÆ’zå*ãx\ Q’]'M>äý·å0ùG—ÍðâllwZ)N=”z¬õÝÊli463n bm;–240e¤) 1B¤vú& âFAö‡GÑóç§nGŸÂ*2ÊJÂûXè(_Ê¢]¿à¨e+Æ­¡M4O¢e‡+úã•(ÿJŸ…ª®? ˜õ{Ë Ðë²êÁµoúô~ìÿ†~çý1´Ñþø¨þdŽFêÿ~9€q¦Žø#)ÉÏO©–œüÿÕà—„#) ΊQ@d¥ý:¼÷_ 1Ì;Xõ£àÇíOg/_˜DƒëÚƒÊKÈJ+¿Šß¼öÕëz툰¨QlFz­S6¤²h“²¥‰Eùœf&6*þ}z;nÔQ¬·s®Ý-r¼;+éo¹IJÓè¤OTJ€lûICÓ÷œ2¶²…°"à#;Þ´ÐPG/µ¿€#<$Ý]âë_Íüã`ÁÓgœ6›dR•뼨 ;%iÐú˜X@ãÒû~}~\öî®jæ‚.UÝvºˆ$¤”©÷UÝ Õšóýuúy~ý¹ôJÁE”Kª*§'øêR¤‚!ötøÛÔùñ¤ü+)¥mT0³–ƒ) ¢1BcJH°Xw½Àß奟ƓD¡¢0Gy\¾¥kðÿ'«ÙFÐVõ*âoAd&é#)±$‰’¨¡=ˆÃ÷fßQõh~^Q챪´ÉiÆ"Ù†Q(‰¢ª7R¨”¿}äþ×Í&{å©ç®À ®+ýEsp[زhoærYKCø2½9ŠGøáOãjÅ‹çƒà²Òì˜Âä…µ–K½ž­[?ä·3†å`ïPâ;à"«cã< +žŽneÄâQ=BõP¡ H¡Õд+v–œü종,`6SÒ÷St’#;uXƒmÆ6–}¢•ì‡èK\µ+«ËOª¹¼ž(ø ~©P¡‚Œ0?®äÓzÃ"Š„MD°íâ3-~S2œ¿× ­ø¼'ã”k×ðbÈŠÅt£{'ÞÂˆÉø´‘Cƒ»­„…CõTšL0U"$ü©L’ÐÛ§û͇…–¡ÁIË¹Ë ÃÑë`?a~¿Óäø¾3ñ]úб½â¯#)|¾O“åô󯋑8ó¯ óE$EÀ¼ÜSѦƒLg?Þ?LÇü·wÑ4³Ç˜ô¿ð]>{ãø‹è!Ó/Ñ?!þ@=Âô‘f>6â½usqHü•wÕ’¼?ûTtC“Îz@Óm[¦ñ³·ôÅÀL€ˆ9&HÈ:Ür³õ^/ul…+úù®uj: œl+Ÿ›\Óåü £öG>΋©r5ÝñüB–k6iíû|õãrî;\µÅßӻǸ¥êvêÊç~:Çg÷5ÀÆ·r¼gg,NÞA­ååîw†ifxk¼ñ=’uʨšbŸ«mÉÝ@T_K`Ç}ø}9dfñE¥/ìÄ«Kš5¿±ÀZ@žÔ`®rÅÁƒÏAÂôC9ô|›qàŸšîWâŠ{[îÑ–‡os×ÂøûµÑ  Âó¾õßéP\£¦Mé‚á–.òØ!UÞÝä#Àε,p_Í¡€²PNV@Ä1+Ú¬NåתA¢å‘ª²?>[²èþ^§£ìíU†S‹š"OˆFé:Ž•¥O:«ÇŠîìAŸe»o«ê=žn³§vŸ5öñ‡ù#)÷Q½’™÷³úìâöaq!ðöýœ||PM«t¸XÂ’Ø$¦r)È¿®ªb£”¢.P’1Jß¿9¾ÛCÍC!ij;f-Éûê„D’Eˆù‹©¢ª"׳!c¯o^\ûzœ÷|>£e<ÿGÎãè6pç낪_7Âtú<<ÖhØîøþ‹®·““ËöÙév™÷ÚÁ^¯ß¬kóZñ¬¥Öh~w.[lð;Òȵ=‹F‡vóú7í÷JéCEÚVŸgÙ#;tß·Ý¢ÞHÞ#'•燖Ì M\&µ{}Ðü9zÚÂáã>oìçãŸø;~,©mNç'-ú¼ú܇{¸óÕ>t‡„‹tØý¿ŸŠ_ÍËìÝèÏG˜Ø4hÀC-Ðmù[£â}˜³‚èÒó´âÝ}ë¬ÙÕck³›Ç³UEøÙ÷×îÂÏ4†É9;úv%Bzÿ¢_AøÈ@2Í 5Ÿ#;«ùß„W“_úyòæ²tâ;¤ëñ°YgÑ@8ò÷ï°qýÒÊ]`¦Ê£¿K‘‡“¶Páv e<¼"Ûqë{Zû1¹“ª“ÏÎù±Œ,®¡ù 1ÕÉGtzôýf×pãÅ90æþ‰ÛÃM[WÉÏ®g¦µäÛ»T_sæ5ÅÜ̯í†"Ë‚Ú\BrQ¨.옶ÓŒn¹­ê–±Û8º$\ÖòþŒ>Ĭ³\­³ª&·¨y^£C_vVVb^=L>XØ8OðÿI†ëÏ>f¼þeâ`|LGqût²¦gw˜×ølÊ<¸¬Ë-2wþ>ì·Ã¿þçvân–;Ýå"^¿G»U.¿²”߯ØîÙ¡¡áPØÅ“uü\êÒø©iÏÃd¹¹õ5JHï@oO•ŠPþ~5BÃOù]a·w§âõë|G!ð™Bkæ§DxO³íî#;‰Î:ÕßsÇ—òÚ=:»üýKÝ÷û¼þqìv†r§uôœGq‡!à)´gdFÛãn¨nÌ£4,W~{lñ{u-o³£Kûµ jØ~Y´úüÎ"SÕ¥ÎËÑ–×zHü‘ØG×µûÃK#;™|ã#OÊmPZ lz1¦^O£FÚ@`¾—qçŒûc¶’§½ƒ¤KE~-)ß¾Ü(£›ö¨‘JÑ4à²ùçñ¿G >?ë]n.ØqúûSÍü~(KUŸ§f’í9_ú"`W•ße}wõCW¸ %ÿYŽ#Úù#;8@2ôr/ƒ›öÙ”(<.´~H¯Dt¶ÀÁ°ñ|µú]€×¡—Ò¶‹V÷|Oä³àîW=Ù/¾*#ª®–*èw]îon•pO þFþ¯«þO{Cæ¶ß¸êd”FœŠ˜ò°">>À;ž/æñJ·SÛìþ5³é°sògz|§÷ò|Çí?¥Ú;s×·òÛ‡4“0àãgd4z½YÍAq*ªA=úáùÿ#)Н›ïæÌgÝ/dÒêñßù¯Êøyÿ­ú=«Ô¬¢Xø7†¢()¨ÕÑèþ¾F ôœxõ=—çé#<‘ImãFÌjäˆSqpSïtG9W‰¿Þ$P|öò°¼#)Ä2H‡|Uök§£Ð¶Ïˆp>•u~ë~n.µÕê úüc®¾#)×òiúô[ÕN“òü&éwg¼Oïfm~.ÁÛv>‰ê~£»ó࿃¤ú5jкµý¯åóšºô"qþeîAÜPhçé±Ãöñi/¾}3èý:î~N¿pøåC¤y1åñwÎ.yކÙõLø.wð )ú4îª#) þåú+úóJ?¸yÉbhæf¿ð}ÿ«#Ž]ü€|ê[}l¾´¹;‡‡ÉÆ9y Ê­£æ+ã{Ø#<~Ù#;›]#;ÜM@?#)„*ƒoÕLž»ò ½h” N¿ÃÇ3Æ‹Ÿ^ϳµ:{Wˆ_·Ÿ£÷HòïÆX¯ÜÜnÄ-¼‹£•ß®|+2d¿AðÎÚxæŠ>€Gwïý¸Y7L)ðªŒtáᘾï§`7_N.žDŽ§ïˆ»[‚LlßQOV‰aP€ŒÎò r0I#)Ïšû^ÃÛmÚ¹è±!š\M#<ë;R[Ç=Ã@çô®èO‘8>'C6…ü™™‘Yí®}ñSÕ5/>\Z§[tË¢ã÷^`ý)œørñ:î 50qܤHt³¯T-Ý,„^´Q>›~Þ*$aD–|jâ+rîÀ‹‡¢fp$–:–¥ïQÆ9Æá=@DCéM¿ vøç§äôà4i¾ÿœaëìÕ :‰ ð;ƒÁn+-ؾðÐB)Ö ¼.˜jð•+F¨ªIî œÛˆ«ž«Í^ûEH§3çIJYضH‡­H•Z TJQWtUæjƒýÍÁ»¹k¢Ù5¬oŒKÌPŠÔâÅøÆqƒHçL°hÞ·`怀êiŠò0TÔQ$5‡6…5Ke¼+ê¥t¸sKE™vÖ¥£MÄŸ·Ðñ¦xØ7&š¿D,°AJ#ÙmÈ.­H5C= 1¿x¶ã„ì#<3šãIpzE\4yp}|}¢g«ò¿ÅWcƒîö¦È$Rw‘BGf÷¸D‹õ…MŠ|±ÖŠ<ÇÕ.Bé/qg^ß<›a«K=êúx‘Òä(†¾AíýœPYêøøYÔûÓo Ú×ì¸XréOV—ŽèoÔ¶Ev·m—ÔÚÙW‰nHÀ*~IFnœœyÈçz‚#;Qæ 7üSìv3fŠ:õëÌ6uti³Àe…uÓüùËô~f•#—k¼»êš¸j qžÉ™û‹Ë. Øï=O‹«Ûòñ‡hTû¼mq&9 {†£ú<ûc!äw“÷n]ÁÆ+ú¹=-|>%øòM£O;ðåLJ~>jDõE1I&×MÒ'³=øt?²K‰ìŒo}·>úŒ1¼$ø¹~ÇÁþ|p“»+ìôÐgèî@£Œ ÐošôE‡y˜’Uºóí?ŸÑA`þ?®Ç!Jóh‘w ñ¨ƒÙ%<£ãù<ãeëÇ Àátv^Ó 㬠U»´g?ðüh>_•FàìŸ*?ò7¨ã‡ú…¨ÌË™U˜žYpÌ!aéL’ UƒpahÉ~;ËjA†ÕAŠ‘Äàš”T¢MÛ¢ÆâÂAUZPq¸TEŽ7wC³Ww)«2ÌÕGF„B!‚¤´ŽÅ.ey#;`‚óŒ#Q†™-f Õ‘447B‘K#;X­‚++¦© nŠô`ÍšVôà˜V#<ñ ‹]?KxºjšÈÎHHÛÚ-UßeVXÑŠ4§ùУåù¯œ×'(9¤œn?¿Åïû¤Óù'³ÑÜ›…Û9vOä–|Î8~dM˜ßøY÷l²bù,€˜_Â/´.)ì< v{e?½hÝ’~í¯ùVü‚Ïdþè>y[xéýù5“û¸¢òXqíçlbhÍ/3¹#·âEËlV„z;:¡§-âê3¹-m£–é×unÿ¤‘Ûp˜ŸëéÙ§’ä|qj¯TŒ»3åDÜÝ=2·áøT(ð”Ëd?ƒYUÅßòÖF+,¥¨§’"Ä5þÓ° ý¼ºo6Ñ/ŽÄªB‚|dbâÅ‘¬»ÍêŸãñgï·;}Ý#§ìõx]@«Žc¿‡Õõ&Ÿ†MÓ[X€ #‹NÖTõí›RfZ¥#<æ9”Yo±+!_Y ˜Ý0Æ44–‡ ¡ ÄÊÝ\ZÑòÔº7‚ØÍš©.pÃ,a ê}tÓOY]‘±Ýö¢Æ¡­A‘¥^HÁ†œÉ™†ZF L¢µWFhuë¦RòŽnvàDbk¬™ñM˜‰•.œ "8oôyÕ˜°h8Z°²Ò&â¬j”5ö#;˜fˆÖ¬D"}‹S"%›·86AUƒ!'fv|ƒ0oQʃª:U¡óª UŠ£8rÌ­’1ÂÈ™¶W Ôˆ’#<&ÆaZ9X[;:b˜(Ò #‡-‹Œ`½)Å Li´m»eƒ`Èv¦êÀ\†Ql˶–`Ôqå9þ?ðÝ›6Øúá˜ïmç·]†½;Ô¸]wBý^ºo¡%ÓJ(¼Z'6MØ$ë ôZ±øÝ 9á‹”N#ºô}Çï×\^ô*x~#<Dq1†)DKÆbý–}ÆSsc?a¢Pp™y×ôýwT}àÃëá^3Ž»<ç"_¢Þ!ry×x‘»¿Ô ôübzr×Ëñýž¿‚üÿþcÏ#g_Õ™¸‘»’<•¢²K Ž£,-*ªn•T@Á§w1‚ûõÆøðËÈ÷´@âGH¹¸ÂéÃJ1¤J>¹ðÐ`ªÉ.HŽ)FD‰©„8ÌùÙÚ,ob'h¨7™¡«ƒgêé#;hl“×w™ÞÆ-¨Bð눣ÒÔQÆ»ÜÜAl‘ê]/F°4¤´RéhƒDâdP)##;ÈÆÆë´ÄîsjéUZ*„B§CžþÈ`óOäü›ü8ݶ­q!å. h¼¼9Ë‹as>. €Äü›2þaûžWeû½]ý³—twŸ]áêÒ.ËghÉ>v ùô^Ùks@½¼]´ÂÏù‡!l‹Ä‘{m¯÷–gP<&>£Êà¦óÉ3Ï•‰"§äŽWîþŸ4¼FÈæÆ]¡û¹âï>ôÎ"ÎloØA'm®ûV#;TåžÇÉ\'JvÚ1‰Úî¶4Ûxâðöž—FÈ”Ùf9" f=ÎÛèW0šµUKEêÐ[¬4Š–OÍ‘êEõ;ƒ]®fš®½s÷3m±Œ~1A¾CyáÎú3RÈ÷–I*éòÃ3LcݪÑ&3Œ ›†ð†DÛ{1×$îtóZð€ÛqÓFñ3%ö^M»Ñ{ç´íÛ°v$¤F¯%ÍYÉѥȈ҆‘΋¤Ø†ÒÅ>jÀ°wÍUí«3{rÖÛй´ÇçûÆå˜ùŽk!„<Oå1mÈiø@Æù”F_ƒmŒÓ™¶¡0ÚÀã¤Û;ˆÂÛ]#;Àí0!2LRqYB'!Çbipî@Τ2ìl)ð_óTÑ©ðÀGmç.d–'O±DuF+|ÙÛq³ [‘ï#)‚<õFÜåç˜ÉŒÐÃ…bCj=S®gÜŽ6&D(l—ÐÔ(á¸4Ã7„›%ÞÜ¡{ußkÉ»F:Ƶ¾±„²| œI6{ðj]CKcnMµuÐÆÅV+U[ÞLª‚ÔÖêJ]([8n<.ÁNê7$í¦Žƒˆ84a÷äÃ>dg8rYÔ/€»K"š;cwrîsÆÓ]ÔéÓÆ:(ÒK­ q¡¸"Gw‚‹êÏÕp]óÎó¡.ç;ïgQ×gtAZ8˜–JÝ£ƒ‚Ž—–à× ¸Ì¢—\\hÐ^B¾fÃvcŎŒ{®‘Z±Ú5˜Á›nµtÜeó<œõ\¼ÆQ†&kÀX4#ÙJT¹Õí’µÖì6ÖÑdá"°Ë0Ylƒ<œšª6 ÍõrLѶBQHKz’´W[»`XVC69smŒœ’ ä›YÕÀ" 6žfÇ eö{ŽV6%iŽý¬i=tÑL2m!ôž‘Bß³Ò9…­Äìå­}þÞ¾÷gkÖÅ‹žãÕÒÒ—ˆxP®\Ã%bü®»òz}¸Ù·œ°lWr'Ì]F1„#*£ L™`3¿ž™2h›¦“§J¥tã·fpo1ŠN1ÁJn×¹²O?ñîe¥Ý=ÄÙMa^îxGkí]–Û”?XiibÛœ*¶/©ÏeÚuÙ6–Ó°Bæ2(¨6‡±o—·¦s¦k!Ž{km–#<â7+ý(Öe—âbiR4ñÆðWVÔãýdŠ,¤k†L’¾6Þ–|‹-’yœ¦Ò‚‚R1X½]­ºT-;·Š¨˜j&aXæQ”Ñcío•d*¸3o,1—™Z¬a*Òº–³oŸLº\B· rÞü²qÒ¡øq—sÖêóIȈ¡fŠ*#)_:µ*_—ƽƒÅõ~ÄpÐã€Ws‘é,Ëž\=/ÆÁßXw£Ñ_¼×Þ‚UIÒýâçí‹ôí#Z‚j‘ƒù—éþò.å#;Ä4}ŸØ9Ä&¬aºH¯ÁÝ¡žß¯'ÞÃs{þžöpÛÏò™ú9çò–ö/~2ƒ‰öµTPzÄ=ÎhÀ<ý>ï i\£Ã‹Ž¯+è®ޱ>SäÚì×€gÁ–ï4΃NÿCûÌkIW/EI²Þrg¨—,õþ3~^íÒ7‚yÐq’ù†hÈ]t1†"ÛFY·Ïïù>FŒ»pï_ ž8Ó¸+P2!FXbí‘%‘#)§‡@¼YŽ=¹AAýúpÙŽWªaŒi!ÿ“‘ãhxò”ô'›ìr8÷#) ÷ßëÉŸßÚ ®ÿ1,!š¹Súõ{6§£:rÔ)ˆ¨+Å¥[Ç$=¦ Š£“¶-Ì è¸uY”‡ó‹\2±;ŠXÒz º#;;‚ø!”Á@^:Xaæ#;jlB™¿Htm'AZÊFp]X;Í#)çg?´YѬ¡×ûäˆù¬6'£SƒáPU¢H83²¸ã5°"Ñ ¦î|ý=9¡þŸ—àrFS°«œ»«1Êp‘Þ`w±ã., MKÀ£¯Œ&b©ò˜Ý}2‡á¼sÀÙ¤«vK.Ë—!B#<6˜%Xq-¸Áµ²bØy‘ÚŠÔ°QËíiÇœ_t¶æKd{2Ã{×Ò;W”牄û2ôÇÖ>F%䈧øC­+7&"]áÒÛ8 ®1yFÂ](—YÄÝ7ÖÆbìBHº“û6k¢¬zæ¥Ý¦üd¤!Rë~”n Ÿ"ú­zÃx˜b}™“éº÷dÄËᎂ¯J¨æ½O|–J#pðëEQëz©êêÛÙ#<]Öae¼ÑÓM9Iø;½Ýs­/1i>J$P´Šd£PZ¦ßžaw>zd$+¥jPnô.­éºwn¬çßÍðw2CY§:wCê7Ùð¦O‡¬/}i"鉀„"n¸€ñ¼&Kµ·ž@§eÏ(¢áŽSpVH÷-÷Nòm-êû!;Ñ]'eê‚ÍÊ;3‹ D»°èI€˜v!•¤ÊôIõÇ•uæu»ö³†Ÿ‚°¡ºÙñ%ðoîG Pø¿à.'¼vœx¼ßI"fG‘CMM<» Ô¤k>·s|´dõMmdŸéÝÕtÐJhVϘ‚Å%2éD';1ûóK×£6±§_ˆš6piXñÞ Ù5ù»³¯Á}½s#;³Ð¢“Õ°CÈmݯ©M2qrsñ¬Xºøè¢?ÏÛâñlAŽ&]ÎcÂ!"Ï#ò‰ÁÌð õ©øÏ!ÀžæO×Ù¾Å3I`d”•ê0Bއ$d\P9‹78PÕNÕ„ã¦Ù”˜1š&‚ØG®°çÛÃ5!i‡E¡aä?vªì<Î#Žœü²ßÛ¯M,ï˜]¨ÁÏS&`6x¢±Ñ¬Ãÿ)S9l4WÞÕmôÅ/\Æd-¡GƒïÐðÚ1™Š3%Å'¼#;èq÷\3#e•B¾ÑO'xØDÐ#;×T³Ùà7fæ$"‹Há`…‰ˆa'#ùŽüf,B8&¼¤*D6äy´.—IjSÛzuÉéãÖ¼9Íí\š†id“þJÊÃy‚ݧ²èÐæ#;öeðSUxòvÆ0bÝõ’SûÑ~Äè-:¯«¶PÇZx×Àèöyã@ÆL÷Üs4ýÜVÝ9WW‹Ù1´$Â@lf'É;H˜\§wªzé+r`qÉLB¼@rJ¯÷$}8ø·àäåú®ÞHÂIOµ‘*áT\²Túññ?h‹ô³ÝšÈ¯ùæÍÂÝIÓL‡]L=<Ó¤%_7tJ-SNL½$výÕ9Øräô¬¨ñ9û_Çl–èÓ1UÉ$ß‘g*¼x’ç›e!9 V‚R\-µì‘åe¿éPªÔJ"ŠÌ‰#Í„R—Þ²|îç}~§Šn«Œ€ºr—(#<é!‡¥F詠ͽÈ좤îBÅš½Múlǰñó‡Ôé4ŠsñuyAÿxôëçñïDGƒ”é-µÃÑdYÉ£=½œüqôTÔàËu%Y=TŽdŠq8…Œ»N'TÊU_<èh±CŸŠ3©aôZn¸3Î͵Ҟ•²öXfÀoDA.ìnÄ]¤¦È§Éóânn<ðÏ=g/^ ëm2ÞŸbI5 Þþ¡ŽˆÚÚ…´s]‡áq9UVŒ¬jUá6¡ jïЉl¡Ï/?CbvÆûL‰¥“ÿiæwòŵ†›,ŸÂgÃ4×áwÉlžxdžýúç6Ú[±”@ýJ}+ÒÁ/D:)™¸äÞßåÞY¥KzØëÛ•þî°Úèä?ðQθ‰mÐlvjŒõϤ2s¡n„ñº³é !pÁ×#)Êw¦€‹u¢Úi¾á(}P//•˜TÏ-¯wo~JATgû4ÍfÀ¬Æ‚SBj—'»³¡¾]k&n¯_3únð_b÷­ÀT>#;Ä”Ã-Äütcx`óƒå{½ˆÄ<Ÿ3ŒN=‹)œðïí$à߉‘#)èð}”µ>¢Áj%§0mq-M9ýìÞܯÇïÄu¼úÖ%Ð}õÛµØoúÝçj̤äxm;® ^ˆ—ˆr7CÊrJb¡¼¿®Ž…v-•­bó¤AÏoý£nPû#;pÑYÓâ/.U¹ËiEú«XúõR#”,#;Ó{K'aèhK+p…–}€°š]µÓÕhŒ6Û?åv½ŸÍ-7U›š…Ù59•œå_Ö 8X$¶YÇ7…¤ž×#þ…ŸÝøÔ?½¾œîvðÏ„=TtႎQ09ãSU|[¦N¹ct¶Â·ÂçÉ/Ck·‰^Û/˜Ë `%ŠBzË?(k7äïK!"TÄ…z¹°|ú!7/ DÓéŠHchä–:+q£Î«‰¸-Ð0¶A‹Gƒ&È:R– ‹.‚šAòî}1zŽõQ¯ŠÆÔû´ ¯Âùç8I‚ß{^®$¹Å±3z¸¾Õ+cƒõÃô6Éôç+I—·Ÿ)³Žêu‚|oWfxëŸ ¼_–CíŒ`FñgÁÜç…Û¤f…´«:âéÒ2×ÓgÄ$&5òZÓª6w;-tzõõå^B/s5šùºƒÅÖÆç&±ù?^;3®c«hçÏy&ìp´ƒ'Xx˜ßž‘Tñ0q³Ð¸ÃºòiÂqÒ|nhͼCËd‘‡LkÇáÛ9­ g Ý€`úA…¹¯UÏ›„e©ûvù”dYJz즗ü\Æ/ÎRx±>ýÕ>>1ç´k¸~ÑÁѽè©äQæacûKâô\U·iVÛ£[`Ë2u4#W•c#)N®¨9Ê@†Ž#<÷+¤ÔÙ'nsJ^rwR·ÅÑŽs%âOZâ¹P肊QŸÝ¢¶`EŽ ,W6B‚Å„N9gZANìm±°5H„¥u‹ÉVžt{{"6G¶®_vO<]$ïQöby“ˆ¾#;6Kk¼~=±2ù¸sÁ듽ãç|_UʉÆ;É¥ msÅPïIü?wx½|y0gF7;ÏD£¥KÂñ•Ï$´R“Y>õØ}ãlm#;¼ü0G5¼,ü•D>R‰ª×BÒ¶TÐgzÿDÅ´„q^•Çk@Ç)øak´͸Ճ á—“ºi¨ïkÍÊ»—9M¹£8‡ÒŽOÓXÅ2Ùd±Ñ*:VR׸@ÁèŠÇTlt&PzÚFfç§]ðïzÜ—°¶€âQ¾ØjÜîH )Ý èôZí¼«P‡Ø¢¸ öïœÀ~µÉl{¨«xT5­GDL†“QùS žàê#)ë-ОÝo«q|ØQ û—3û&á{Ï–ÐÆÉíÖóÌ.Úߪ1ôÍq¬Ö Œ(X¨šòŽ‹yíá¶îr%õTvâ¤âDŽ Ç #‰©}·½æÒ÷Zl»E6ÕÄN«‰ÑsšZ9¦à\üéak°t]8r@P!ŸME”9“®êkŽ ½Ê%BVûý²Õ'ÜA…Ws«úž naÃqXÇF¥,«\d-†=”k#<’urNùÆSa½úê+\Ú¢­fµŠ¥q¶.hEËu¯ÈÅú•uØ·‚`qih™{÷,å[§~*uQŒäÏÚYëf‹ñœk6 =\‘ç6L<-sÉÐ}'\U#;ÂÖMYؘížÚwôìJ­8k6Ïl5cÎZÅ^yhb¼§Ëš¥‰Áƒ$hâx_($#£õwù°tØî؃bç^ó\ƒÜ’ sÕÎ8Ñ‘´±ƒ\D‹uÚfWÂ\$Š„•98¥f_xo@§ô½³åÒpçoY˜ÇƒÞ}æ­×SÌ…ž~ª!‰ÜsÀ^anë‚\µŽqØã·ã€Û°¤$¬óÖZ«ý9Y·D4ÒYPZC%(ÌT=erQDG9)-ƒ{âºM!kÜç(¨Q¹Ÿt8x×'ôˆ1ʪä°*nÍÌ.<óÀµìºÎ5zBîšÄCÁ ï~Ú¸JÁ#+¾­¥P/xsªs>ÿŽ©^¹ò çO}s‡ê`¼i𲸙º£âõ:|]/[,X{{ÅβÎ:óK!W¨}®[T-:‘ ñiãm¥íöÓ§#<á±ÊLRr‹•ÿ~³åAeÝ1ìÎÛ:Ù­¹:,’#0^í0WŽu†YÇ¢·ÊÐ:ËÈ1AÑ‹;P&0ÞØñ¥nrèvîzÂ*ÆÝŒÂÕîݦá#)ë­ «XÝ@;;@ÄX–ÑHP¥P—”?š/ýaÓ¾™<µ’¨µFiI·_É m#)!@³{‚ì°?|p›¤€/$vf£Da²`$ÎÁ¦ÿ'.#;b\PJaÚ#) XP\`ôOx ä7*(œ³ɲ,9yYܼà Æ6#w8¾ÛkÁ]5Z56—5y$UÊó|œÓ)-îãè~ˆ¬s«>L ñj†t‰ ¨Ã5’슠šü¦ iæÝò«—-°‘]vŽç¹âºœïD¤EHjµ¤9ÙGYtayç(ÐëV#QÕ«2ÙIp>“Sâ½ÛiøÌÏHÍ)·(â`„b6´‘\b:£Q/sRÑòÝCjS¦”Ó›[*loÅâØ#)¦%D4ëo§¥D2˜¸`+ñ”9Ì‘žêz†­îmV;[ÞCp¤˜D8ú›žœøÃ–µØéÔÝtXÄ>í„e±ÄO2_e„<#;äªhÛ;¢aâ"f_à/w¦ºø`ëG¼}“?OéíÊ-Ýïµæš×]Ü,BF|bûUÞÓÝc؇¨Y >û®O:o_¦ˆ¾7ñõmýS–ü,qÚ6òžÐ]Z86#<‰Llx¹7áŽÙíí–#;5±z,—Ù`í²ÖˆÚFM;œQMTY;Å•«r§FŦ°Á„òŒ€Ç¢ìB VQSD:Fp©ò[nc—ã!Bð;”LÝEä·êþ[ñú8ŒIë/ÿRZͶ.K¸xá|‘ <¡¶£91Š®ñ&Øæ#;za›H’ºûlÍ'GóŽáç8:u¸Q‡^Ïh‘µBfpŒîÒðíN”QÇf‡¬SSž)š¥ÓO>]§mÚÈŽ¤4åúC¥OåËÅaáîiàŒh¸œ¨FÙiñyóžÆRG|%'ÚŠÄ ÷‡¿ÙáØ+±ëáï ¯¿÷ÍcmëÞM#ŒkVOm‘™MÛ7›K#SR•9MyßÃÜ/+‡Cƒá÷D¦R9Ôƒä}»Á…êrg¶ÝíÒG“臷;(õPéJöî%[à ‡MµxÁãâj["^NhF„¡熑oûžxߤô'§#;¤Î"käõ9Ðìç‡mÚ\¦ê.^šô·ðÂ[y»0× Ìk§ª Ùç9n|·¥ì†rÇÊ " †…k«±3/:N®¢…gM!8a”ˆå0ý92YŽâÙÓ¤J:w¹~õqP%K³×ŠåØ,\Áøi ½Ï²!Ô= 9ãï¿Eˆ*@’ŒmnU#Þn»§É)Z½Ùrq£….TDåHªR ¤Žm/O˜ú#<pý©ûÿOÒÇärbŽ„QÑg'8ý|¨ÖÕÁÛÊ dÂiú¿7—MbÊrÏhc4Á?g×\J‰'X:ùK…ª-¼#†c×Ïמ˜guvZ?¡?…Y°þöÃhÖÃß­MÕÚ-ª>–ëžû†Mí1ú€ôyù4„~Eá¾ÈÚAúk‹Ãôj)-òÆ£ã©e¶Š%&Õe•~lþx,ª`ªQ)•#åMëQ PjAÓ$×÷<9í™0ÍGÕtC|Ký‚¦§óêQoéáÓàŽ ~_ÆH$S7­«#)€ª¬Ë‚³Î/âχä_·wÁú W3Áq*Ѓx.¸‹ßK;:Û[“}OÓªZtÅÆÂ…Úí¬î{E#)åzfM˜¹Ø½§ÓÌ!`˜yh´7ÃH~;Gó¿ÈÿlÔEõC*ij¡;O žhµ·ÓŒõó[ûÞgrÆi¶+s8QÒŠ#<‹ ;}u:!Õ#;P?[õ§à„õ&R˜î€7C§Ùü-¬#<ˆ@ERôˆì‚Ú ’/\ •”Å2HŠü^»j¿#ñ*ø¢&’€Ñ*ç"æ¨ïdS,òÒ°èå“É”¹u$À†É„AÉ5 åC¬JG29ÖÂËNÊ<ì¶Ä°ûâíå@>è†P#;±¿¯¦»pík9a?Å¡ÙàZ½ᵎô8ÄÈËå¼îß’ô‘á_âãÉêíB«yÏ»åŒ|Ü;åÎ>÷n˜ú(_Üã‚Î~Qw¶«‚¦|½{âÈÅ¢&šæÄ‘8þv0èÑ‹Z¥ÏEˆ\4U‘÷‚„ÝU3У µ¾!I@ÂE‰OhÞÕE•#)¦Ò§ š0‚ÒL•{é|´•¤i›†¶›{•qñßúñI޳ãf¹©»P+Ýåeäà]ýµ¦¸ÍòÚ¯ï1Š‹Â©#)Š|#<³¨`5Q"Q¨õNçZÉp`"YI˜Ûã¢"V·qô;õ@1É ÅÅÏwÚäàÂþ'%ïW¿»Þ"‡bOîdË6aÑìýjÔ2¾4™!æˆDí×e³M±’Š,=^I™oÃf{M•¶ÿ‡{n04# œéï|âàÐsU¿ì¤;5ÂJLãmÚøGï6ŒsC7Ü€Ýþòf×™^Ñ5MÒMRATšj„NI¢QÖ¹ðÅ”@q™ù†0-òãâß¶î|Rº“™OAan0aÐz’ïI-. ê”cÒ"=;*¼þ:óìRkTBà-Š®Û„ôN"#;àø ŒêïN#<ˆKƲB#;F¢öôóÐ~Ù<„ÆOAë¢Tìœz9=‹ÉæŸêÿ#)h`‹ñ¢™”Úåœ.{7\‚ãЋ|k¤¾*d-Wµ¼ÿy˃2|ý#<’sÇjû<‰Þ×Iåæþg×¼âû3¶†m5!ŠK›owœ¾÷“'H“Ë,ÝëªÁÿçûOáHò¡Ôºq#<“×¾îë%*ÎÛ57 ÂŒƒ¯ÎÐ…BI @¾ä­ýó"= Ö»¼'3\›˜~'†ðt†8r²}Ê)>ñ…Ù7)k“|Iæ'õ±”Ó÷~«s¡÷£W¦;ÁÆaš ÷K‰ÝáýlÙ–G>'xb.#;¯™4¢ªŠð®&"jY[u‡4Þü祕åÖLgEYÝ/­vçeéoTEE Y6ޙʡ. 8eÍj0\E£¸€äÎpê뤞"I™QáÒ0pp~žl\©º{eŽ(2´DVýÓzˆ¤ÿRã‹ø=jXÙ‰×0v%¾«Iñ8Ÿ¹pg(#;¾o£/ŠõD*\dW*×á+­æLñ€ñ0Ó¡TÆhB–ñM`X!èŠUÍÕË#^1ŠŠÁ—I¶zƒß”îÀSiM¶k 0Šs îšÜב×7$Àb"…îW(lP°ä—uDFù #Þ{±¬Y#<÷úb™ª»wDéŒûê#;"“6ÏQ,Cú~¼Ý#;«êe²Ì”H‰xÞ-@ ²Ò–#Æ<´ºÈ ¸‡œ@;K±ê#`;k k»•r~w)X·eÊÁ\­’Ýx,lˆðsGfÁÓéyâåCIÖwBà.7!GÎXÍB^­éÞDÈóÙú­.ÍŸ?ІêÀsœùi§kP=ƒ¤DoGÔˆŽ72F—}–æÍÙ÷˸'!òsRÇ-ý‡ÒƒÏ¯ÓM”û¹J ~dÓuãíxå÷ï™—ˆzíLĤ’YúäõëäáÔ+:IR¡Ë§L¸ž #<5ƒ*‰¸äèdÏ<·.R ûûï«çÊTæCÈ·Ò_Nü¼}£§zø¶JgÆÚœÇ‰Í3õœŒÃ4~Žâ~þ”À2$è"¥¨£K.ÇŽnJP0¥Ë}–ä*¢ºO¶£)êæ F);#;ä™ln}†Æ½km¸@:h3¥ënçS4—­:d6vvyóþG^éëÙÏö¡föÓ=õ>—¾Å6ñøËÞ®dww/wè¿£âw“öš³³¿'ÄJסLűÙ#;d)GŠÏ^ÜœUU˜~ ªmX›nk¯™MqCäc#)°‚Б4r v½æ¼8EӭÕ^, XQ¦ÅZêhA!+äŠQk–§UXC#$Z¬sºÊ Þ±0Êot“vR¤Ÿ6×'J‚Øwòã¦ß=ÁØ)Af§?s8‡aÅ–'ur»HnTN¹Á ”DUMÄó¸1I².”à$4¦[#|=®“<å©§_çÉaËZy¦ÍùE¥J4ïàqO¢d–ÜpºvOKi*‚œ#;eÙʲK²)kì±ÃŽÜS÷Œ1±#)§!§>/dÆZV¸i¤“_D €! ]Ñá¦á[lM ê#[Wf=ήõá#;àà¥Wɲa¯D@ÅV‰71(ª£edŒìª8F3°tíÜPãÝŠJÅMšQi"4ØÂ›NºüxŽœå0Õyó6ç#;ÀÑœ¬è ¦ùèžâ\Ý&â‚fœ¨áÝÉÄ­#ó¸Ì”jM§óíþ×Às{ †óvdþ¨µÔCs~NFËóPù¶%1®Æþ²Ì­á¡ñSgl#;Ц’ªeß¡ð°³P’ž”?¡P)B9ÎÜùÑÊ9µÐ3zJ!º»(ÝIü\û½‡qý™“û°¨¬ˆuœØ’±´È£»wnÔjPK» €Å‰HJU`‡÷ÿâ}èÅSL÷%@7ÿ‹Ó5ZÖ1ñªýè‚ >Þ¿ãEb*ƒ…øžB1_×íøÖ𣝯‚«÷\øîú('Ͼ âï÷õžÞ›%NÐü=> ¼öR{•'ÕŸ~X¨ª° iDö¡Ñ "‡ñ‘'÷Ð#’?ìŸ?ó}þŸèÑìâÿ1¹ý?—î¶`ÿ%©ê†šzt0oßßH›q”÷­ñÏç#<úü<9Š\ªç/5.¹À)KFS*ˆAè(镞(Bñ ì©‚ Á”‚,íy!Ü&ù1#;Ëq³ñãîtc«ËFÙŽ¾ýlO²½Äã Ï—ÛßÛXkç"ᛨîªQwMgÓî×QëÔæ—Ÿ¢‹½€àbqa.ùP©ÛúïÄÅ·©ºçMüÛ1êÙª•†D`Ë6ó%ã„åÝ©S¥ý-üökþ #;ê ó§°Gk!ˆl<½&™'®à|/Iïîµî7 Ô¹ëIƒ'’Ò…LjÁŽà©ÈŒ¨ˆ»ÏÞ8¶çí°Z„]mmÄñ°ÒèЖŠÀB{Øwü^xb~ð¨ÉpÈÞ#;\­Ò¨¨˜™õKë"­3óý´^ïH–ß·Úi‹Ÿ­AÙ#;A>P›#)µÈôæîöñ3ý~–HÍà×Áþƒx€A™0l#)zY#):Oá²åÓÇÅÌôªs=v¡öäÂZVùE³ÿ*o¯_ªA~HCª'¯²AuÓBþN}ÆÂMZˆÛ°•tD(MP0鳋#)Slðˆü7èå½ÁC©ªXU‘ uÝ ~÷ž`Å 0 F“ctlòÇ_„¡[„6¦â’ÆdKƒ¶å#±Fí@ŠDB’.#;j6AÙÒÂýnAlˆ jÙ5J#;ºð¨ä.·6žÁH°F%‡<’’ê&Y)º—[}åZÉ#;D” Äé¢ ¨!œUþt([CæÔ=„ŸW‘TîñðÀp)å)\9Y5”’pR!0NêS sÇÎdŽŽMé¦DˆCÛÇàIºøv2]…P½C(\ª)’¡ÚÝäAe´ÕÓAÉÂ#=zò°4deJ´a¥+ªNÛnë½6æ9ß¾ÜC´²!:„R\þŠ«šÏ`åþ¦±ï<.ÍÚ^©gÍZŒDSb6•2·(•¾õg áâvz­@ÆF¤‹‡§ìjÈŽ­/V¶æ¨ÕC(zSýDÃèiñi‹¨“Š_†,:ÎÊèzL6퇋@øì©¢[«ï—˜®eñC®ª˜´¤|#)!ÃÞ‡‚ ìN‰G ¥¤#<^³TÕ5€xæ'l°ý©ôX23hÇÀ1;Í©]~|å ýQn]97–< ßä­^÷\›4YÑLÓe•¦d¦,}ÇŸeú£=N»»’æ§S#<¥ð韖nÀì–ç4òx!¬3àp³G‹»kÙBœK#)TyX$^Cn¥ì-Ó6ܨ‹HY!HD(rõ—fP8dÃ1lÈEOdP/'€m¥1òÁËnþðEOYçz†jpIET=®ì ™¼Ž\Po‹óÿzUX¶ß¡‰–8ähÔêk>JûR#;¡6‰Èº&"I!²†`“Ñ†Öæ‹ˆòäÂ#;±èìH: Ú+f´ˆç#)ÈC7Ò-°¡ ï¡/-tƒ¶:„ Mí!pð¥$Ü@w*Q¥ž{Š£nûÜQI´AHq…»;±ÃF|èRíÃOÏÔí;&9ÉŸU½&è½Öw )¼ŒªmÙÕ¿n:ˆï: mêr$M¯8H>xua¬Ï|CHH…Ò¦ù¸Lº›hC¨°EêÐÎ ]Á üüÇtßÞàxцHù¹dg¾àœ§˜ÈÂY¼ä©”|jŠ‹#<û‹¥Vê›sU½–æ¶Hµcm|eWÞmÒÖÆÖ£l›Uã#)ƒ#; ƒe„>Nɘˆ”bKÁ`¤Ñ‚¼ÎŠM•”1J1fÚ1.nÍ^#)ä©$ááR’#G¤p>@óq 6¶xÝ*_ƒð䇾òÇDù’&±ŽóhìóÏ‘·+K$IsD‹¤êl|7È·ð®D|÷(y³¸´W—-!ïjýtöžÛy×.-zÜ$$6Õå-&‚ß~ë¶llÍXÕø5½G,@z¡Ž7èñêV8‘ÓiJg\xò`,ÜÙC&—™óè«â0È¥X¹{¹4bz¡Ê¼ µIK)2I#-늤”–¾5#qöõâˆPÍI™§Ð1±)ÎV4¼)fo«²W+ð“t’ù¹µñ«´¶ BP vÛ‰Žê¯?&ÔÉ€EÓ4¸@P"¥¶\Ig›cQ(}ßIiBˤ…A¶#)Xþqòónª¬fvpêMÈç_Qê‡Ãh ü.^p¸Æ$;°¼¡¿„îI«¹¥›'¯[C4°ëË1"H–S¾¼%×[v/¯Ö#<¯ŸmvÈæ¹-^+%ÔÓßéûÔ4vàÊ$U’)ç*œý=<5Ì Œ33ßéÛ;òÁX1p: CpÞì œI<¶ï¦R‚ü>{DÖ@#<6Çʃäßf¸õ渻>½Üü{N#<#;Ñ8*½I¯Áà…Ÿ#&#;7£AÈÂQ¶žJ5|ç—›ÍÚ×Ò‘4EÊ‚¤"1º¢˜T€éÕe¥Eò› çèDÔòù˦x¡í»^ý«ADtAÚŸ<É©2Að|D#;†ïÎÌ^NŸƒ`nŠ„é¨ׇn!ì#<tm áUäÍJ` ;ÏfÒÕ†zg¤ ¬2/['2,—[»ýþŠwç#)®tÅ é䡃™äwØRĦNaþˆ®³–;÷5ÍôÀY·§P‘›.{u;à¹MV»Ù9»¬˜Ëe$à\AT\È(A$ÐÉ ¬·ïøbg®-îIýn­Ã¬óó¦åcŸLôówM§wC§´ Øoc•Ùò÷fŒ91AV ’A´í†ÕG_Ý-aDö+žŽõà,5¶vü·)ô¥±¬ÍUv ‹~ñXd/´NõPHçHÒ)Tþ=îi×HBÓLCa#;CK­3Óœ!°×Ÿ&Ã#;‡Ýû|ÿ#<äÒã•™À×½™`ßa°H’7ìhoÌ#<(5§çzròáèŸ3#;9«í8q´Â…ØøNÍ#;P“)4z&”$B2>qaùûo–N»«Ågw,8¤ÈäŽ2FÇ£¬H{RÓÕM,§®×•âûþvb¼êãÕ¡ˆ·›¦Åˆ/$†A˜ÚƒÒ{ø›òÁ5»á½Q×iD4j=ˆP#y"_‹ÝB‡=¡û|s3„AÌ79ž§‡^cs³ï·;üžº0øÝ,‡k •AsÑå~Ý^Í1jÒèúRñŠax5àÂ-!xŠ‹*#<à#<È’kZ• oí9ÔÇ9·p|Ýyo äöHï(4ï8Jwî#)[ËA"ÇHDä[÷ ’I[1Òó±F+«t…ð³JD;ì7õ5l£¤ÄõSɽh†ð´7'¡¹Z‡Zêtá Él¸°¾ wľÜX±¶K««ÚîÆy¨ßK:Ü)>ŽÅŒàt×3§1\@iñm’Y!2#<€&$) ƒE°†(¤8×[a!WµÔµ®®Û­]Q¥/}^µui6£àÊí|o¬!æu˜kƒ§ŸÝH@Bûá3׈ÿlÅu­UuP¸ïúg×îú/jæ¡§•£oSØ&læt6ñÄ&"›o€‡Å‚ØÆ MV·>˾´/{@ æfù¶›#)9Їµ¤’èaãGx²#!éh†Éé¸y CÀË<åév~~÷Õq(˜O’BCNïp!hqðΪ®–_^S–š¤dìƒ(sC•«ZŠ#)œñ¼ Ï,áÈ®¹²L9U€2‹°&å>Û[‘-E¸¼6b#)¦0 ^¿#;ƒÔMÁU¢µK‘Á²ƒƒÚýhUUòjkG#;±¡LC.SÂèq‡)1"V(•¬ô“®ú‹[G‹é´;8yêuâ6ÌÆ.ü%ÖŸîl(M¢aXö„†jM7Ûb è2!Ûô^g,É#<Ãc·Ä8¥ ³‘·}y>cÔ;Vg£¼ç²­µnE:>Gȇ>†‚'£ª fù(Žª×ƒ â‘MÐêÎl)í:ÈT˜‘ ÷Rk]»é‚hÂq(Ãv™ŒêƒÔ†õØö¹*è0¹$˜ÚDÑRzúo<ͱ¤ÆSEŒ<ãcº± ¡@Æj“ÔÖ]$‡aÝ"pf`EÁçÞ[ƒò3Øà¤†;[rYsŠ ø4J@ès¡+R a°>}~œó·²a¨Í==´SkŽ”ëf^®~·9qyJo.v=£ã@O=JYœ9ÐöTíè ä´ï©â²‚¥=vsvªûÇm68÷T,rw¶|u·EàgP;¡K°ê H‰ß9zöÿ_¢¬Z×Àî!üyõ÷ô¬ÄWÑTrâuîØz·å¤hµ¢#)B=øH¾GÆ6ÒdÙ¢o•¾L%§ÊÓ ›«¨Fòz™‡æüáâyOվ߿ØZ<8><§½ä‹¦OÃú0j‡©†©èAbÀbl=F¡Þýœ5ðåêیռºnÛB¹©È>x§N³·÷tÏò+Ë߀çósáúæ˜^"±¿tŽCàÁéC<a󿉩‚U­¶%n>¬â̰’{Š«•9ÿìvv¦Ë“ì³y_-ÿ𧬠²ƒ{Ã6³·×]*A2>24g“ÉŠ¯¸5v»€Í#;¢4.Ã7ó† ‡¤uºï¸i·cäöIãˆ?t '™7(:F=>$M‰ŽY‰>ñq óŽî§¥FÕéOãT’ÃcÍïYè¹8®•±†²§ÚÇ8'÷a€0x‰¾äÉC»Óä±·Õ6¢,Ha»[#)ü?Éü>Vü³ÏûrÆ1}Ñ|u™ŽBkQæ(!õ¿ºŽ¬å¢0k"Èj®X[žØ~ßÝëÁ}6³'&n,žBW–x6}9™k%·:%‡òŒçû¡HoÖ¡€Â©T}õr’žX­1ááG§=ú‡¯}&`ÿs’"lë·(%ÿ“ü_XÂ'ôÏð[ç·mçgŽ2{ú["#< @öÿ7§õþ?§õü_¶C÷M‚ÿ—úÁÿÅñ­x5Ü+7‹ñäþ0ú¨¯¯#4ц¿ß|9á5ÎÕ|£Éæ”+¦÷Ù¶rxe°ù²K+Í—&îÙ1;çämókšÛùn–K^9¨ßÄW>…eŸÌ3üO<ƒÜdú?£=Ƴý¬]H|ÍoÇ_§\¢)À޵Gø²ºf‡¡Ž_·õ„+¿ÞL·’-ßÉÊ.úñAHC&ÿuÍùvó§8P †^:M¸„gˆ™ë‘#ÎÄ©+\SehŒ­tT×P‚~ÌÂ]Ô݈6 7A¦˜%ÁE†®cÑ.¾ŒÙsÛÂéWd;‚¿úê‰ßWS‘ô#)%h#)#ÃÃör%?0áD xµ,b¯â³¶»°}Ò¡ec—T+¿FCÌ /¦‰ÂHÕv&ÙéuhÞ”L!ŒÑʶ²h€ ¦‰ØÎµF¾_<ß#;”•^|$¡il™.ÕeDª{Xÿ¢J#Tà­ËH£—!Ïz\ z×.7†r+´3Ã’ ÔCD.° £f±é»^•¡>ïXI²wU"((³²QîöíøÕ>êçkà[¤´…b²Ç/«íáöcƒŒ¿¬æšúÛ‹#mNƒÚÇ…vÉrð˜Pëúµ«µD>3’áYfÔ|Fqkðõ7œÞÊM<â6-4ýØA$E…U•«Õ×3ƒ­Ÿ#;¬ÊWõ33‹r9›‹cºÈÄ*zÉàê#)u„èòÛP5–5¢jVs×õšg™ù8ßÍçûÌßzþ’ª*%¶yÿ?7ê}oõz»À¸.•zÛñãåó_ãñú$qÚç7e±ˆWÂ#<^!êüþ#;ó‘Nµä Ðò{úÈn]XM¾û.”G^ð~ë#Œôæ5¨#^¯RFþèö#)þ<¨eÑc=Ñ€rÒ|ðjñ™¦«òijWÍ+ õtÖÂ-¥#)~ÿ~@x¼>¾1ÂH;Ü:÷ùÇ*TÌ)#Ö!óéïÖ7NTºœâ¥ÐƒÀçÁñžáO«ÎÜä9ˆDÈ¿Z€;w¥üJCÓ?ŸÑ»~¹ˆcjZ<Úù¾`ð'»~ܸpêë„qã¥^$<°“Õ½pbgûµáSþG뜋[2cJ#<¡¿-î¬v¡íù¬X)y@¯ƒ!s2µ½ï²*‚ÅW݇“âø¹Ý 1Ä•åæêû°Úƒr|s`@ž¥š¯Ù¬²î*Ò.WJ${—Œâä»ÞÎ?qûI÷­Ècôm‡óî’¨šñžV ÜÌâ‚B–üý7áàãÂB¶‹ž„3繂=k£Í†f¬~0ÌÀg9«W#·ü³_Žó9UÆâ#)mÞYÙ÷‹üu;#;M„­Í”Å¥lóJÔ¡k‡Ì涆ÅTç9ïÛôù˜ôœ&b!æ)Eøýú”òáË.G+^ÒŒ(¹Àr,fõt"÷;KJOåò䈎r(Ø¢¾_<ÝK" —fܰÀ=g±d•ËbåV,ú²°|%ƒ5ü½zÐ{*u§¬yýÃ’1Om&›%ÖÛò+§g½ô#)ö Ë óF#)h~{vöév#)Þ1ïLÌAðYE) 3I|#òÓ·éÉÃ~\5@6½ƒ~lìý]ºÒwªæÞ®F`#;“²ª$¸ùäÇøíü›oªÏ;>f:~Er™ÜNã½£…ÕRë>>Qšßžc0ª*ÝX®n±.ùªÖrþS_1±¬™Î7FL30tÃ#)=?÷í9œïÈ ÇË÷r¿‡-_@ n´ù¾ŒÈ’ ¿IñÈoÜïG4†Rý-xÙÍÝhôqŽƒQVÕEm‰«Ú~#‚V›fBü·g³V<[Ž’éúH:n)¯YmïʧŸï¶ÖDÓnà"FÞLrÆV }Å@e™Ã÷ü¥'êQ{··ã‡´0ïädw8½=§ÛAò‰ó>¤àsÏ=Z=Ï«„fІâ|lúXg9 à´¢»ÏxÖ05„õ/«œ-×kŽ?æù–>(ÿ'‘|†d=\:8;_o›öïžw#¥t+D_-K‚J Áb¹³H®÷òþ;lÔç~ʼ_ˆd‘úâ!Ûœ»HHóSá¯öT@j¢Ô¤îR!Ó/›š5. 1lB˜iʉôÄí˜ønzó0nì&-ÄrçëùM7*hÿŸ´4Ê)9§–P’üäZïN¡s¹­JwóÑóâvÖ8Q¨MQD°ÜÊÈÅ_G,^^ÊOÙœÒáÉ™ÌDh†æýºàü,t—å I»eü·àÿW£Žy›5&&ý¯Ÿ¥™|ºsO‰elQúÛ5#ù>ø,]à~œó|s¾u:-Ž´ë8ÇoŽÚtm¯^¦qÌùÈ<á_¢Þ#;k™ˆ§¢~ÆX#e𪯽z«Â[ólj3iŸ¶ ;TB›’z-±9Ã{´c$ÛË,Éà¢é×jj?ÕµZ:wÚqô¶ãðž˜#ƒëu´ÄuÍö­S•ùÖQɵÅx¦¸qþ×|MZà·‰Pºú*ü<¯!¡àTëxŠÃëfÅ˦`×pÓDª€Â%‘Óž ß#<É@Œ2جû¯îÍç‘á?¹Æb3íP3ß0NìÁãœÑ×Óû*L»«6‡œm,vžcºÒ#<£’¿gã†=xãñ„oÛg bÄÕË™™hÍ'  9‚]à±Ùެ¼,‚æn¥4|ioé,ûâÆ|ZY­z?Ð4pÖŸhû¿éú\Ç©«$§eé‹jšŽßøÑÂàL%°°¾éƒñhvBâiÍHB8®!þØ\°"íSêaÉ#;xךIõ§Ðñd#;ØSÓá[õ£AìI))„˰üžŒ2':1MÛ (½e»ÃÚ$MgÓ1Êt{‰˜´lóóý…öpéžE—»F™ã×­Ly ÆŽe¦v|ùeðŒmŽÎ#)«Ir8üGÜœ–~k˜stvý½cñLkÛÍ<$léõaI3Ý?[ðé7þ ç6xj îÌêí‰Æú~ìaÆYõæŒiÇ1u_Ùñp)_„@ÞÿˆAÌ#;f¢*¹úv¾¸lë´~ÒÞ–ÅËAó$ùA9rPIÍ&ÛÆYðT¿UvJ‡FëªgC~NçWg´È{´k‹ÂëÃϮߠøuÓšþ›Ý #;¸W+åfZNŒ­( 3±‡ ŒR?VúJ½ªìˆuC®²,¡h««Ì„¿­áž“ŒˆŒMãl>Ü?v}!ô#;ë½JaÉ=Ì<Ùy’¡É7FªAÂtæ¤Dõ3 òIÞå4B['© Řá% ©y•iípV[näÈc(¯Ž‘ëÙÒý0|>™©Kæ†/1†ó)#gßïÍqtb½ô‰¼Â$°7 ÒCùZ!þˆl5kCif@#)Œ1€÷Œdª,‰x  Üøâ QÙUˆ©ˆuÕ!Û4µµçíÿ?Ÿy„ñüsªØã  õyckîåñh°\ø|µ¨R,+t, Î邱_Öùûäƒ~ûo+ÅçcâR&_¢1NUt)£ðfS#ËnÊOvÎC~[w’y###)fQu¨nmVøÍ,(s"7Нž9uïÞfpü¾oç$pÁ3?ßêÛ6ûjcH€Imýn_'c[ˆ?Ÿt`Èyà~ˆ…ÃúÊvŸ©:dR,v”ŸÎ@!dÞ5GßÓ¯›kE´ô5ÞÓæ)Pµò…wx%Q|ŠÅþ'3€#NH#‡úOæ?a™ÁÕéqÒÉä3«TøYßùñûƒ—ßò.x#; ¢ÚBÑ@4«È)òHšŒyÀ8®!Ù„O7˜O s&Ï#)¬Ÿ…ß·u/'0úâ˜EŠ~©GAåcŠP~Uĸ<Ia²C­]~¯³ã×[Tö9C):CÇ^‘ãŠ7æ8¿aÇ‘Zç–´(Ĺiþº2†1å·qNžòƒ5€tØŸÔžqY$N˜ í'8m†!ÕìÅ·g¹,ås¦6Iåƒç’$$Ÿé*Æ ˆŒ@r~à~“ð}á(?2õæÈ 2³Hí³_?#;´”„ä’œÀÍö)ÿœ«»õeÝ’!öà ÚüƒÃk>Ô8ŸÁ7Úu¬‡Ê\K% )æaž$>ï…êÏûW¸^ÔüòÒí–®Áß’é)òH”Ç%‰ò s•‘m ç¯{ÜìçÊ·Mܽ#)“L‹Ø  ƒÕ„ykéȸb:b÷g"¡Å×ß®#˜NS·5Í6Û˜üø0#<Ó]1ç@ù扜z²‹#)!,Ô#)tΛ…Ä=ÒëÔþ©…ÉwÇ¡¬UzÖ 3˜,¡† z‘ȱ]¼´Q%x^»]©d·Òi†Sâx4òã@n•¦ÝòWŽNrÄ;6„MíÈJÕVKILÃ)Øpãøë¿;y­RF^¦v:N>ÄÝÔuÑŽ2ð‘ÆK„@°†“ìºf^£Ñ*YUñe‡^¯SZÌBc˜$<[ŸÛ'ñôã6r9‹ H½'{>åv‰+&`Ã)U@¶çúH¶4#)äS¡K:èŽÙtné"ñ=bÖ¨|ÎÒéZõ÷ì`pºC‡\Åâ¸Ù§š@¬.s–*ÑÝ0ô\è[u¨"0ïÎ!æîÖL\œm6¦ï??[²_ÎE´Œó“y²šØ;Ÿ½0‘#;×¶.e·Yy8dr˜ß?g|{K4ç¥2{}®<Ö–›F ÛåK…”êkŒïy>§S°¢ä£êLCY£Ô$®ñ®s+§Êê#;&i¨¬u#S3þ_fë.Ϫ:«ø%Ó]} œŸ®‚ë_ëÃ0ÁðE·û3¿¡ÔÙ¦ 4¬Ok;IÞæ¦:Ï©µ#<¶}TÖ+'‰¯[-uÔïlû®ºyuÜÏÞ¹×{”ºùá#<&aVÊÄ:’”£:h*õœHrMIjÜR)ÔÇc}tiïêÁdš4Â3kÍêº_ªGZùéM‚Ë`N{tëž|ÿ¦¸=Э„‹öÅ’´ì˜VÞY÷¿–‡=½»ÉUŠè›ëóŽw³¾K‰òðƒ‚Iš».¨¹Î{LÆ6òÇowÛƒíÆs¢VxöçNLvê$Dâï¡ÛV¶É xËœÔзª‡°ˆg –)Òê1‚_t3)Bš2¸^«˜tÓÍ£¤œEüT•ÔTj¥sMó+­x‡yÏ^ª÷#<Ó}­¤éág±ÛŒÓ\ÿýW`ì]C’#

Æ•F^뽅ɹ,AžtP>¢HmK‚‚ª=Ë¢¦>Óÿ£Tº#<‘òÿ…9ì¦~çÌÍL+@ÿåRUÚ¥ µR`j&hhø½ŒÆí)::‰ë÷½­!µ§l ´¸rTÎ9“Â\èô¹?ã«”4ÍÂ=µá×)êãe“¤8üqúÎs¯½ÏX.éÇcûH@L\½dȼ³ÄßÁ‡n³´ŠÚ’HæK·f¡gÞŠ‘ ‡üW/¯¼°‰j‰ÜYÓÏ2Àg#;dêÏ+å³×Æ ´ñÝg›a\€M¥j#9éÛsKƒïªßj¶/åÞçä”Ï8oÊgˆÅá#\ ›P5£ªÚÜtœ$™#)â5‘[|ã¤ã5ÉÝ]w•@¼Jä`êÞ;AK7,<#;Œ H7IË$²#;‘C1œ¥\l¼‡,i×U4;]ëMÔ_ÐýÆ|-¦Wá§sIb0Ï™ãÅg òõ·‹›lxdí^&ŽÀï{[™NaêÉœçR#)¦ ’÷³óµÚ žÑÍòëUË å¬3”MͧmáÂd|ì“¡qÜÆtgÏŽÎøõуVw:ÃoX¦á´0"Û4혂û#x“^ù¿·çGÏB0‘Ó”šˆˆÆ#)n†ñ‹¬F<Òkj•~ا^Yå4JÙy‰£‘vm ,7,î@y+ÆOOj•ã÷øpÄv¯ß†q7T#¤#;åèògNÿdm=‹b×tÊ]¶à’Ž(¨–G›øI-ó@þ”brÂþ‘ŸUlj¬¯ÜKw›¢[iT~—E|¢üÔhÃÑ3Œ‰oª¿VTÂÓáAàur qÐwn’ìo‡0 Û—Ç‹IÚfQÜœµntbW¦jxôDã;ù½/¤Õ7‚0Ö¨¡vA„Ä:ôÈLj„㽃å~(-}@ŠFÊ;Ðɤ”¹‚vЗª!ÍÛnç¡ÁÁðòðÌõC¸i„47N‘¼‡ÓeÁØf‚¡ØÍD }™j:{gH­õë±ÈíÐVÛ?•Òê™»nЋ0NX^ÔÂ[KÓ5ðEuòº!¼³ëg+šsPüQ b4aÐ Ê¿Ãææë´×ÍÌùo¬¼Á»ŽëØì΃Ĉ\Ï}®°îQòûàÚý'"=¬ðOŸAÎÞ;ruUBàðÖ»&#;mA@þ©ô@.^ (ŽzÝ‹a'9úÊ«`Žr9ÐMÊ;”Å#‘t#–a~ÚpööççáËoà~n¡A²L„ŠhpÉójÉɼT`${aA &ÛÃ%gؾuœ¹¾¤CmŸl=Ñhƒáïàx"L|8K.û÷ 0MœYÕŒxƒ¸û‘œíöŸ_ãyá°ÛrÛÔƒÔdGGcìFÞµí²[¤-6I~žçQÞ«†jÁêŠ-åºã|áë¢û¥äT‚âôp”ÝŽÙÊpÜ¢1\[Më0o›…Ì„BŒ÷êxЇÏ‰OàÍ”%µûñàv°Ðd£'go¯ÃÅŸ›‘IbÅñ^€Ëä¾.°½4H,û/ÂtQ äáºu½Ào¢¢t”è[À6EtïsÝ\µ tñe g˜£ˆÈÉ[üpÃR·Ž®L'F.üãàÁé}“-¶/'~Íó¥5§:F<áÚ•»ïM÷Õ>×z<٥ѵ¾;!j:ª§ñ¦0ý>X뛲\îÌ£Oht”ÊàŠ|0êãEö냖uÑ¡I?-û‡|ë­O„ÝÍ«¤csèƒZ™hÞðún’^üÐh­ãHìm»|¹Š*v…ÁºýߨÇÊwÊÄR8jÙ§åGPÀî(“k„¬ÁÒZ·2­mLõr£P`0×1Y‹‡-ϹÕKG(„]¹ î+òF®½zÞý@ë®#<£ðy¼°7‡ªc¦McŒhî×-ÅgáMGR@ÿ/îgâõùËü$åR+jä5 Æ"xPbHá ð¤-1âÖ„ 8¸¹/v4P1=Š‹PŽ"cଣpægbÏÌ""Îüüüýoιò눾@Ø#<…™öDOŠwR1Ó\ì=6;‚ëõ­¦ªƒ20N ¤%Á#;3XÉÌMåa•{!KÜ›0TƯ`€ÞVÌV‡‚¾zKÕ•]sÜ8iP§_ó~œŸ£ßê”{?'Ð5ƒ»‰MÃîWéãg´ž$ÿ‡ÃõÙì¯Ùè”o¦Ôï–+ úŒ1´#hÄ¡Á¤ç$ó輇B¦Š‰Rƒqó_çZÝ%-Ó£IÑ0ðdÈN“äaHãyBr*E(Să„eþð¾¯ÂŽ %éÂìX#™B'=6„´ ˜©$ä`W«âìø?Ãî襪#;º".}§”sbíVoÓñ] ò½ïÁ­8ø#)>ùgÑÍÆÜ»BÐ#;NÙ¤\¼1”뛃géÕû_Ó⿱Á#ŸéûŒyÆ^if4eàÛÆ uÛ Cy×K<á(,D?€Ð€ªr\§o##<Œ5Ô6í,Ýþ?ˆZüÎίÙüW•Ê«;ß÷îtáü#)Cø}~ì|ØTg#<¸ª Ô$ˆÁe#<Ýä¥éûö/‹×ýÔŒ#<7Úž?–ÏÇv¯`ËõþùГüHˆ(Z,åñý>ôC6›+Yÿ‡¬4¿Õ*©)þÙû‡€{?£U~x{ ¢òíÄÊÅÀ0š©`ñÚÀð6ê-ßëgøK{tÚøwØÔûáváá¡a·ù§ &Űm@£øƒ' ;ÑÌpsìFÝÕÞo6*šNÖàD'CÀŽ\įO3jOWŠ#“^;‚ÃÃ…S>ð˜Å ±+ØÉùä(ÈR8¡0–º ¼Ãý®aeTíMuöx›@Ô.L=éŸ#;N>ÀQçöy¥:L>µgëPÀ$/úMÍ<áÀÒãû·~eÌúö”#<; CCéG²#P>½_ö=7óî¿ú,ªh+ðŒ º¿1=¢oK-#£ ܹú.î ÓS0ô®ÞŸ/. ™†ùª§Ôn(#;ÒO¿×½¹’vÔ½cªnBpý~=Gó®ã׿٧§GcÎý›Ý]µ_½¾^ï8nÃYÝnðJròûÁîåh.wkì=§&ä™rIâ4Ôªõ{ ²AåþLÄG"\@²#)#¨HäC ç×#I~A #<Åb›ï¢›­Cá<ŠÇƒ*CÙÞ²QâFOÛ¿oe5[lÚ¨¹'»åà }}ˆòǹ;Ž ;ŽkÄëðmà ½h#;>òM†áÛ¹%Ó1òëé7ÓÞp½§…±²*#;Ô°l1‹¬dI$ZÌ8ŽZ½ï. ¹à´¥²@÷{‹fÈ@Št—2›½ …òÝóBBJV|óIù?#)Êä"zÿÍÔ<åãð#<« ý_Í—]mN²æ#;Dà.ÆÎØý޲”^´„ÿF¾<÷ß#;iv#0ze†ó#)ömo<É!”—¦pÏ·*ºÇú1¾?ÛCóþú±÷“°’irêep5H[=fîÞ8ëÏÞä;žhƒÖ‡meÖAõU¡J— Q‘ÑcÍ=ÄÙëëÇ …87>|Ò›Ù—#;¹ýðè–"²H‡RJrÅ“éËË©4-IJÿãé =²«ãošÇÔW絞۶ÑÏ®}óý'Á •› Ë#w¨jwënÃ~ås9u‰û†:Š…#)‡…¤0ù8‡ËíÝòJ(.6*¢wš›“v‡Þ¿Í«,þ=C;e!¯àjBß!O RQŸvKà#)‡ …µ"¿Y*W¸éÛ79ˆ@4üÿÜ*©!è?3ÌK)Ÿêtñ* *˜D¨ uésÞoos¸!µM"hYNó»d7›j\²qsÎÄXôƬ—kçÔÀeî ;ZŸƒÝŽRËŽ‘lYñ‹vI,…UœÕ*ªŽšÕi ?w—4„îãœôèAÔ*X¿^ì=Æ\”Ï€jõs‡a7æõà8de#;§C¨Ítƒ½ßñÓc0íK6Ð3t;K˜`(¢FŸ¶Ì,IïþýI§ÑŸ¸ÀaeÔÛúb»Ïâ1i‚‘šÚ £¯3ó#Ù>¼U^ ƒaá÷ÿdµ*Ç|RÇÛ9_Âèw~x–ª{oÓèú¾ßhP[­Gy@ãÖ4²€’„`"0‚'¡)›ù' #<»<-¸Ýiå9›¤#<¦ß×(hCöI³g°Þ±«Hˆ ÐÆ&‡eÍe—9Í`>™æ*--bÁÂ(4#)D œCkA£øøíÜÞD‹‘ÁÍàQ÷z$Јh#°ÈCP‡ã ÏãùÿätCoÚþn¾ò ŠdW«±MÆôüY+c ³³Ÿ¨‰s•,Þ)G´|0Æb‡Ç$î#;3P}¼›f*ÂB‰ú’®ç&ƒºMוöÒ!þ†FÙƒ'\¡ú5’è^à …°ñ\9™fèSM&Æ¡L AÄRG-…4f¦õ336"ÓaÈbú{…ô÷•õ†¦±ØOd ,<¥0*Ï;?ãÖI<7a¬8ö¢ò –F=éלèõQ…Ï-LÞ°¶ŒË©†!#;RIÁAp3!ùŸ¯¯ò~Ü'Oä V;‡ýo¼Ð˜Å0F/AP¤þôýuZ? -]Òo±ÉÀÎfÌÕ°K©Š¡~Ë¥…ªNŸâ¸1MâŒ+c¦1¦%°¡Êh±F#»;2Ç›ÄãœÿŸ¦q¶µÆÙ˜Ñ²!ö£brûÆ?8“½&mlçIÞgïÎÖ\îé8í,¦t%u‘µš5šzÔƒL³LmS¬#;5$ K¬ÇYfW)H†‹&íuä’IšíˆÛZRiSWzš’ÒÖÛMæk3VZõ˜ôÊÌ #¯u uë:ï½tò䈋@üïõÚ6hD:vÔ²]êNb(¤&}o!ùwéŠÏØ~ËôþÓf—4>°¼ÛæZú- £#<5Ž>¢Ù¡UÞ¬³X0IA#)ä)l¢“húA“$éÿžÜkÉ¡d­î.I&؆‡…\²’šÎÝ+ß3/al(R'Z{γÕMølrÊ{ß#â™1ßsÕ‘Ý~íŽÈ¾&e`öK—¥ðÉMÂzC7÷¬Y±ÀÃÖùÑ¢ÉÏu)s»W~ôŒ #ç¶þʬG³í)´ü¦¿V€Q÷Ñe½ç©±ôë‘[‡ÔÆÇ?ËùÍ2q»Â"zü|ZDWñNTþð°³³¶‚Kì_º}Ûr?ìjoÛž ^¦žQ~@2þ£Ìü5è©ïü‰"Q²2Bí26)š®®leºÓ[ˆ[5ß[Ýš¥• Å#)Á‰JÉ,éJ‰‘û{î/¨á³­®Å¥xmèmÇ’ú"s,#)¾i>¢¾x~!åùþgÈ~ºÜ‡I3‰œ¥ª~'¨{ç„WÒ@,@KFB@å1Ú·¤´?Á´dÔ¥#;è ºÈHFᯇ_9ì èºRuìÀ.çavñÃì¸ò@£P]Oèˆ`GñW~ͼðËñfXs"4#)#)A#)ïnÎðY8Ü}ŽàJ…þ[¸ÆïÞ=¸ä ÓÝßû2¹¾)>Šû­R}ؼZø$·¹ñK>ÊZ"ˆ—ÞnDõ–; ü‹…ž‚zàúÿýüƒkŸÊO™Ç@9ýKɳïe‡OŸbnà ¤! þc¬!ö4<¿k™Ï{æ8‡á>0ì„XÆœ€ñïý¯ØwÀ†Ú§8»XÀþ¸~;Ý»|Sòœ:ˆ{U;ˆ©A7G€Ÿ.å@&&iF£øÃÍ`Š@=\:ºë¿ÉàyÓ½£#)Øô³N{‚VÀÎ<@éê}=…KZ´åØânP‡ {{RPy‹€’!FB>pšzÂÎÕë «âªÓ"€M¾}—Õëñ­{ܨƒc]#Cë#;ðÏ«=O¸ô~_m¡ïÚUöKÉ-)ª3P,Ä$=øŒ’I>t Ч¸~gà¡¿ùvUŠÈGD,#;#)Y »Óúüé“ïíQ;cìôz¸7¼»HZ|ž¿ÂQßb£{”×ÏGHÔ[†¶¶2Ô÷ùñxÒƒxsFÝ*Bäóè'œU½?‡»‚66‹‘Jå_Œ÷Ô•€ÀlŸ`iÁñÈ,BumcâU~¨Ï©Œ’~*#)¡ƒÁw‡U„>0;3MÎ,'Ì©¨µ}w‰ËóÜÌŠ:å Y4Ô½BðyiH}¦J²!z«Q¥GÊJ8: €Õ>Ô2¦s$q±zòp‰`±'¦_ë¥Dã³±­B‡”úýl†b'š¨ŒdEQ*BÎ:½yóvÚhÌsx’-ÈÛG¢-Ž %Ϭ® ÞúøúÓoW72)˜E^#í<¶Lñ†DY>~°¡$´Z•{9>Pë;Î#;¬BĤJ(~ÞgQvÆ#;W#< ±FÊ¥`×=½åN½ä{ 3D>žÏS¡ëDóÔ¾c@ús{;C¸„G#¿Ÿ)ØH+æAØn5Ú}0òûÒÛa 9”ÚªQ{ž`=ÿKûG›ûßÚ…¾ýâY¯Æ:Þwä_ØÑ…ç\ Ûߢ±MåT"JýkÿUPê0Œš“ú!QGþj¤¡_ùÜØr ¶ðÒˆ80UÛ@¾¹#q›RÕ DÙfé¯ÛßáæžÓÍ'”õZØ;¢ŽÄq¡–4(‹ø3Uí|O½žußÕð’^¨kl1W0QþTÏàð^&ÇÀˆmøÊ>#)Pä±÷¤'`'.'¿ò¾Ç‘Àðc*”T¦-O¥O$ä~ 2ûòEÌ"†ûÜ5ª Óæ(8Ê;)˜]Ós=S²c“- ô¹=®¼ïÈBÈÀ§Í°ßÊpëæ8>!cÞ\°X¥y…#<…£‡ÐEþ#3ê• kE´,ó{–P;:‚U ?«ís:':(>I4—ýN~‡óNYõ@'½ž«0Âæ~(6R?¶kxXj—råxoQoZQ‰qûˆÄ©œËe#) $ˆuT-Á#;CÌÕ H#` 0Ð{è J.Í‹t„„ƒ%Âì"^‚Ê•;¢. ’R#)Â#<x”U Ds]Š€fzƒõdb*Àƒ¢j65åú$žURzŸh’\˜vó w #<$#<0€âçzŽsÉýJÑU5îÀ…Šˆ ‘9#)ÚÂ’E;¯¹#;»S¨?4Jwí7š©¡´(úö–%‚ƒ¼`ùBPX‘F5û?›õŸ?9T·°ýV^®z¯¾B#;ûß’VûTÎ xò¨CV¯• abÀû,ã± A‚Œcâ¸*"lX²Je4i¼ú|õ[ä±¹£Ùª+›¤kmÙ#;H4& ¬¶j»j.âPHÙAl)@,˜2Ÿigd;ÃÙöû¾í%þ 9'éYøú§¸=Añ}¼ ‚PPp_EÐ4ùÙƒ!~$€K^“ç sOw¥èŒ5v¤r#;#)°÷;MÏáÐÔ 7i7Ô#;ÐMz ™U,Œb2#…#)8Å34”QÙÒ,‘¬iÀä éG%,-f'™KÉè”mxóvðHIG£.£çne=Bñ!oãE¥¥æ;†6hÝÁ;7§éžÚ)'*ãÜ`fcª‡¤@#;§‡ŽÂ=#)¾ÝÅ»¢¼y´:µ×aö|Ÿ;ï¿Ï|MI}°“‘ u%X—²R~£²@Ÿ¥ÏvJ7=¿šÏ¸<0H~!Hú~;°ÿ {x³{[eûsÓÎ8ô¹Š"uŸ’°RC3½=¹{¼\ÛÏq@ é©%xº^78§×¹øÝ>èþåFe-¶m÷‡Sîõ6±ñ©Pó¿6ÏåDÙø‹B­-<0Ó3òÈkp ó9Z"*ßÏ™d•d’Gçé`ü¸Éœ;ö[xZÀ51 ÐfT´}sW²¾=êû;?É ©q¥<‚ÈF¸~‰Y7™Œ2EŒT–x3Ô†KhªTÁS³³KÈ´††§(Cì1—ûÇì‚ÿukõOö#<;×çý Õ]-ì~ Ÿ‰þ§´Ÿ¢×Õk¯d?Žø^ø©Ù™¹þ¤?½ºÐMþ³ŸñQáñÈv.ß¿¥D¥_N^r ÿ:iÊo`Ü’S—Ô}f.w'«ÏQDAøª´UÌc8Pd',)ÊŽëøl È¿P¿JŠ ìÁ_0bŠÆ#<„ÒŠX~L)5 íNÎdåêVµ¯‹¶R@„H?¥_Ò‘Ø@ƒó8ì‰óœB¼;ß½-ÏíR@tè«H¥,A ‹€ƒŠÎð»cÇCy°.=GP†iæ@4ô~cºæß¶2Óø¡’tËÀÝÅp#)ð3â¯x~W¨2 ³Š÷|?“ÅTÉP#;6"nvžžãžjv&o³~£Ø§Xu‡`qz¢s“–ù D,*Ü?mp\ˆšWcÄ=6){.o°®dáÄ <@ò=%ÔEaÞd`ÐË(7ø}‡Ó O—Ëé<òý\ð*ˆA)-m5ÛÆ'ÑæHÊJ=È-OŸÅGóÀzG®ec½žáîs‘Á;1“>ST|£ Ð’J£ÃšžMV £âÀxæîú£Èò9c+=^Ït=Þàj¢Ä„øþ_#;ßîúïCÊ $é=;Ã1`üJN(½¤{ŒÈCãÄñG¢vƒÉ„㤤#;Šñ{~ÿ7i&Ûfâ#GT†$M¦¶(ÄK×^¹-º¨bžZØ·ýTv_ß{Ÿäæs¦qàÉ-«:ëT9TªMäÙQ\§%é,èÔ±2íaH5‡Ë¸[mŒL²»lëPåÚ¿éö!Ê'Þ›;‰ÇŸ—~óŒŸ#;ÿbuU*ò-]‰ø6pK)ÑäÕ˜åêññ›Méw<Å ¯ÎPz‹¯n]¨±Yv¦3íÓ…¤R¼Öâ¡=´ÿ—ÈË꜀Ü~,¶¡ËxcÌ30¹çm¼í3Gg³¼ÞÀv*Ðþ2*nSÉ:ÎMÆ‘‰è@æA«ÀFÑ,1@)HG+×eº(Opz^‚>\ÉÖ…ž ðɳƒ‚UdUQ-"ÙÈ8;û8œxΆӴ،"î)…„zÏâ6ãmPmÀRyÀ_T(Îy³¯#<™Cü;Ï‹æ5(n:XxzÆT<&…sðF·O LSà€¾œL:œLøE#)úìáðí=Ï“õkýLÏ@§ï#),"=#)ó÷WѽŸÜ’h[wR•ézs꜀ƒŒW©‚M >1È4’‘Ä0Qèlï„qA†¾×êàÏz¿3_œ*~Z•ÙEç-¬ h¿Î“\“ù¹úŒ§?CõÐs9ªÞ$Î/ùWûðlìë+ìãXÎ#)sáöª»gð÷ãö^÷¦¼v#;Ú]¿ÌÃ2sœøSŠš ’"7dizT@@$Æè#;°æa¯D€¨ý¡Ì³¿p…‚ƒLH4‡Ó€-ØÇ‹ƒAY تœKâ¿ÀÈ«ÜáÔÌèûÐÞ‚Çï‚ÈgÊ#<„‹,ˆÿs°ðÐ.¼ƒpóN§Ÿze0D†@X:ÄÉ A^á"©`Ùç:Î.Λ3h*;¾ŸÏµ¸úô'©vs«žR‰Æ-Ž…yîK8'RªdÏ¡‘?2>¢¼ÛŠØèwä]kê¦ì¡89˜#Œ6v·ò#ªÞ´ö|ß/³|ûuûPGS÷ÕðùHa‘@#<¼ "±RÚ!Šn´V¢‚H}O½èÞ!“ùZÛ\N"ŒGòó¯Ã/%d#çg#2„#;ÁõFM«ôø|~³ó=DÌ÷ïù%܃ùý”yiȤQyÄ*##)t¹ï]áhDƒ,#<ñÝ}ðzVaµãù±‚\ýûzƒ©:Gâ ¡æý¢~,‰Í°Wà‡Ø£ûEËéÉ}#)‰í”;Ü¨Ø 2ñ™‚Wöõ¡ìêŽp„FÂÐALDho\ë¦e²yJwžm»ayx•`”Ã#<–È}âD%‚/ÆêyI˜àÓmLT"ÊV Õƒ¤‘‰Ž§\jÅLÆ&ÔÿÒÎÃ5?¾©K“ý9ãÄ4)Fùf^ZBèK’ÉyùÃtݰb M’û¯C¥e±a¤Å¨w…¸¦ÅF¢FË&ù@ãmœK&‰h^¹à ˆF4nIò2Ñqckhïèw07 ¸Æ¯4RJs‹1ý©™­ ljC‰#YW¹a·Ò|¬úh?ÚþåÐ( j€paÞ†¦Kd -¨|94†S8‚Ôd"H}úÌ^êÂZ«ÏôáÙº?wu?.²Çãô_4rý4\Xß¾}¯¶¨X|~¦oŒÀVò”M3úØ.ý–ï;KÜ Ðzä5–¥ÚP3a€èíh¢ñº8v‘8mدBü„ƒ€Ö.òúÕQN›î .j7—ûÆ&ŒÝ,“+æ!IŒx~ê÷+ ›ðN»ªO#³†ÇŸìƒ_/«Ž9Ÿ?Ú2/£c¸¿Ù¼ÚÈ$ÞñfØ- ÙþÜg†§C?±¶+««ÛÙÎ!ä4™ñžÒHɆz%\ØõLE3g;fÉ+Ã’ c.l†3fgÎFdRýÇX6³0äx𥓓²†÷Ó‘Kz¨£a²̃"Æ£ùJÌW“‚ñ~Oƒ=þÕçaa"±e6Ä=Ü•{¹e#;£ôH?ƃ~xïù»Y»ð˜ÿ–»è{&­Ö¶ª`m¬¢wµG#<(ø¼ðòÅðè¬é`éÙxUâ_ªÿëþ÷#é—¥R[76Ÿ’”.Ä8¤CÕ•!ú x ýßmº¾ÎöÈ’åý\L¡Còr#;dh ªŽ€P ÃÖÈ4¹®<ŽȾ ÑÎÈ«ÐúîÔS†Nósök÷o4ä®è+Q0)ÌH²ÿ`v.Í·Ó©—Œâ÷kŒ Q6]®e {=»;Ÿ‚câÄn5».²öIâ#)I%&Vv'nðO“¶{>Úf1ä'a!мŸós¬ mÊ#)#;2Á@Ã7]Wý2QýúãÞý&—#;ñ·U˜iç¸dñH}. ðd5#<‚çUpsçàz#;4žƒå.Aú]·ÙÙðµêípÐeæ÷‚À<ÎÓ#<"¡Â;Ä·º?2ü”køÌN°ž2Q1K”MßIN'×ï_7 ¶#<æÊàÖ Ä #;¬¡ù_G#;SPÁíóH4•ïT9TéX_5‚MÏVRoïO_ó¯N›-HL]•¬Åi=¹­üp¤=9rïÍ?EœHè9Èâ,;¶ø |7¿N½¬¢ (+„›b«ºK¶â!ñÉðʧÆB‹× /œ\³wtž^Pu¿#<´¨”Xª¤Iµ;©ûõ²’•ÿÉ霿}^i^o?®îs®ŸP¨ÏY³0õ÷\(ëNɲº{)òYçÇÊN’8á¹ïNï8ÆîSG$,Å£?tïèí„Zsô[m;_^ì>Ü!ao\6ZÁÈ Rž2Òˆ0^+kñQ­ËásœMïœ þG)‡=>.}ž¡ø¶†àšëÿZ;K€ùò#÷‹‰²4®”Ôé¤_¢¿NGõMð·‰â)aË?@#!ù‘ø#;Àkäª G 3iç•61"4{Cº MBR#;é¥ÛÆbžðvöûn°,a·˜ß•R>r5¨qí~´:%r/‚2{ݯ'Q¶H L…ð¿wŽo†Ãph]{ ɘLƒd6fª¯søHwöó³k˜ìD~¯ñÈëd´@›Ï3ð ¨pôõuËT!)‰DI5Êçi±RR\¤yXÉœCù{YMÖ\(Îc9vóDeðšÚn‚bØGÉÂŽþÌùÎrè]d ¬A"«#<}Ó‚mݳ)+ÀãIc.$¨M‘AæaI#;üñ†„Œ•H°­ÈÂê²G“˜F ½yùæ*à›õÎcbÖ“4Ät¸S#;PáÉ 9‹‘€Å.¿›¾å~œè™ î,¢ã=3Ú ,Yû'Éø”à›ŸÓŧôþå–6€ABPÉñö Á×ÿqoG „Òóûñ¦–#< }QìÁý‡¯°å®›!°æŽËB~ ~ƒ>•iü°Üô›è~³_Lá£@5eLËû~¾€þ9sø~ñâ“üÌúFâå<é¨iëKÁËüØÈ·®ªªÃþCØÀº˜0͘ÃjÜP6mÎ$‡ç °w?Ä#;·±Ãh·Ì?`QvæãDi¤$¤HÅŒÌñOñ\¡‹Ð<¶yM𣇉¦¶*ã'‹ƒs¡Ó6`p¼Ä#;#<#;öêínÜx‡ƒ´;TÁÛ¡t4ŽÀ2-ï1¬žDâ'ù2!Ó…r*Н?­8dNž/ùÎmÃÈÔHO!åÖvq ÚÙ•øx^ƒ¥ëYRRï”6UE…JIX|ÆÌÄ–1ù£_ë4ÙDb •JÕR¨ÕØvI¨u;27»ÖiöàÔ ¡ÏyË­†¸ÁØð„¡:¸ûý‰_ßo#<þT,äd‚Å7‚¹@1B¬é9qôÅX‡Ÿè=!¡ :´5ÛE¾2ŠeµŒ4ž>…4À¸=Ÿ¾">ýÈcíߣ ø,Mj÷ÄÝi£#;¾ÂjjÍÆÌ¦õRÐÖsÌî$à Æ#;@ cXb ‰¡ŒD€¸RAšZ,#;7ØQÿ› èü¶\¾:ö°ƒ˜gHA˜ŠÀ”fXWyà.§¢Tì—ÏÅw3xòp™~–Jhw¤’tŽÜFrÜ mÏ‹yð''!;ÁB¥B}[}^Mš>²ByøyÉØ¹}Þþ«Ý-Éû!{8jˆÔ8¾hýníõÔYž‹;7¨0#)iĪAG^waúô$‡£K†ˆUQ[4DeˆHÒ‚Ì‘1Œ#mÄ,„KYX»-„%PÐȰAÂ]Âqð¢ÙýÅþ…#€D’F@’­+ÆÜ¦F<¯ë¿•ýïµ·´Ô­)c2ÈÍljÆ¿ƒ®ïygu×rÛ›j~מO#®¶vn#uÕö%¨´ªU?õ“àŒE¬]·f „#;M²ýÊøÏÂ#){ü?3á^ûmLÅéœ0dâöø5€I¶¯÷ˆÂAâ¤:z³º¶•±Ùåàr·ùæÛ¦Flj¡ÄCåâ>>'5ЪŠÄDv½]ãáüSË‘“Û†Al†‹K¡c`ýÆ#<"†®E#;MRhHØÛ›Xh22}€B‹ºiñø¯•YmFÁ{ ÈzÍJF«ÿ ]аcòdgÒÔ¹^ópÀp´‰X9¬4H`ÁpÞRíXã¿ÅN凔d^ C3¤“‘ú|ÛÍá5*¨¤²¢àCcdøÑN '§#<†Ç`rY7,žoO<óÀÌ㢲<)#¦0­äv};aÚN½\ ò9¶îf+©…‘mK0›|^n‰#r×S’Ùf­»¤x¯LÃP›èqÙED£àq¢Xƒ‰xm&µ«ŽN#;÷ØNî(x°¢O5;—nú#;ª¼—°;>§©´ŽæqÁð5#<Óa2&ÈÿÁ¢ͺädÊ#'±t#@€ @9r!µjØÅæÍyêœë·}wÙ«0ÌÀ°¡…ìɬ ñ3 L3 -Â’!Ýbsàå50t7q—n$Ûc+¹[ie™Ž¯ͪ+‘ì«u#Q#Æž&Ø·²üàÄ%ÁfÌ8IÞþÔ~µ“É‹ÛÙ„wF {`±QÜíȸ#Ãv0HpÐ9ÛigE¹n¸QÆ™åZÝNÐîï:sè„dÙjJqU„ÝF#»o¾­ÊôÙÄ)É€:1;_Œ6)Dälé™8”{´±¸Ç`–¢ÂÜ¿fòE¢øÅSc«vnKjøÈI ñGÈ«¦†i8XÀð÷™3ı‰I±±cÃ×ÀkŽTSj‚Î}ÝRή˜lÇ¡¬\Ý(ˆ¤¦É4ôçRõÓ°…Q”qè«7t¸z¦í8Î8Â'¨ÁA-ª‡×ÐŒÁÈÌóî ±‰W讲9l£Ó^zÖß+ÕkšÑÉ¢(W¨¼e͈pwöA$C~ÇF¢mÒNLæt”]8í5Ls*5¦åfÑ C´ƒßuÜÜn0îx)vMQ‘¨…J¨€ÈÂ`´¤’rùµÞ¾»¯Ã}f1Û–9ÊZ:ä@&à+Ë×­LÁ(ˆv…37fdË™™™clƲ¬Ë3$•c¶gš;•ócŠ®{óÖ#<ä…VÔTÒW)EB  ²A`€Ã¶©EŠ…¼;ÂñvT£1.ýo»Ô<)³·°¨†É㣣-ÍÎ *–éå[‘¾÷g4Î$!3¸í¶î #<ëg¿‰­nª¢³;R(3—›Ò.™\Ï‚uºyÆ% Äˆ2OZ–eÇU‚ȘB¢’l³ÎÄj©¶D˜ bvB>g3™1¡“¦èÝ`E,ó#<Û-%l;Ú: Çðó7 ¡˜šmtld Å©#ÜÅ ¬eÜ ‘GÃn×µ1æ°§!”æòk Vm;¤¸'hÚ#;‘·2.8}o§u¬Ã¨r×Yîg­TU²&XÝx==a¬šrHrÚ0²ÂT µh¨™ÿ\ Hs›w"¢­j#)¼ËóŽÁ š©Í€WiÝå·2uQòï¸NuˆȇÑ/ð7*õêZJ#;­ã„pu–áЪ†#"#;aÀاšsK h†hRSàú9ú¥ô“f’n­„©w”#bÓ¾ræÈêŒddö&2––mI ‰,ù RÓ¤–.a;Ìuñ= )ãbr¬[ÖhήšHóÔÃàé+›ö…¹Œ§§#g‘`ÍšE£ƒ¶nó.¢¡ŒöÓ_MBÎi$—Lj®‰øœR½sôµ)³ŒÏ²Q…ð»ÒÌ8dv™Ä¸<«–¶¹D8²µÀxC\e#<ˆÑÚ¢up^ÀEîhP`K4lÊS‡È'‚ûÎImòsv#Y~m¦QÖv;>Z±ˆ„)mßyâ˜v#;Þ&¹í«Ë¸]ì-0æÞ%ZBF 's)¥0£ZŽ¥Á"KË#ª¢^Á:YC£4õN;@ÕD†¸â“»ãUåÐûŽ ‡nÀÀ#‰À“"RÉchšlÕ3›Þß#<øˆ]äYQÐt2¬Ôz:Ž€¾gˆmÕ\Êh~ïT–ì»55é '[Øsí{ŒèîøP;·´Å:dc2ŸwÛPŽàòKä0i[Éàæx‰sÐ0óuC¨ %$ØÉLšÃ|ñ)h.9í è°5¿„6$“áUžâc)#Ùzô]yûö”’£Á¨¿9íb$R0§w€w„¼¾>7,QîƒW5l ¨t›V¦„›D§_Ì·£„¡ÅS¨ùkW.µ&oHcHhbHÀù!œx09+80|}Ã`–ìc¬Sd›¥•d’\¦‘ö é:  Xžx†Ëì6PÐü˜ÉÃ#ð0Lö[Ÿú¼Ÿ¸0•>¶¾öý¯Íû i¤“2–Ëi™¶L±#4E)”iîê$ÍüfÝjTÊdQ«6ûu7ÚùûÓ #<}œútw È: 5MQ#)ªÖ^L  ÁD}Ÿ#<¹!B;—}×T–¨»%ª¨¯÷š4 F+81áß¹à"Qï‰$$$¥¯p¸Îô4z,ƒûÏÈ'øä€]ªAB#;A@)Q$ÛJ ™½$9¤_.G;#)ã¥âv¾l”ÿŽ£”#;mU~yÔ<*•bĪ(zJ¦‹»ß#( lüd¿¬R0V0Ú6–jŠyï¤/õR! º€ìåòݬjØ¢)¨Šˆ)I¥ˆ¬E3&šY–¯×ëõ$T’_Ž»oêóU`„ä~Ìà©_ˆª0@X‘# )€ÙT#;¨ðR(ÑC˜w3<â ï¤,¸¦`D„P¨)À6iÆ—µNÙûéUªiÌÝôÅÇéãÍòA‡C[n¼:J._6¸ÌîÖ:$ÃÓ–vííµð»Æ5é¶#%¯W­È0JÕaœAHDGqU£?ÎÙrFã·‡|ò$=«fgd<÷úl?Œ:aÉ$“‰Ï ¢ùû0V˜÷•åŠ  d/ó°bQam~à¹%Šà”G¨RÃC°%‘ÅO¶fÊŸ"#)›~ªÙPµ‰@Ž ¤¶zka#;AŒ+ëŠVçÝ¥x„±º‹àðíÓÈîÇb‡`}zŽ]¾­=!#)#)ݰsbá#)|БUBBEuåß0ö€0"»}JÆÕËERÒY´©&Öö.CÀŽCÊ ?Þ€*Š#)Ucm«(&Ú±¶ÔŠšÚ#< b±‰Žúd(zº$žÎÏdª¢ƒXDáõ<Ñ“D럌–#;¥ñ…em®lNµƒXáLâ#"&A`b!ó–*ØWÊö¯miêúM$ŸX¦ns5é‹Ò½)$‹z»™^=*ÜÞ¥½MÍv&ÅÝÚ /.®UËwìyæ¯9&9ÒwW7K%eqPDÍДJìl”#Z1l$Rˆ #3*?é¤1Ü#<fƒ¸¡X1¼­ß×ß96A M[µ·Ù·àüR/!0‚zfH\4ùJfX²ëذí`ª‚±íkæÉíÂüÿDþ¬¿i\š¢™&Úp•!äÊKª,ißêÆÃ ÙÜyž8ôsñé¯{LX1-¨\$|K|Ï,Ü/£ó]2¶ ·¡ôvËö÷lõ½Ç·-ÛŽ‡cÁàC䪒ÊéY7¾+]Þ¯ùp›7e6½\¸Cí„$¸¨Ks¡<8ÚHK=^-¬?Hjè!é€#)H€È!3{‡½‡N³îBˆ‰ïÉÌéöéQ–¡Z§¨.f&ä-]ÝPÀãÛ!rdý…÷¶É¢¨Ô+¨T/j½þIããTž£§[!™4Øhlb9Ôœþ¾š¸#1w§³¡ ÖÝ¥ú¾Û–]õC¡×h•SIÜ ûÏÇ~¾¾ÂˆBUJ©E'.ÿ¤8rN'™¢Š G`®ÔöPl¥ðé=B|YF€¨"|Ìb6*#;Uó¬¼Ï£l[Y¢«êTçÑe+è(z£`/TI ‚ÄÃÀòȾ#<#Ž xàöJ:˜YƒsÕ¶–ÄFØ›ØÝ•~XÉ#;âQ«ì£©FΖl9EG‡£†\ÞZ|Œ—àî®;™´ÒnË\­?‰ÛƼóv…à$¶ð|ÌÑ„ˆ”DÉ2Ž5ˆFšÅZ6Û%j6ÉÍXÒM”IQ’±Yi¶¬M*’ÒM±h­3U‘•¦eV•µ5[3Tž£ŠIÆ}3•¾™Rs†ô#;¢'™E€"H" t¯JŸ!ö¬7õŒ(GG¨¡(#<Xa8ˆv9l}ÝÃ÷Þü·î¼[ªšhç-!‘‡›þH®G9¡a=ÃÖAå^ƒÌ'ÀñS͆"²½_@}!ÒÓÙ!ð?üŸÉöÿ'çõÆþݾØ!ó@Pó±’,=sŸ*-EЧµˆH®¿µE¯A 7LUIiT—d0“½¡$–¨Of´Aƒ>—U ”P@¨„D}UU ˜ðy™à|w»ÙM¢³‘£:Ga)…H6Ô6Ïöb¢Þ. ~“ôƒ»ç»rÖ!y^Ì]¹ʱ®´/@úÙöeÐ'º™ܨWõ{ÝÒ€….k1ø¼– ÛÓÖj ÷Œ·õRuuÎýçö‡HÜëW¢„^ #;X!“M"Aœ¾˜`EH;yàò8fè'’v5ÚB úï / 5ó5éeÉN#<~|[F•ã\”ÑM¿“à쉺šIˆ«ùœ³Ëƒ¤å0êBƒ!&#ª1SHþËèåÛ÷õ;Ï«ÀîøÑä°^<ÊŒ¨Ñ 'y;Rƒ>Óf+gc÷çA”?rŠÙ„kg¥Øøy óð15ã–|@yQÚëævopt}Ô<íŠ>‹¶§󸫔Jœ¥çt×Zé¨ù}9L’ª„šÁâLÃ, R0úžOiôÕÖžrå#ôO(hmß‹\ý+ÃN$îyÈlxé˜òßê^Œe×þ›†÷(?ëÅÛÛØUÐN¸#«Èˆd4BÌçT)D ÛsL޲5ül ÞAœ~WxÖúô›ãbr!hêူo#;±¦,ªjçZÉeÏŠÿ »³YÖqvC$3èCƒe&S˜€J#<‰.ššíXRkaP„¯ÄF(’ˆ°ªzÎû%ª¬ XùƒÍG Cà#)é¢SX€m;÷œ-:è{'j”p$—çû}úi‚%Ïé?«Ó”4¤(5e5TTQ¨ˆ,ΟWiéËœR#cðmÝÛw(Q²X}ÿÓþ&é½´#ÊŽÌcBûa»ñ³½Àãh¬–ÈÕ#Ùò1ˆpÏæåÏL¯uyØ¢ˆÌ}=Õ4ß2j°šL•’“‰T–(î`ŽF;öƒE(‰ÙIܰŒ‹¤\Ɉ9ƒ.ÆÁòéí¯"@ÇRùYHƒÃkšÜã¢-ºïx=Šd”1BåCÂØo´-¬Ø¡ôA`eëÇ+î€#%KMà‚åɇ½ùq%ò)­ô8+Â#<‰íÐ벺:& &ÄÉ¡MìÕ‚ÌNPWQÙ6‘x( Òð#¡sÅC² .!¬1|⌕âË –]ƒð7eeí„í´Ø§qF;jÌt,½ùQ»PÜ-ôT&ʲŽÜ>Ùã¤r¶§œÝ6öf‡£•ÊŒ#;†ëÒú‹¯nxõÆT†žqÌ=p&ò–ÁÁ}Üëg^M³µ\9pß‚½ÁNÍŒ¿L[›wϼZ¨ÄÓ<€X>8>¨YJ:.çŽ[ZÌ5©Þ‹`Ñ0¤õ39Øä]ÍúÌDy^Þ­š›¥ÔTø9S #<<Ë> Nl:ôÞ}88a®ÄÈn§”Éà7صOfN×ݨ©f…#< ã…ê6Ò:K‡¸à‚¾ÇÜÅÇöˆ£=Á ý·7o|†òTÒX€G)²oÙÙzðG¥ÉßâoC]øjqÉùã DɈ”zßo›#;ûNè)’ÏŸ™Ô9WÈÎû|#;suô‚êÎìJ,ô¨r•¡Õ!Ö¦i©©GíðôdG‰®¤‰8¡¾Ý/„•?R:q1æ+ ÎKŸ-teV ., ´ÚrdÍêÛ¸BˆB#lŸW^«Óu@‚ù\ :ý™5Ù½@›Ø>?#ÔF@â#)OGKaÍD£î­4¶šÛ'[rŠY²k]76S;«©³r.MA„§Ŷ†l¢”]B(£F¬AFRŽ6"±Àq0PAi)¤‹©Ç±»‘Âä6E’Ƥí´dá,˜EAÄ„"±AÌ£zª÷_ ½ï«F¤jk%3 )"$`»¬CôCµNÈ<¹naHÉ#;%‹8ôÐU9¸€‡Eë*n˜Ä) hpOBQåºío:.´íÖì²_ƒË¼d´0¹Í‰µ~ ®Þ#;„SÙHÊ› @*Hˆ ’iݰÉ@¨E7ÏÈ\‡ÆÁí{“ÁÕ]"¿„‘EMÚ,/¶¬VQƒh­%ié0÷†ôSÔbÛ§¤.?7XÎkE1pT(Tæ…1x•ï£óž$Ñ¥Ë(×SÙúg\dF×q±R4ËQ÷eæåm3°T—ˆ[“CïÓnÞÇŠB½ê­4,µâÔ6íS´ ¹¢§Ù²´À#\{ÞYu•^8v$§sz(ŸÍ×oŸ¦Ì†‰µ‹ˆD‹­1£ ãQøxÁ¦rü%Žß#<¬‘®Y²©¾]Qk7Ú[¾³'Ÿ!¡0Ãä%q¢ÿC°¼7kÄßn5³4@pœÕåhP*àvëÉVˆ°àQ]ø> Ô3§®øZñ8Âî…$¨)º–Rç‰Ãl<ëEpR÷ó9w.pŽ<“Èèâ‡ãÊ5ÑF/Õ±™š¤n/RôîÊ8Ÿ¿[ÚIƒÝ®øfi‘̬ uƒËÆ/y$’' =;ËÈÝÚü½Lð÷óÕ.¨#wvA"Öá°„èˆl¢íÄ$ Í#;†¼õ*Ú­FÃ`Ü ¯«J@È"‘„ÉØh]¼2 H#²‘¸pK5Qz 5ÕZt%Î^E†A‘ K dN/ñs{²»¾f" 3Á#;@˜Jë?¸Ø G);TV¢ @+^ÂÚ()¨Ž6;˜q^{ƒm«Ñb‹AТ ªª•Tb,ê_¹„&$ê3æõÛ†ö#)!Üh©Ú‰[Žž«kÏÓŽ"—Ìy9bT²syEît Ô¥G ÞȨª«ø·š_7¸¼Âdˆ3àˆF‰Ì™(Å&l¦9ƒêÔÜ;ü?$DâRB1“{"±Æ †G uBlT*ꪤ’˜üj˪TŠH£YœD k #<ÇŠcE­ý…P†6–ft Bè7dd"E GÝ×^]]Rlh²›o:êI ÷ÚºL@˜Ÿ¦ÂÏ¥4T¢§nº»Ü Ù«*îNrHÜ ÆžwÓEˆÄc&ì…1l"GˆQÖ™î¿.¾Ò¥›ZÉDÈdDd@ã`œjF›=¸"°­.¤Âˆ).ª›#;©šÁ>ç‹4Em$L©% ˆ²w/{ùók7Ì z i²B6Æ‹ïúhÃ#)ˆž6˜ÿ(÷ÓÞMEÀpÊyxh©¾u|”Aeà“­Òß3èÄuŽ¢ŽßÒ`‚ “Ãîjªl(™àòÜÞ #)gåH°< ¦tüî¶2HnzœYb9è‹ÒÍsùPªdP’Äñ֎8r(‰V1(i#;Ó74ƒ.Û&Ë;ºV™‚L|³’Nm7Ä¥Nox–Ô„jƒÛÍï¿oosºÌ0ˆ't—+d(Â`¤j D[uК‘ wùP·Ä¥6“@ˆì‰Êa1N"³*×ÑZœͦí¢‚Q¸›ÄÈæj÷~ù‡t|g`oöëÔÙ:ËfÜmmÍz­\LÍy<ã«êÏ2#u4Å|¶ß–­=ÎúÁHÕíF¢r¶@¥IÓI¶úÉC¹O›M#<ÈMoY%øi¸ã>#;"çó«°Ï¥Qy¥I.bWœ"+–:DUsPÊSƒÉfeÅebÜæ³äì}þ¸ƒ~âêô )xx8„qrõAÕ'iºÞ#<¹T‹ˆ#)þä Ôè%àgìg [¿Y>‡æFÒU­}åd¥ÊdÉL³TÍ2e¦¤Ó‘TZfmIWîݦa6´!!ʉ¥L°SJh¢aEX’_B鯇[˜dÖ“IDÓ+fÑ“&)šf’2ch‘Q¶,¡Ø”/ŸuE4bÌ…&H%™=–®ˆLËE b²¢Š$™”Â(¥’в6˜¡5¦)ll†R‰I’™M@e*LÄ i ¦VH##;dó'*¦ósŒ0õè'ÊB¶œZmi[Æ?#;¸‘GX„R0ðe÷?ª®w"&"gÂw)¸§ÎMQñg„–vŒè÷‡ÓÏø…Z­6hÄ<æÕHçñ|!pÓ4ÓbÆšes-§§#ÑQÿu™Á”&½h•áRºW¾¹cš¥v¡J; ¥H[6Õa}l¶ÂsµÊqA[Ehæ]#)%O²aÍiÍä ÙlI5óXѪù5¾LZ‚]&P/”ÇMµ;r½æ2å¿R%ãyv|éÃÂi…”;RÜÓN⨼ٮéthc ³k¹žJÂjáøM*UB—Ü÷yÚœ·„‘"Ö£;I†£Ç\än2Ð<áßëʾUbÞ©ï¾ÉjÐ]‡SÔtAžŠ |4FbÛ>Ló'RÙ—#;vGQðÄ vÔGÑ”la) #›0L£-Ô‘P;Ä=o‘ÚåžO§®kaõuë²z·ñònÏ7@†ÚHCžÿœ¥[ìØìÝ^߀vvØú½ª¥‡ÐÝõ×+ßõV“­!RCÚM‰¹’ë´‡/~]ÏÔ$ª vÖ­ÀNµûg¹FwÚë#)篈+Ê’A@ª±ísœ¸jX™Æ7MÄM²$ò}mÑ‘¯ÖÕzkr1ÆÍVe¼šÓllɪ(=E6æ±ÂŒtÇ 2yu÷èl‡ËXÌž5ŽqFÝÕVeÓ¤Î2ÍLšzfd53lve9¤m3N>¡"‹-)1ŒÏÜœôœާ4³ZUœÙšš£Íd[´Å×[Ö"e‹ †ff"éëÃ$Õ{z#;WVL\J¯gO¤ñy{•j%8òÐÔΟªª™rq†#XéN,*œö? ®6Æm•/ÆaÓ`À?#;{˜˜²‘Y »UUÄq4î–!(µP¦¤‰Ò„R%Žf¥¤²Ô hIÓê·=1ˆÚÍ â‘BÔëŒëä4°ŠœA…¡cN8Ž'Hyz—ƒ;FŒ—y§†Óêe#mÃHÚÝÉ ˆa˜Ý@oµI#u#M`BeÒßOõ#;väÐÓº³óŠwyR9i×/x^ûÁøÆv¹¾*$Ú±5-ާS6ïʱQ˜E²2t%éÇ.Ã)2’<¸áäéëècŸè^xçˆ7¼Y8áE¹e1gMÜÆÙŒI³-tŽ/+1$VföÖùß;co¾Ùƒ5ÃæŸ;Þñ<#ÈÜ{—"£Oê-ómâ¹ÌôõßRŽ™Û2.›t55}h|âÑ-xïˆté'ÆD ÊÎ4µ#Ú˜ÕV{c‰ÎÙÂH‹K4ñ®ü=pue©FÑ #[ÎøÆk-‘KƒžwÆŸ˜Ì::EÚVBözŒ;ׂ†âŠÙ÷ŒînFdlB'­åm˜¢ÇÓô©Þ¶j‡¢‰#J”¿A™!1 ^MêÌaI§£^¦uÖÎr“Tc’Ç“cOÆ"|Ln˜·bóªbfÎË!¤FØûÄ€½%(kPÅMqdIkZ2q¨¢^÷¢ÀPÔ"aËDa†5«€ƒ1Šq/˜(ªa…À7DŠÔ•ÆåN%Ú -R]:$éÁUâñXNéT#*|^q6Ϋ¾<6ÌÎ0’ÉQÑù#•fë#H™L³‡¸Á¼ÞÛpqÂ6iÍs&û?/ìB½Þmåái”ÃÆ„ew˜k‡ð‡íÓcˆ<ŒYz$—‹vç#¶]1 ¦PUßKp::™œg%šZZk¼aubcœ&y¢1Vîg#<…‰IùÓIÎåÒ2€ƒ‚ ™®é¶“êU߬ìqÎMAt†bÂÅÑ&6’¡èX‚ÂëúMTà~$ãï£z7Á¬§¶I—N~Y.ÈAí-º¼Õ–î’A—xqı$µŒÌã4áµAúc´§ÅD©iNÛgcˆ‰Ø%ó&s0ùM ç74]žº´â±HšsvÞsÄ\n!ƒY#<ËÄÂ\dÄ$*„•Î7UN#æ ‚Á4<áÚ µ3 +w⊙„¡R“S3(d2S;ÓÕóŠU¶\·—#h •M‰y©€êç ¼KaÃDã¾–Í{®a*y±Ýiá§`Üœì7§dt3mîãš3+Z­æ3<$Þ¬´J# „aêO‰‰œ#<ÇåŽ+Ò#+SÍ5tì*Ä,ÁÞs:Ëã¥UWㇼ<ÌP¼¨~i÷Ø£•”‚ùทw2¸*#;ç>2è—lg(wœ•¶Ü&R4ÊÛƒ2Fm·§d´^Œ+ÓA´ÖÓì…ÐÍÙ%#ûkZçTmÖàٲ٬¼G¬tsI“l…tlgˆ1œb³mR=LÞj8¨OÇ”Qƒ¬53]OC7Â{ev=¸fW,|ê¼XËÍÖî¦+ªo%vJ'K£&¸Ù³í6ÙdrrÁViú©”S«I±ªWœÄ†ã§as¸ÌíŦÓB桺¤;ªÙ:§ ø¼³Vïa;;SatrËUW8ºE¬Wd7e߆ÌÞuò©HîßFkŒ<å²’kšZ¸RiœÔ›6ض€Á¦É^²”apà¥K&úò´jHß/:Z—«AÚ ¼ªPŽ"Œ9k¨¨“RÍ»Õ@Ò PÓE`Ž0¨å5ÎüVŽINôlÎ3œ$9\“r¾-Df4iáU¡4ÒB˜D¢æIŠBG{Ëb–è¡ULÓ­MCœªc³e×oÚ“ºmGZÞ7j»_v ‰y¡^¼²nNd€§ ·¨&0©597W¢ñ±7ÌûàÏy¼€nsÀI2¥œs ¶KÕÈðú`Л ¦á}[T(ÈyîÜ•§0°1‰'LIF#<èn˜ÕFEšÎM§5p&è+ò©#µ‚:°Æ-õ[鑊°oG ²ã"é#8×’È8†9‘!#;ØÊgV ææ‰!šÃ¡øŽ&WºÏC£ÏÓq·dŽ«[e¡ÀCÇõ'1$Ñ⑼IºÐ/:6ÉÑ&*¸°´2ìÀ±žpÍH£Ž¢ðbÆ•­qQÙ“^êˆÅÃK³9h#92rÐq¨Ú.§f±šnQ3Ɖ»8mu&ŽŽÑLPa†ŒˆÉ ÓJ$Àá%%¸G{µ"á˜Ú´KuM,IQtfF#;P‚W#<›¹L3ÖÆ+d2Ú¤D2†™h˜B9â1AÁ#Œ²2pj'©˜-ˆH Åî˜Û;§¢,&ÎR ¢Ez¬<†o\{TxŸïÅÁØ| h\ô:’ràr„ަ•QŒÐZC )#;RP¦C €3#<(ÆÁÑÄ:Å™R9\u<#<¨ð Ð]•4‚¼ëV*3ZMu"Äm=©Cüç@zS”¶%ÔäðGhAÅš²‘eQL")¬œyÞßnv(mÜqŠÍÊÌÕƒzŽÿJ¤c5'6“#J#;¨Àë}u ã©*W:ÚuÒà,æ Ã¨ho%AÖ˜+RÌóX4A¢£7Ôb†Ö6©±Bª—Ê[#)£2ÍWI$ÎÃ¥Šé¡¹mI©Ç|H¡a,@ÆÆDÉPÎÐY#;VS^hÓl #¼‡³YŽp%%o% 8›±Å“¤DC–rL@l™lŽÐÍ‘§)c*ÆlÏ-Éaƒs1a±Š2:b7BciRŠn”ÑHt4hAkDÈ ´Þls£=9ud˜#ÄÃHf7t‡$2fŒã%Æm#;ãÄ °È‚â[кa8&Eˆj4f­ ލh—ÄÈ{&œf8ÛANA2‚ضk;é#;]©œ$Ó"F7.….‡\‰‘°h³—CQÈ46ä&NÅÁ““ &Ĥt#)3³¹´Ì#<68j»Àß^¥VÀI¬ÝQ™¦k®¥3#võˆf[=™Ùs#6{ï^ZóJf–“M¼—lÑ­¸¤’(RânuVƒ£±™&t9~ÌÌ L0‘oJV"´r˜Š©M•˜§1D%ÁŒfØŒ 0#)åùÇ´é’™À'‚j£Ñ±à~#;h‹ü¤‘Ú˜ñ¯§›ä.X¼¸h"ÀõÃ(ÞIc"hªƒ½÷žê¿aWákjm 4–¨¶,jCkEX“F¢&ŒÓ2Ѩ5ØØ¯£ø_/>¯§kF®Ê }ÊðÄO’\øS/FêØ£â¥w©Õ»¿Ì¹&A’ô.ŽÃÇé¥Gi¯ŠÏFJ‹¥'äÅXÄfÕÅ \ETAÑ”3KQtÊŠ‚AFÁµQÄ0¥8€"A¸#¿¸œ(Ú>µìå;§ªææðÜ*¤(®‡œð¢×Ò迲Ö^²äõÍ#;㾫býÖ¢Ðìùdl¸âI펚„vR¼ìZ%CyÇfÅ\…µÖG‘¹²Lq[ƒæûä6²ÈŒ˜m’TRGZ$ZL2Õʺ[|›z—¦Þ„U3[ů6LbÙ”¥4ÑX&ã#<Ò噢‹ Ž!ÆR&lcƒ8®åV–ì«Þùà©ùN6]ˆñªbF0DŠªƒ‰di0`_ª–øxf0Þ£æ*]øå2w¸æºKJ¹Îj†Mí4[ŽóHLU1¦4ÇÏ~½µ±r”¦A„$(.×{à ¨÷¢®´¤qŽ”4Ìè-‘Nf­i5 ¢ßr0 ´¦  !Kε5Ä7¸Œ!²#)°&ˆMMØY—@]dfÌï:±løÐ™<¢wþb›”]’1$ØÆV‚R$˜ˆ ±¦Šsõ„$.¸.4¨þ&$‰# ¨„T‚-¬(•f0”! ¥â+ñ²îŠ"ƒ ‚Œ(;‰™Wcõ$D$t ÷ñõdœf F†ŽžNiêœU·˜þu C¼EXi­´*ÂÈÚÛ”w÷hb(ÝŸ­ª¹eß#<³–ä1¤ÒaAzU˜º 4gJÿÇÐÓh¬Õ ¨¹û35¡Þdë§V¬×Þa†Ü‘jê;Aá‹´‡/¼Ú+{:uÓDwÓÇšÃvZ.hhE7JÀͬ¬PåÊ4¤pç4‰˜6fm#;ÍÙ97ImuLòâØE_÷ ’p‡C™5XBd[³#)­ä¥ª{eN0EÆÑl„RŸ+6;'ØÔLK¥¨,L’+|Hb E’‡PXÓÜ£aá­b#Ëô ÌÎŒ§6ß°{ŽNÐß^#<ÛãDp-r%Ðg6nC‚xþzN¶ ý:à™ÊBcÀH;’vX"Ø¡:§*¦Ð`ù[Ý4sc1£¸^Ž˜ l¶ ‚¬ÌQ圆ö#£ò4ÀèpºÂº1Jê°­t<2˜ÓiBu”ã-X&#€’—Ši£­ˆñ[ãfQŒ9gFFžÌvŲl­f³£]ž!#\EpwÅ50)¾ÕÃðøO¥¯¨‘#<ÿ6dÃðW*ÓŠE'²ƒÔ‚¥Ôc}äÂP)‡Ö?çâ#<•óQǬRâ.Lw575}ô!fçí.oˆŸ½Ñ„âgGO¿„ \JøÈsžs*®›µ£ U¤½ËGÏ/%2škJ–3&R2«í7«à¼àþÅ –Ë8%g울š7Ç6ÒºK—/´e¸1Í×\<Ã;ª:×3Õ²åá %r'mŽoZvYâ©-”ËÉ•j¦aÓeJ%Å,µ™NµÍ9yÅÚèë9bé axëTé)Š7¾ö£šV©DMN: Ã[H›ƒ$8EÛ+5Ò­qŒeáÃqcF˜Â‘¥hÔ‹Q´Õb䢔š¢­:P,цŒF!Ü™L÷9²µÓkÎÜ’J¾)§"$Ïá¬;2æó#;ó²¥mª=rœ8íÏ3(4N%Š‚BZ1)C=¦Ä4lô­ÔàÛ~fŽÌbãÑ–©×‡aÍp29aßs¶m®"ïQxg…„-U&/¥tÎW¦iLÌcô´NéÝ‘˜yœob”Šv––%šßOÅ:,›LÉ­õ NšŸy‚ ɽO+•‡Û˜Ò!æ¥j+¥–ÕóŽlކöÅÏ#<àô$'K9W/fÂl›PÖ`Ós_“KuÖæÂa±"H€‚„mJmdÆÔ›Rլ͵‹äbàn@w£VéJ¤‹$”Á`‹LÁG%Nå”§_{¶ÿ4pàª1U2*–”‡XA…:šXÂ0x 4- ´0i¶#;‰´PÔ'¼,6P$‰,Ž_æ¤s7Fá[`¥‘]üÄ¥7d! à´m´@®¥™õœiëZ íÖ Øm¶¯?‚\ä®î¶Ý îëeºlwW¤œôòñ»—ÞeÜu×]çuç'žqKÖ&·K4Ü«¡WkuWNíÙ7Šå¯]®L´í<Ýw™W“bBÛ©[†ÅvY‚ %Æ„h層ƒ\Ì…»Îݸ¯m¸¶«ÆÅJV™¶I Mf¨Ûi²¢™®¥®•e¦É­,©²Ûe–};}^h¶ Áci”Z²UQªÐ•P4Ô–c C™o_ é£‘ÁbÏš“X6"¬Pšiw0™„A¢(¢_żH)“ˆ” €÷@Ed^Á`‡/¦#ÀˆzÏÛ`ög‰ì!\Gô°¢PÅ ïã€BÊf›>&ß«4óy »XL`’Làˆ‚0æu‰éP_P'ÉÙ„ÒlÀËÂsaážÌÌl#;ekà]#)‘ˆÛûéê1i(¸(“ͪÓm8昪yžÞrfnOâLÚÑšAèŸcøì¨Ì÷¡ªQ¢"$h*CACúð@V&Њ³(£3" hº›©­»jbÛ塳I½£²)¬”~žœ8YN³a 9Ôû8o¥«p-"n˜om.#q‘Ý#;@°(‘å[ZÊ CdëC¤ …./^ùõ]S)h"¹˜m寷÷¼¸uj»^,ßê$úûm#”“ͳ•Ô~˜*ìe¡F~ÏÔd_çû©Ë?¢å$’"-㘠7‚Bò˜"´m¤wÎÔ[I2óµÒÊ&¦{;R¨4’oVécfD¬Ô›d˜Ö7›µk™¥SXÈ¥´›(ØÔ¥…,ÍIR™)Zb–m¢%#F[ò.ØSZÊf6†b¦$Ú(Ö´Ö©¢*õÝÚ»¨ÚI¶WuÔµ }{]ªë^Ýv4TÄh$ŒÌ¶ÚQ-JkRVÀjj1©L*¾J«µ¾N˜%‚£{÷l™²(µ‹%±¶­R$˜Z›kEªåu³%¢e&[M%¥lÈÙ^yæÞ¨•›Mš4ÈÛik#;j-–ËI½œZ­+c´^*è¬ÙgŠëÎä”eT›LšÆk¶–¤Þ ¬µã’Å4É"ÛBQªð» Å‹ 0’ ÞÌzx?Voƒn§¾÷3²ZË«|:K(~â´²®|J:f|ß7}z»{u%±§~å @?”Kv"©Ýè|"#;õéÎãFd(öç«ì‹ùOš'žeJŒˆáØ•Z'Y„ ÙnX±Da!”×jÛWÂÛBSáÝ3RÓifiˆ(@A#)… ì£}p±#;F³JÙ¥mìª×KjŒ L$,ÒT¨TÖ¨¢ ŠTY$Q‡‚¶R*fZ¢S`fâ•%" °[nZÞËZÜ©5¶[66b–’˜›[ôUÑBQ©IZûý]¶ÛfÙfjÛ%¶SE*So’Ü¡†$ËQY¶% ”eiI#<46›e)¤MIš6˜Ê6 †Œ­”ScdÈ–F…“V-R•AQR›*RšKi-E%R²EiDÚŠÙ´Ò¤(II‹&Ra4É4Éif¬ÛÕVD¨¥‰šÒd‘e­©e²d±¥2m)UK5µ%B˜D©U 1ŠBD 4›|fµ®š›-ZR­d°‹!"q ,\@ ÅKA˜¦Å[cVRØ/4Ú®km"€ #)ŠŠH#)eíì(ÙT¸0#)í÷Ð<?güÎÄíÄ€yq²L­ìt"~ãs.9»÷fg³f<ûWõ0htþ;Gû¸®™Ê&…ß<)âZèD8 »º[ „F1q‚žšR’¤Áçu=p¤.Fð:n©j‰! 8óßoo9Üj[̸¡ñ‰ýÝ2Âz!Æ<#Û Yíx†EØb¿_V5Õ³®»±lж²‚f=%ÑÙ´ÐÉrd¥´”;¹T¥v˜—UóK_ðíÀUYþ¶¡£n—ÙŽg\¡Ð§ï6$¼"Á„†ò<€§3™¬$ŒiŒÉÆµŠ¢2Œ~°ÐG´¯Ì.?˜ ·¤¥ºP\‡ù ƒè7mÁ¶Ž¹µ7@zt# z;RÁz/B$Ù^$@Â÷¬@üUs–xW*IRI!%H=]À#)XªU6ß9WuÛr§I2”@+@æXÙž ‚-dNtº/3[X®Ú«J+©ŠOtñ¾þ© "°ÉÓyâZ ¡ŠÑEIUƒa86(‘*R°…†jUQ©²k0'Ü$.!!0“Õlø#<‚z¶"鯡ðî]‡ÏK´o:yÛzìÝãJQe×â€ñI #)×€?¥>¦&XÜzÉ« £ä‚ðd‡éI¤ÿW÷E°*å§T*†R((a”Å>X»ˆþÆòÔ¦C"Á ¡@¡ƒû¿†íÝ–™]*ÙlQ´2²’!/5Q4 kMd Ù¨V¶‘ÇÆ"UEß5ºö„س!â¸{úëyëµÒÑ‹c@4¦XoµŒÎ–À3uIDh½XEXÙÃAƒmÛ*‘Fsba’#<#C"’ÓyŠÎjÄsBÅF[ªÃPª¨#.xµIã.i9ó&ý#)}hkåHH2!F’ˆD`ó]µ žÍˆâ€äãçCŽú Ð‹D1+a2¦ª&,U¡*ác3Ë¢¹(©¾ ¤Z×Ê=°í?Ãñèc%.çàF“Ä+‘ 0Þ&¤áÉÅ=¯{ºë°Åž#X…X¼´A°(Z­@7)P!x*ctHÁ¦¶÷âØéÕ[EîÕ®©b$-œùÁû>¤È²:޵¢Æ|6ìqõ˜áÓëô‡Pe°®Ý3´ š¦?tÚ¹—í ˜Åâ>ÊðüŽïª·"„©á$ík1U#;-z½972Í |%%æ…Áƒ¡ØbúF·ÑÀ[eG+6,hÝ0B&¿]¤³(¨Ó¾ ©P!+~Ïåë?N†í6ÃãXÝYx¯\S´!žh<;f!–ÜgôÔ.iôt;06ôÅÆà-±V$©ûÐÁˆÖT–Öòbþí1æD34t„δCª#<°-¤Êoƒ ¿–`›¦y±ºu~ [™m‘¶¹ÓR' GÕôäÈ0bBí4ЏHøhâj&’“Ìh÷ü7Qƒ!±¸MÄŠ922á@Ò…ÆŒ%åHZp2;ôÙáZ« È¦qÖ+²-Á¤A— B¡ÕîXL›~IÐuG"×Á’fk\M#;HâŠTß•0ëaŒ’c#<àæN¯芩! ºâ+ŒnPn55kS¥d¼:éVx©ÄÏÕ|UA°õq‡yP¢©z2ŠÚ‚Á µEÙ•5|dzíoÅLûBˆ˜#;#)Ài b86Ay½‚bœ%CŽ“C1€/dpÀ,Ñ k -å~ž?N=G_͡¥(rÎ6ƒ¨¸]Uy)t/Èšpò t˜` bdÀuÈ‚2šs’êÚÙÄXšÇíñ„ç“Àå…ªžüÖ²%ºØìš%Û§M(j™ŠhÐ6¸#;@f#)#;*@ÙòËÓDÂî(¼?>+;.J2xÁ.7M®ne‡BBo¹®´`sšd0´ä£Ý=&ç`í/|öíR0$ Í26ÐÜŠ TyPt)Üx#Þ«Ü.AÂÆC#%È>ÇÂóBÐÔ¸h%C)3d%Ñ0M…wçëÌ×”4œ¶P AbA=Ü aÁâo48;o\“ß%‚&°ò„ ö‹JRò†³8Ql‘[¢.Ã(¤ó ÌÆ©`,4Ô.h·kÚc µDÐK%³©rœG!¦\˜°b(x›PtÕÀéáè£!#;±ýµPª¢£Q#<ŠD‹ULdªåÙ5sI´r­ÍI@ªÀ@5Ýò¼Ã `aã#d#;õ+^çƒz¢8¬ùFyNt)èÊO%숰*±P€, Ô‘ݘ™ÝA¿¦ß4Þ|¹¾Ðœ“^Esȳh¨ÈºBW^à’)2B‰@ ˆ(¼D21eÌ ]ܧåÓj®q4Ì}ßíà‹¸4‰¨\ ‘- ª‰:{¾cn2ÈQ±@ˆTD쉰¡Ûl•Èßø¹ò·w_V‹¨{òòœs9‡ˆôª¤e†CÈQ„£`ÙÖ#¢ ºšýÔ[©£/5ìÄ@&¦ò@à#lî+ûôe$=Iôìœtåo¿ø^ˆìùÏkìD($o(–ÑH¦Î!ºß6üí;ï‹0íìà=Uòx(«Cš~/iá@TT³ä´8àÄ+«îÍì>~I>^FNͤ8wõ…æÝ›þŸ€uíì#)†‚"ýa:¡ ]#; çÂÕ؃åæ¡â¯Õèx „ôI’—¼ h¸`#<¡î ¢J¥*#<‰DRA*"TRE©À#±f…â•‘ ‚È.`"SPÆP;ñ~…Mæ#;eÇqÿ?GŒ©]+EnÜk2$(·žýi°Ì"dwIÔ{ì}/j™þÀ#;ÃÎêÉ|Ÿ`ôH17'¥=©GXrxíY±•ÜŠËúD„‡Dç$$|b¥DNA¢*œíÚZ#Ô^óÅ´‘T- H®^e~~ö®¼•’ï­Ñ2—_m>~Ö a¦ŒdªÀ‰öš”þB' ÓÑM¢ŸÏ³Ö\ÉIª@†Ÿ­úºÌ{zû~æ >£Ò]ú-¯µÇ• |ÃNYx帷§}‹'äR'‰C¯·8FLCK]Êî—quÝ]®ôÝBÉÐWµY°5î%{vYdûX‰o•#<©M°‹¥T¯ïQ–3)4ÐgÉåÕu¢kub¢¨UBiü´Zâš(š SWŒö»›\®5Ky>M¹­èj¹ 2Сݒ˜`(ëGö¥íûl-ÐD@?¾#;%#;¡#r øÆ#<Y~Cu¤²–ÛdõT˜¹¢j³ÝÖ‘àQPŸ`{½Ì”1üYD"¢J’³3yÜÛWy×^L·VikT1¦Ë#)4Ÿ?êËú)Jaì È4Gõl½¸œ¹|¯¡¼<4tçÍT­-#;¡TÜÓ÷Â~h…˜A`TõîÈ$‘”Ô–­6•´­/—òéµNèÔn^­¼j¹iSIrλMâÔÚ£%*)yÕÂÜÛ¥–¤ÃclX¹j¢¶êk´XÖ§Wr[d´©´šl%‹BAP6E¸ª° j‘üÅ“xý‹üÁäXw*l  ¤-‚ ¡¶ÀÿWÄüOoBÀj#)brÎ<~Ï.ï–VžÁÔ¢Æ#;î_o ÑrCCѱT D)…$A Å@±jU ûe1 U1Qú,ÔŠâájDZ±¡xÂ`BA=ahœÃ²ò ™Dˆ‰ºë¬î¬ŸHõ§hå#hãä@‚dK¡ôÇ(…à^ª¢$Š¿¬Ð%âÔn_æ–M_m«t×®º™RËW6åoM¯­VòËÓ¢(¶KVbµGMbÅu]Õ½04+(˜©* |él#)âÉ F›D¥«Í×S,Ĥ’ÛF«I4‹%ïðíªø#;W³m5Q@\Â##†pΪұ‡Ã—Q¤K1(ÿ!N2n,,6ÜXt‚m€5—R¿2FU¹$AõC@aˆ˜Œ 1\AÂKLD)cÝÄ#)m·22UUz=»?–ó<Þ­¢õìÏë>Φ-`“tª¨qG:?Óçó|{v¼DcúÑb¡hí*`¸]—=´µƒ…#<yïÉîGï4»`¥DCgȨb€@4£l ,žpúO²¹Òqp5ƒ!í¨l㯂.à;ÑJý弯¿wå–¼s«¶î)#JZR–¬k_ÅÝÅu›v¾»cMVi_™«©ï+¨Ú"4S‚Óÿ œŽó‰€8ý^]¹ ÂC½ ÂiŽšo«MJãÞÙ€±þʼÝl™ê6ºYÐ ¤2Ó`û}wŒ~I|Ï·‰M*³Ëùût&óïµÔn¡‹1¦×ñê÷3#)èÐèqoWµ,&1„ɵ ªDâáÿ[ÃW1Sœæ¢¯ ;1 ì8+p­QH¡G#;ôÎL×K ‹u´‘"0J)aAu`ˆ†P€[Å¡ƒ4:ØP‹;(ñM³A¥‚ƒ}oU€³È}"pô#<èO ë:ÎÚB@TUU‚¢7€—ƒÓüG¨÷¼ ±Q_T6ì6ºš›#;…­èáÓLŒ½}ötT¾ºÍG-¢c½[w®Uô¬Y[Â¥9DdvBñ-!%J-ÂÿGÇpˆníu7ÉšœH5lu×^€VÛUJ$Ý͘ãcE#;LrIÎÜHSHovÕ2’J?i½‰jR­€,éÁ˜·Ñ¶»›aŠ»>ˆÒºiHDÀ˜0 D¢¥Ø#P³N6X©Õ4OB¢ûž8êûþžóvõ&H‰¬Ð8 zé ÒJ>-xF©Ïý0°LÀþ\òýx,iœ³LöS LÜÓ[|bƒØÑ³Ú­W#èq×|Ÿ)Äo÷a±“¯¬O’y(‡©ö¦ÍNuÇS%­~˜ Ù#)ÊaØò˜Úˆ$A­ÊHµªR’Pµ&Š)-4¦LkSU–¦fQ4-I˜Ka¯Ë+´I!¤,jR¥FY¦ÒÏÎæÚÍ"Õ3-i³Ce+ 0ÖÓER„)dÖij[lÓJÓmJÊÓV2Ú[f¢¤(ÕˆFYEX©k ¤Ô°Ö³D´Õ*µ¨ÕŒ…-1"U›5¯z#;OŸò|ìKªü©„ó«EÀÃ.½8a7BÙlHDØ‚àW!S¶!Ë÷%‚5d Q¢"%EFE( ¨SmÙKÎ*ó#<ŒþzÛM^–Á›)œPƒÜ=‡g8ª®ê6Ê®ms‘krÆ2u/ÉkûMío4dvÈÄ?)wD1zª+}ÞÙ`Œ‘W—;‘fsšòî¿*¹//ÔÝHÀÐ}x£ß0?nƒ´Ú‘’(²ýû#ÙÁGu³#h x°£NÅ#)|° z|ÉÜéÍ Ê H DF¢î€âÃÚœÅç`þF*f{BÿiJe!#)¢h-¢¢”D¨TÅT· ‰V[¤ q¥Ob™TÌ¸Š–¼U‹c8ó[£ Gê㮃?ð¹²uоlÞƒW Àö8¨â»±Ò@š‘ñEÂxyÔÃ4FÚÙ}E°èÌmð 0•±«‚BÎP24ÜöšäV@‰¥ËJ°×5ë§&ðOæyh‹Ðý1BÙ¢©`d h‘K‰v#;ÂÊÁ…#< 2,"F*F[™òŽgÆ#)¼àž#)PÒ3Öz2dƒÐC¤ö¢:BôÛt*ÖfcÂ-®b6uGÄÜ`x‚‰²sKNÊì1DD#)€@™3¯©aÌűH7`œaàI·—µí¯_.¤ÌQlî¢Þ[¢j°ˆTas€v?@tipÏäz°ž0%ÛϺöíxµ¦%wtt¦o[ ~·}@Á²UQfâÁÔºÔòxý]ÇGëo ûš‘A³çú?YŒ’(ˆ¦›UëõofrBŽ•ª¨§ª#;ÏÂO¯¡t0û ¶£a4÷ œq”NÑþlI`^­£ ‰":bØÆ?‡<¤ÊhÒÕ“U%5­öòËM«¬¶ÒiزõüºmÝÖ´ú§®N§#<G΂osÀìÜÓÝ©ÎRŠˆ™ž L+•Líú>”‡D òlï )&©#<§Y b²’¥% nT#<=ÿLuÚµÚ2¾TX])™^ˆÌ¥•ˆeF65÷T0o#ô~Ô|x`=è#£¤äí:#®‘QPØß!o<Ò„÷!zÉ#)ï@.±ÛºlÑPç™°€|cÝNÞá|ã˜;yСÀMC] ܵ-#;I„ŠÉ²UÍr뺳*¥T*JUßpšËû²^Û¾°yÉߦ×)çs~é¶òï[ËFÖckô<¯3Ö÷¡Kž:MåÊd¤#<»¸¬œÚŽ*¢©mA#)…dƦ¨}|l¢È¡ïk‰ >P¤D:›ˆ—¥Û,Mò]˜qK³™“T#<Œ:65²þf=SE#;Ñ’6Å3Á†V¶ÂÂÆfFÖÐ!¥‘“%›¼»ƒ¤ OkÚÌ0ÂeDŽkãÀ®.å%}RšJ bÕ"û`L#"Çúòo4A¶bK2åK+cˆN5,§kIü.MÉpúä#<©A¥®˜?•9?[Äxµµ“Œ/Ó¾mÉCá¿ãs“XnûŸî£Y™\ˆI#q1{wh1¡$Ú@šcݘÙfá¬Â 9¦cÅC¨<¬šk‡p–FlÓ¬+.°k.aŒ~²c2¬¡GIõÔiŸ¸þzÇAA¨çINö£=p龦#)¼¡åÅLèxC“©#;”ë[CéQóÐtEµad)ÄùN±%† vbô”±)úó§Ua§·ÕšW¯[‰"[4Q±í"n B/­÷ú]vÌòN¯0$‡•4@ÚÅ#£H!IAzä0aÚLëµâ»Î•×yyÎÚí ¶jÆ“UbÔkIZJM´©ËºêîîZ$Àh  #ƒ–Pd°£ìûü‘Eê~ö#)rç[‘'D“‰!,!ÈSìéke%4Â#)/â¸eþojÄVÆÆ€ŒÅ`m€Ï°•«©ŽABÙ„…2(f¨`»ÑXhð|xQfä»ÛyÖZB9ªÞºfR˜± :zîÕÍ‚¼ÏRñb£Z它K†`H’1#;…m˜ôП{ùoíÝ{+52äñÎltÙžÄ1¼©B„Ìb½(¹Ä) 9rÍ(j¼¦,J±é„k¶8(Û™\ciäRÝÓ¶Þ4›dmj„‹MÛ[5%g…8kBþæÕ M*TV£Ñˆ[å͇¯±F"ÅÃdr¡‡T 2òb¶qv”È#)€]ÁF›Ôõ„@8ú‰Â¨¸æ¦~d °!¼)ãa,g"ª#<g ;vëî§Ùîü‹#;²¨ÿ#)qO¯¾––.±G£Ì.Š£û–EïPÓj¡›þª­ºëFDL#;±¨’¼'1|<  {(J¸böÀ2’ "¢÷E<ÊÍ%•É[-“VÆ©¦µúK_µÍ]Çn®µÕR“ô{ÎíãE¼Ur›»\ª4ƒò>õ"HöOÕÔŒûâx!g»‰EÍݪ`Ëзú§ }ö³V£ð›ƒ"…9v: Èî=øÇDȧÏå>c­‡êIéƒL&Z‚ÆDùÜÛ]u«íãŠõíçü»]u®g*¿²üVE´¬¼Þéð°ÔžÅ­õ3[2w» @â]ÔÁ0 …ïöÛºéô:7M¹¾Ä0°ÄÔ ¡/O“ ”ˆÂFÆE3³G–)1{h\qÆ8âàadBdÉ·Æ'Ó¯@s°,QžªÍæðì&Ù3‘°kŒÔ˜ÜÁFAÂÉZæ[÷¼ð3´S-AïÈ¥ò—{}[bÜù›H$/çãŽæûÛuýÜíGž¾µ]†N‘vò8ÖÐBLóû%ï…pŠÞàfë"¶Šû?m7ë²Ù-)…ˆ€±G€Áµ%ÄS#bñªËePñgjØbÐR¥ÂXÆÖpM1¼\l¼N#;4úo<›ãˆ`C…Âáe€¼l0¢HHTÝP:)<”#•Ä‘m æØf©T; L]A8Fî.G’ÕlEû´MùǬ"6}0 5×ZÙé·TÔ{ÑÇ¥õ>ކ Êy{Dù·€7†:^$a|‰¡°ÝØKñOlçPˆÄBˆ£:<ÃbDqÉ *¬Ž4×µuËwnk­Öjš[JV¹kwnÕu3amlÍjº­$Ò»¯™¼»µ+ym·ÊYE‰KKm³mµcmkÊ‘åƒ`ÚSÃÂQ @ú|½ÉJÓ^¢”cY›¤ÊÝDÃRZA#–¾,#Ä| ýdD#;Mï>«r' Ê28hƒ•è$8g^oœà+“©r@·Ä@æ{§…x¶¡Pˆ¬õ6ƒíoë÷!c%ìQ8œýÚ…½Ö«åÕGªæ açˆ뇓Àó:å]kéúyÝÿÀÓbÕb0V^B­Í;4¤¨ý˜Ï%ÏÂt¬v»m‰#;ŒC Ì~äÙeG£ˆÇ>´ùÀD3f·¿ôÿÈÿ‡ú?Ãü÷Ø£ñ P¥‰Ê©k®{µæÁ”Œ¤Å‚F#çé{Bè2óíc< ít©ÆØà:#<$kp°:ˆËæŸômÃŒwËs{P5u¶ã³ðëŽ:ʪ÷ɸhAÔì×Ñ'×·]^Î [å~šƒ‘­âá0Ù‘›ª mŒ…;4ãz Jаd‚[Ê;@8tô[ЂË2ZM¶†ñûXŒ"0ïø3¹zt¶Ûì錢&à(y—Bb=0,”˜gÄ(™Ã¤Âé[èÊX2êÍm’5{ÀÜTÎ۟П‰ÌðÖ.”"0€¤5³ux#œ(§ù+…6S3Êy¼²‘Á#)õ@#€¾C±¶6‘{LKplŠëÙ†i1¤lˆF~•¦­Î¼T®…€ìñÇð}ØÒÓ\°È923ÇN/‰6¾‘ËÜcËw .ì#)wf)QY2Kß1ijr†Ö æy¬áß·7X)̉ÕSËz§¿“/ô& 5žûµC’°: æ»J‡{ §¶O(¬öu,¿GáÆ{úëñ€e'à$F$;b¢T$! <‰Å0y¸©+üAwÛŽ@=" #$µ#)5¢s›EâØµcZŠ­Eµ¨Ú*ª*5A£m&Ú+%Š¨Ú­ãZ×+&*®m[}9†Ž#<¢q0nk'oâÓÜûÊî %DL C ‘aŒD™AýÖ”‚‘²d*DdF È R¤Iðñär=ùâcÓ˜ëÅMÄUL'9rÁ„ï«|ÕݰÌ7•"«”Îæòò²F{7(fˆçŽ&3~ú8íÝ ­OÙ}þ͹Þ®bÞ@uq›©ÍI i‡òÚÌ&Û$†ŠØÓAß¡+Zå°šݤS¡(…þŽàù/7žáoÊÐ@"¡ Ôó¨¥?#;Ô€»ÍÔŠ•œÄ8˜ä•à7ßSÃÃ4ðF®Õ ›°§ðb„Ýšþª“T´8š•8ñÓ^54ˆÉï8»óÑKäHyˆCÓCÙ[ŠœT$™—\æÚ_bˆ>åîÁ´#;•€eO¤é%³1Œ\Œâ$ϾÌ6‰ã°C‰æP,`˾ÛŒÌÈß—¸¸û"!ˆ˜‚NŸ­¹$$‹ÉmþOêrEõ_¦š—ëS٥ɣ†nG‚G2$C¬Q‹£lÛTȼZ)õBððÐ`i¢d‹(i…} @Ûpƒ@*0WW2Cý‚à”‹tH–¨©b‚Å@ È?Š‹G~‡e¯ÆfrŠÓÐdEIŒu8 1Àdcá›Ã²DŒ…V6a¨…@qß÷ˆ~¯·àë÷CìÉòú{Ëc׉†!½f×WSŠîw]ÛÖß#,¶M¬¼F°(Åј5é"M¢>v6Y½F¦CŒðÀ¼¨´öÍ´5cca÷È-=ÍïâÈs³~“2a 0çÖ°“Ö—ŠN"5V%˜Z8jÍÍ­£K)QY:À-qΖ&‘“kh#;ëL\5íéÔÆ©¶? JX!ó1âké$޳ŒÄ]Eú‹ð:M"ýGÔmêî‚˺¶ 7-Î #uñ@À§ðö4ÖDîé¬c„13Ù3¼òu—àÅÏ2{Ý<}¸éi7¢¡V”[o™ËEw!_F+!a (hm|£Ç-¯Szd=2TšÕíâæ•M„føš]SÕ8ÝÈLJnoY!¡&s•\"ѳa†•Z¸ETrØ©(?YhÃÔ¸hΓ`˜bðÃÜDy{mã3¤^‘œwÕ#båþ†Ãt,Ü‘O¢ô ]"§6hFv0ՠɪDAÖ¤h²]àºe‚bÂÕ»´\‘kZEXõËPÌ%&a©&å˜!ˆDavYB H…`l6"¤[tR) E•¨4&#;`a° ¡,¢Ë €Â$K…„è1 eÃ×TTZDÛpC[þ„Ðz€ò;;}dnÐuOvú !( Ô7Œ"¬®Ø?Û´>]Ÿ¨žˆNèý¨‘’jà®rbé`>ènýñD?|â†×Ì„(W#)m¦ço¿å5ÓVvÖÛ¬QtëVýM(ɇó¼2m%oÓû·<]c»ñ««ÎÍ ¼çÜúùÃͽâ¦{4dÔ$ržåtö8j«32æ¤0&‡—#¿ƒîS­Åˆ„’„ÈÙ¯#<ÌVÑ»xÕuÖûp“ÆÎ3¸ãGhÂ,CíD¥™•ðlmUQTe¬Ì˜dÎÁm#€ù{VÚP›) #UÈœ¬èpø?4ø;ngO‚!È7ç–ê;¢4‘Cšíµƒ™wúA Î˜Ó&?ÖQ\MÐÉÌÀZ#‚·L9)7Ž}µ_ŽYÙÑ~N×±ºBÆ*“cYj{æÑÙÍýlñ ¢y¨Õ+衪,ˆ™JD²êXÚÁl°$Hnz¢`c¿*¿Q&Ÿ}¸ÔoŒ~”_Ûßãèûh08±³˜(y­-Ãn1LU£«<š`ÿX#ïP`Å„OWÙ3ݰô9^¡ǘúã }ÑTmä-Ä×’¾m¼«ëÖú¸$X9™”XÅ ¢"`C«W•_¯(´£JÚ-«ç¼µCè¤@ot • €ÉœjU›Q*”¥*–Aƒw,nÚ#-“D–”ÛoÚºV¯5:V¼zî¢ÆÖ-y×.šºÛsW;&C—mxµðÊ’ÆÒ,ÌŒT©nqÝ·wU‹Rm,¬Ú²@-¶„‚2™Œ”]ܤb±‹UxÚ·«_™ééä·…1Ô~[5}Zn°£÷[#<äµ[ŸSSÂåbôj–Cê#<¶€#;â#)BDb*0…ÓX¥(Z ³Û$Bº-Ñ)¨'ÙR‰•‹^qhjñ Ík­{my3D–àtBÉjÛ8mË3d€Ô…ÛY·VöZ±ŠÆµ)©ªFû[$R¥h*Q3Jm¢¨6-3EFFª-FÖR°XÑ£i•¤Ô•22bZ–(вËHɲÍM•¬Õçh(YÛHÀ¢"¤§Ùç™ yhhoÝS"gÓµmy^ ÒU£hH!$I,¿9Ù¶þÙöz/ãêzžKzôû¬Xÿ'¢‹¯bà6櫈xˆá1*Ð$ÔpWïs©âÕkm,m­ùZšUd³5¨¥;¡P87 ’ FAÄŠ÷E¤Ž¬—ïúº‚Ѧõ¢ÆÖwvuÖ»´RÖÜŠµ¨¤Úkºº–Šjµ5¬¯]»5–ˆ,XR •(ù\þå5‡,ÐdHƒ>E'”„žp @0™7ÊغPÄ$@HÄÞDt,„ Þ›²r­kÙ¢÷î"®îŪí-j»|4‘J8µBÀô#À§áv§Äu}<šõM z=8,k&Ÿ1ûkÐQ@…ÏÍy fïT…¨‹ éÍ6#)„ }§œ*Øzj£ùYÇj˜Ñ¤#IPYÈ¢§ÊïW÷ÛzÛÚlk^J¤ÛfMOn¢¢ÛbŠMjé¶6¼[É«UàífˆLiT¬2% E#;hU,p O}fgY¡H~ Ì/¶³oU¶õjÛ늶ْc%­«ëQæ³åv{îã«^wW,)î»Å]dD›JæÞ^]wu_zôKB¬E,+ "‰ÞEÅÓÝÅXÙƒ4¤U¦ØÈá‘P5ƒl –µ©Z%AÄ"´ˆ‡§ZUkºj¯M–•ë+ÊóVëo7Z¢ÈÁ¢£C ÝHµÈ ¤Ù#)LÁ+±•aûÑf„ &‚¨†…Ð-žÐmÜ¡°ˆÔ@#‚À`ûIÄ.BœŸÕÉ×Ñ*œÞ#)e!yYìè‡ý§î’}˜v¶ƒ SŸò#;â¬cNKllÁÝ»Stô\‡8Æ3-¤µ÷ìøñφˆæC¡ÛÔUèAD +UE­_‚ð˜#)1*Þ­]‚]Dp N_ËÐñ;о€aò!ï/Áaò&¿œ1Fçðo¬þïâbY¬Ã‡?6L–%söÊg™¯Ÿû#)(‹Ìë?N6HHÚ‘£Ù!c'íóßcòɧÇHR=N#)3i\s¡œ9hTO!`ñh|N—¨º²¨iP²©ýl»…²åã2HÄQfÛïgGÒDÐxÅW§MgK#)ñ,2ü"šd0ݼBS´C®T>Tw›.i¤ùÃ\Fiu¨W¦>*0±…XÐV‘ÞBÄ@ÉHÁ#‹ôfaD˜0Eh‘Æ…†€ÐuK,r!Ä Ð— 2H"´±ìª%#<™&FÁ‚Á˜ú¾‘þaX°Ê ù †n½}{¼gŸÃçïråI!.Vsñ?A¢kS†qÂ> ZJkS§52œSÔ‹ûò0vp íâÒxˆ‡\Õ](Uݬ·Wv3D¡€öÅ]#)K·.ž0#<H)QiB!Ž]À„‰á–œÿ]Ÿ+¯[êzQÜ•wUÇVÙ͡٨‹ó˸ÅdOáäO‹‰4g ÛAË%hƒi•ˆ#m „﨔£/+”Š5J–DÒ"6R¥¨¼n–¦ËRV))60o†Ä`.mÊå·3‡tÞo.®:ë¨Ìs¦.Wwn”jåâÞ<›c”Ùo'“n®]W5™b÷yj›Z2<í¶ºnZé«dÕJXÚëæÆìÒlšÞ;£tæ®®Ìé®HÒ§vè9WYs¬´m9¶¢×Zm¹Q#)]6A^#;ß4TLa)ê€dm™¤Ü @#)woôñ€qÚǦ bx#)<?Çàž0/#) ‚PÄ#<„ N¤¡,'{.-ˆ?¸Ì*ì3GÍùg)í#)C¼ÞÒÕ¼­ù®Õ½Uõ’4L‰(#;D¥Kb4Z9fw×<¡<ƒP×ê«g–B|ÐS×´CÑ·ÀR~*(?7¿Š~}{ƒ©·Å;rD^èÖfFEEU¦iŠÃqØÀUˆ"öÁ¢@€TÎP~ø(‘ŠNÏwK#<åD!DXD‚±@`h0øŸ&Lp(±_šµRéi¢_hÆMÚ[¡wvÚqÛ»©¾#;ÕK\ÐYh-‚4Ñ¡|(Q=Å$I¶†1‰D5ðFø™!,9#<À^ϼˆ|e^›mÍ®”x­|yq;¯]]åÜ®ë©"“n\.kº©¨”´ÖÃGÆÔ«p/^y©»»UÛßw¦‹&½.Q$Q®^•µ¼—wUÔ«Åmeâ4¤Y¶-€žo¯2Ér.`ZQ³ƒC`jTC@µ¿£#)ß(„¿#/A}­üQ6REä ¼ï¶ÖUd–ÿ/Yî ïˆØ q @aŒŠQ aNïýÓaffiP8w]}7¥¢ý­µU_bª‹J©¶Ô¦¥‘ƒ)KKMµ˜²VšÔÚX$8#)ðÁò<µÚxUWz#;o–Û*›Ë¶Ýl³fÉ‹*™2¬Z©–¬²Ö-µ»l¯¨ô7 9¡´ð~‚vÝUFRZf&nš´­FkCf"±al\0†ñ@õÒ™­Lñ£ž´»P=°RFŒà§Ø`v຦Áfóìƒ=ç´(¦ ÃL©Ì9y}øÐâ8½dzî©áômÝæ?N>˜EûÙ'¨Hƒÿ@Öf{9Ÿ·=8]#; ¼˜Øƒ„f ør”iEÙX.¬‚YP‚µÆ•·‘ì³OL×:Ží…nxE?²kŽÂaÙ T-¬‰B×*) ¦d¬´’ZΊ-ŽµÑ­3¯Ahò]ü–ÌK…ïØíˆuŒ;NÝ)>Â*tï@´•'…`l?jSO’hа&(CB’FƒdŠÑD4¨œx@€§cvÚ÷îÔW•ÙŽ¸–³Ú«Òñ‰œ†Rä„FGö¶–2À¡4uƒbCikq#<Öµ!*!X0U¨1Ìf+vÊ#;46Ì1¹rвKA`J@¦ÞölS ”þm Æ ¥àŠØŠÈɤÇõ¾”ˆI$Ž(™&&Lô½M`Ñ$×ÍÄžß‹Åì쪰kB@˜%yFîPȺAjLâH¦†¥êÎ\!ˆÔc vJõûâ´c>½a±oo‚.hS%$ÅȰG l©l.È”žÄî‹…K"©†º+’餈2 ’xˆÔEG9’©hQLP baÂ~< á \ÛGìUÍ5,›÷ï RáC=ˆŸ}€`Ÿ²mZ†ãC¸>ÆðR¨'ëÐêŽÔn8Šy·Uè¤OÏTC@˜H í:pDÕ|ö±¿jÑmjª‹©VØžºb 6€©O²Tˆ #)ŠES¬!Sì ÷„ º&ˆN…Í”Ø;Ÿ¾#)âÛ”HA#K›Ãéñþôå~\³8"¸ùÀÿ/gé‡úµ=$ó‡è(*Á$IR|À?<a7·{ ƒš¢» ]öýLJ>é èŸ^_ÏãW½åÊqñ6æ›&­ Ã|Å?iµSl“#;¤Ú>®Ë:¿Žè٢ȸâ%fg›Ç¬ey¨j(MóBBrÄlÚúUìaŽA–Èh•K«ê>¼hfÚ;wŠ¿¯Ú¡:­HpçQ©|–—chŒÛƒMïÂ*ØÕ{r!Pœ#–CëgA]:Gri…í¦nŒéT£;äÓ+à Íþd‰“2†6QÓ ¡©45 E˜",Ihòˆ`®£\¨lh­–úf`ïüÞIäªNYfM-¡l4èfŠ$îת!ø¾õµÏhqSâLÁÆJ¨,Xý Qa2‡Á#;8¤=~¾É¯K÷Fž;a£ó¥px(‘ϺbØßÂ.½«=bóŸ»_<ÐaÆÁelVlÉ÷QPŒ¿ Î$Žr=ݰDHwVTKóXÄüaîsOƲõüàû&(oÀE–q\p–øiB{ôyë¡ð4'™vú&…ýÞ- œ¿jš)©M7ÊnwN%Ýr»@¥1„|ä,„’2êѲH +–Ƥ‘oŒÊÍr!°Vl’]å{- Ä™y¿¼;ˆ’ü ÐÆÛ‰Ym­ÉwÝÌtXDcEPh©ô Èìëu–&]óˆ¦!VRÂFC)ÿ»0m±¶2È›#;6éñmÐ2#R»Ë”9©å¯>ÖY[e5ÝŠ›‹±P2CµûY‚™ûÈÆÍ1ó#)«è-uöóÞñ¤(DæÐålcy^-¼AØE>hê1Òœè(=œãŒɈ¦PA[L¥Ed.ÙeÑiv$ZJPDCj5JëÏçÆ­iÛÅT´ÏG ¥‚„ @CïpúO×lŠB&ØÐ€a8\˜ú…+È$Ìï™mµëcy ÁggQFƈiŠd„pÞpB:ü¾íNùyÓ»&vŸߊ‘²I j LßÉ1°¬ï¨—C°âaüŠ­¤Õ–ÈëôàÖ‘“Ã#;JšžP¬T/È ½“|†Ä™QRI5|¸z¹_bC—-n_ò.®~L¥›ÜP„Ð Ý—R#)‰öß@5õàÀhŒÄ5ðíþ-‚²ñ§ô4t‡Y÷~#)f$`Â)ø $XP„#)|@<’Ôj_è†Æµükl·ªü“Z¶‹QEli©”;÷—«ˆíE0}9äTmxêôž”ÈffÊOVYYº¡ÈîŠ)äÀŽÇ¨>aÔv_IïNùtWè=GÛCÍ"Ý™–ਣ¡ë:¹«£ÝÙ#Ô/›ÝÀþchXÒs1à#)só˜’¾8$³£éÀ7ŠÖ­«oœ }àN;EW9kðxß0ÆÞËB1´Gí"œEÞÓÐ) i@=£:ת‰!$uÇʾ;a]9õŽp™÷ó#)ÛÐJ=Ž`J=¶Mž¾œíÊÖÚ-ísêæw·<«aõ‹Í#<U o'Í܉”#)¼RDïˆ$PˆP$0òæz#;—m7U¦ºhô º/.žƒk!x=‡šýæ‘¿hy¯©%V…`5øØ\a>zÕ9Ña 8ƒ¢Ì\kׯø×òfVREëWì)¤I¤lüaG>ö¯Í…ª@3;G–ýÞ„É#)F„h©ÒDŠ„v`ÉÛÒwaÎ|r°š( »¼Ôƒ Èx~Ó°ÏIÀo6K•Mžjýwë³&³3ðufSíܧ;W§òCqš×µEú]/žd!N g)Œzîki¬jÃåíÐöfÉþŒwø@ï?69Ö¯x¨è€ý…ÄH`Îéføú[ž¤R?«¡¡¬=-Dc¨bt%þ]wŽÙ Á<žë·KTmmEVvl@×¾,:d9à#A‹"Ne¢MZeÎ,ö\«L0Š‘Ì¡ªj‡÷¾º­Èlôü¹ Aµöl[?SGøxúsóûÈ¿†*˜ä\¤‘̉}¹u‰Lw'·~ÁÝ7¬eU8ЄÁÓ§óŸ]Vôùúž1lÔò`p:P“)RO[ðZ…US—w¡DÞýnÔüŒMìÐ3D1ÖH¢Ü“µ9àhPýÏmBÙ‰#”=‡@:$K­¿²TŠuŒãð¨Æ“bµH°‚¸D4¯Cgw ä¦;.ÐEf2–³³òAW#iµ“Çê^¦ºÆ90UCŠê0»õBÖDt‘~Ø!:åŸZéIÑÕªw®íbj!g„<2õà:t£$#)éB•­ªÀ8W6ºaéD‘Œlýüº$t^‹jýîK‰í…Ã:Ô“Ü{ôÔ>Ú3#)Qn­[†žNlÓšå·uâïàk­iKM”jƒPšÑkMe¬Všm¥dÕ©lV²¥}ÿŸÓåï:Ñdˆ cxnoRÝbéÝÝ‘‘E4ƒ¤}þ‚Fè¢(f zªU¤H-Wï¿óÞ½'゚26ŒÊ3dˆ£0h ”Ú&&¢)1 1£(ÄF”Ù6&…&M•&Š"Æ*Cð“—Ùž0€3&Ìž¨t!ê‹ð°âνTºú´ÇYì¶%Ë›]Ž—©žy¹¶:‡)°á¦5œ,k7ŽyP€(Ñû©ªTþ|#\šæÇmé;j¤ÑÔ0dÿž†uÞŒÜà#–é/*ûõ‰ôx_E¬…Ó•CC£2ügì 1Ÿ¥æø¦ÞôƒMm‹±77kj`sÅ)$?­²±fÇôÿ‹BNzfHòÆÎõFq¡dX,ŠMYË _±$#;²Ø•Ld¥(ÌæÜíFÞ¬Ý6xSQ¶e™½F4Œph$‚hŒ€Øvw\åÔܶ-»]Ùsuv®‘Þ×ä+fkn1–55‰{V jåò\DXVÙ‰¶ŒdàaKž(#<€âàp‰@—b#d"† D*!í¨Û¨FÚ ÆáV¤J¹¯®ôÚ¤´k“ZfÞ6àUË¥rÎë.ëUW4Ñœ¹ÏÕ­¤êÅÝ™Q–˜ˆ_{Ä#ô—WW(ñ‰ºT²—!KN4î*&H )GY)LƒJ³#)ñQ‚‘— QÁXâ:£ÌCb9N XÕ¤ãU04Æ#;D17EºÍ 5ìu#;D((&lhbmêF(Ј3Å33ŠFMƒ$6Å@i3Gä@À§¥Ã`±5¢ÍLD´“„e,e2¶«BE±ŠDJ`oTgh”Ë•δ#&C#;‘&™ÍÂAV(a€°)…$c ]H¡1Æ“TŠ´•L"1A¤cF÷ôªTû¬ÔÒà¶*ùH š˜T6ùªÄ¢%ÄÐR ÙˆPÄlP”Â!¥–8ÉfñJ6Þ˜¢ù4g:£›­Ò&ÉQ#¥ØP!õ¡#xòEƒ‰Ò}Úô2T72à¼0Õ!æD¤¹ÖÇ7a%iÌðlsBlÐhjü¾ÿñåúú0d ,‚hxC •d öky¾q¦»ŽªŸGnÞõ>ƒè)Ð3×¶ïçGì<{³ ãÂ×Ñòv°@2;$“Ã_CmNÊÌÅhoذDM*9†›^•U¢ÉŒÔ˜¼ š2¬5„xÌïb›ÇÑ{(™«O ÃMøIí_G]ðÚ$HŒKKÊ„G‹É~©êõÏRŒý'°AˆˆNzc¨d!àÄVFš5$[j+!¨•i›J¯¥;ÀÈ3Ð*jž2Qw⡃Ü…Š/еå‰h”¶¨*° D¾{ iF¢BÐ,S%=UÚ¶Ü´Óġ쀅DÂT*=/—ŒÒâª;€PX¢È);ám^Îvl6ÍDü·i¶š(ú‚Üšé­”S³H´šÝE=9M¬¹¨±ŠlÍbÖ¶°åƒnšb@qPQ6ßBíà{¦n»¹.®»uV¼ç1L#;ä,DC>i¤¹lÃ2ÀÈeA™q#<K–Z\˜ððÏaùû¾’ÀñĪuÏfÝÍÌr‘Šw¦‘r¡„Þnm²=T_ •Š(#¦ôàГ: 7øbXÖ˜.¤Œ0À—mE„üÍg‘¾#;Α9`8™¹[­¢Æµ}R·¸þ;^žw›êºÛtyÛ\‹ÅµÒ6îÛ¦m6¼nmF©5±¯ÕØš¹»»Zå¬kclZ5ŽEsVî릫ºm²ÅE[¦­ËR[›šÚ5r´îök‘±ª“Q´b(Q\Þ,îÚAüƒxŦOFUKC ³¬Æ p™›ó ´Àæ õÏY¦X}dÞNà߬Aa#)‡RØÀ°qˆjE¹¥#„2!Q!”A@>’#)6"â%T°Sù°¹‘%Ø<·ñåUÕ€üy¾Fýb­‡‹TT>˜@Ü´«ðAsûÀ÷‘EÝ€¦«ð“÷‘%™L  DßýÆáÍ×-ºI¤®îÆí®ºÐ,€º#)Év<½vZkL) Q‚#Ùh›KÜÄadn" bNSÞ,ù#<úhT ºŒV0DòA²–#)Þ*b"ˆ¤‚‚°ˆ`YªQ²H$‰ÝÁ[È–nT(3íî…yÄŠ‚öÅ Òk$hÖÛ&ÛEcY˜£Q¶Ò”ÖUQ±Zþžü·ïö©Šƒ° û €{A#)ç¯äˆz­AÝ}„5c=ûÆl¤k"FÄ| /¶1‡š¶ËJqvÐxtw0‹’'R"  vLÂ!øÀÿ¬!A$ ¿ÖnJÉ7$ !Ðé‘_MH(zmC6ë‚Õ@Y2šíùmUÚ¶þšŠ¢™­+HI²M%a…ŒZYH%±²Z,Ò6¤£j6&ml’’š5™l55¶‹j5lVÔ[jmf´I¶Q£S*V±lm&Ôja_¬ÏyGdH+uÁ¬È,"Á¹é«£xîÚï§×—ž£#•Ì«)ÈÜhÆ é\ÆV8d•7b4À¸ÔyRBAŒ²$Pd¶ £U¸6eÇHLèÄÚ#m1‘¥IvÕZ$ABåŠ#;ˆL e‘€¥¤¤Ù[¥²å»]4Ò­Ömªè-#)A!‚-v˜_$$PY#)*%,,B}łꙩbÚ¢#)"¨½¿mxT¼lº*éi®ë§vbáڗ髱z[«ÕÝKmãUÔÖ¥ªj[¢ÐF©°¤;€ãPÝ]U܇ÊÌ!0•Ÿ¼tî’'uOO[o»¿•Ÿ-R¨¥{#)ù‰*@Hõ…íÃôll„-|“ºü#<˜SGm‡ên°œÄ~i¤(b}öÎ4ß~*inD˜ÆÑ•¾ÐÁïŠhKh`FèŒÜži*!HÝ9?{§5/´â‘+f=1ý{I­ºˆrG 5s¾¢÷’×´%Ûù4~„z#âƒñí¥uËî”t…¹®˜ÜêîïyƺÖòË©SÎêsdíÙ·QuÜmt˜îíºeúΣm­ä1[š«•kš“[bÑ­Zé¶Öeªí·*빢ə§à=½M¯UxÞ]µÐ¡1î— ?–‰4iO )'³û•#)³5=¸ªgš~¬W&h„#¿×­ïu”ݔӅ¼eÌÓ¡þ éJ B[ˆ…F5?Ÿú‹3×÷íx¤†#<óB‰3©±^p•÷F·=§ƒAÏ`gØÈ¥0ªÜñþ ÓÞöƺ¡žt’®†{Å.Ðæ)cOÌRÆu÷Š–ÒäE‹õÌÕ.ÓÔ#)dÞäY-–qUa9à6k!vÐÚåµöÖˆOÇ{»ðk„#¾ X~”8Zá:P~8ýȾ|]Ÿœ‘͸(m ¶vá“¢¿ù)ËHã ¤#; ÌèRÄW+^”Ät])åÊéÉTOŸ³´l›Åg¢â¡×‚ßÁžÕ¹kCÅ¢‹Ç·N,~ze­f`Ö¯N|%™°Á×Ý;GåÚ÷2h֌♸¹}Ž®j3;)s€ÊfŠN½Ãž½öÁ´÷ß:•§§¹•|Ê̦¾y$WL3!3?ƒÎù9s®+ ¶ZL’8œ9vÙOmÞÙ0Ÿ/¶ïÒå1ð÷8«ì9ÖxmVŸåCÇ4@$C4#<~‘Ò•…]=3ˆLŠ­ê˜íD©(wtÚ˜¾Ý=§%¶WFå¨BÿÇ89ƒå°pÅóÜ,CSÇ`ñµjIèU׆˜ò¾ žZô¶#)Ùò<õk#)¹ 8ykRXuQrBå(§|e¨©PÚE‘Bnȸ9Ô'D„³¿ZµóÿEùi”<<èê‡L¶tä¯Ûr™èyønmw_ ¤ºmZ–¡Ô=Œ~¬,(`õ’(P/Á’]¶«§¸?tƒ‰)ŸoÇ¡Uºo„÷9ßFqÞ£¿G•jó‚ø•¦êrÉ®¥k#;OÎÝ´’1$(!NJƒ#°§ôÈÉIk]϶³ô¶#öÇh|c2ÍiŸECvguáCç‰~S¥¦×z¸GÓȃöxvs‹ëåÒB0› †,¢˜âºILu;½jÎ#;Ƈmá#ƒx  ’L#<÷]ü'<ž+%Þˆ‡Jßl×|UÈ¡B]W‚BB å¥Æ•µ¤çf7Ý«Î{íaâ.¸ÜFîæ¸¾9"—ë^°œ¥OxÍxCæñ2A‚=D‰¿K³%‰ <’/íµ^tzè“>ý£ÊÏXU| EË‹ƒ‹¾,Œ!ùaûu“w|Ѳh]7üòIK™úÅ"'Û»ùú硜­aû<®æ)]G/á>}‰ÔŠàjÁ0sVB±­u_g]Ís`öáÅÒéúÎÓàb‡uÔ¦“,Q°Ý oQdMÎÏ*tÃ×~Iñk¶$píB‰CKq¿­ÃMA#;®ò ³]#.2ß'9Ruè–Âvš© ¡ŸVb  UDP‰E `ç-sLñÃ~ŽVQ•¢è\æfZ½&ÛsyC^[,Xü•WÆÂIÊH!iheGX#)Çho÷wm{Ž´®NÒrÙxÎÒI%M»U„ £PÇ*8>‹F XIè,h C‘˜&¤á_“9$«åJ™Â'ï¨÷}gBm ¥îç~7hç[ÎôéeùBvòŸZæÞ\·ž“Õ#<sËΘè‡Â6U+΄<õŽj(Eªm)Š©¢iÈã“4Î ×#<] PiE£°mÇ*Ë;î}„S&©“Œz”Yíàw\bÙ00ÇbJ–6…²G|Xã«–3ï£L§^½¸ 7BgVHPiJ1/ìläBŸzê3²9]úxÛfº;Žl:½'wQUSVÖºa²WŸ’&>9Ó»ª}ûž=JkZkNÚ21D‘ª!‚±×ꤤÆ;ô}âEXªz¸ìï¶a#<Iç~%‰@½6#.éi<=fuSEPæø}åÌÛ˜âtÍqÅì^[)XÐ7ÙÚȧ¢ÇÄQŽiÎт2]›Ó#<ï‡OLMðƒã~«~äìºx¹ÝؽÌ÷x;c~á‡]òͺ4­òM’·UœŽ®cÅõ‚–#;@×òÆè¤%°^FˆÞ—‰ åÃïcžq¿iŽsÚ§ªñw6œs tŽ"ôú¶Çºl]¨¿½)Ý.+‡‘ÞîO'#;AÍÌÊ^/DBƼ0N4¹öÜpNíí ¹QRâ3a߯)ž‡eî·‹Æå«.åKi“™Š…™<²xœ¨¹”dF­KZUâQ#)Êô¹D=DÝ2 Oˆ´D4·–bÛay£ ‡yiæù0ظ$|:óœªgpw3…BÄH\‘#;ÖC1i¡6c å™#;î¡u 'ƒwèl=n>',Õ“¦:Kn8jtçA„aá˜Ö‹ko;iú¢n:á¸,õîNöEÈwzÄ,—ÄkŸCTˆr¡Î®›0‡™Ì‘º¼ÁCöË£1ÍÉäï²=x5Ò†ac©Þ× ôi¤å$ÍÏD²â1œf¨¢sõî[*‘ªº…CC^*<ËiíâgHuȳÝJ´›nvçECA™í.$Á¸Œ³8!u0Î%Ö)a»À #;«*T’s@àö&†)S…3£LÎRMnõ‡CAë8š­êƒ#)Ü:î³£i‘¸xz‰ñ˜åŸMm²û¹Ù,¨ïPÆÙ´5û¾Ìzcïë7j+3#"—ñ-ÄB Ö]˜ß‰A¼cR™/aåGFd¸°ÇèáusÐA¡<K7›ðe™,4kìê²Ûv5ý1;ÌcoÝü×ÙÒMS  ß!ããó‡Í%—“¶¾Òº”Õ*Óå×ü¬õuÕö-#<9qõ‰Çµ9Þ’DXD÷0®Àí9¯{ƒŸ ·Í.ŠŒEX³æª‚ÄIl#<@j‰>B4×ɪ(q#)Ø&Ĥ/önÏ«ßÆù7Î"Â. ¬a&£sIÅVYh̸ÀÊ6[1Š¥HÚ1ªnØ!§X·*ØÈ^0ÊCT 0’ÒÁd-!l /Rwk–#;²Òío7Wzí•© ˜‚hD@;æQ±$ÅmsVôÖkxÛmͬ®…£C»2‘d!Á#;è m63¨48-1 ˜E‰Ud(©a€S 'o!å–¸ç}™ëç5Ḵ›sJ"-¹°~Ž\Óž,»iLb#< }ž¼{ê½ü‘­"Fo}ñŒ/ÉÎà˜BØÚFèRÑ‘d „OzD$AR¢¢ÞT@¨Ô#)ÁÑa˜þ—fbÑ‚ãîÑ™¥U#ƒDÚ‹2}ôºû®±ãŽÛJ™AJ¯õ|ªZ""ƒ&%·#<±aµ0ÚƒFÛmæW3 Ü«º(°ƒD4ÿptmb›-WaF ®&A~J§Å›È”:fœhÀU˜†xeum”³Ÿ Æ0‰iÆa½aÆ›tªµÙ!ÆÞ'ؤØ'–‘ºrѾڎ=j몋‘5JG(ÞÆ#<”™?—½tÓÖ“cSˆ¯,PŽ‚%QÎZ—t!½#CLÙÑ •Ž5„[Fᔤ(U†A£pSC‹Hi4Ø15Z6óqmäÊ@,¶WoNvµ°›—O2­CdT¨­ux1ˆÐ× Z„Z›iر 4“Q†F¨g62í» ¨ÇâøÍT£äfڦ෿æ< o¯FqZé‘cS¿J„F&ˆêjŸÍ”e‚nq ÕoÊKŠœÊïWH˜K4ØÙÖaºa½ëÌÆ™#ˆF"ÆÜÈ­ŒhMêE†:Ý”peÄœ\÷ŒÒhQîÀÜ€ù<²˜õS$´Øñ¤›š$‰ËÓoÌ¥0ÙsavH#;5«+ƒOL׊m£EÐ>Ì+WYÇcLñw =‰fÇ›®üL›5ºœÃÆ—F,*g‹5`®fV&·xÑ3’¦³$*.ÉPÄ`Í5ŸPÆ–ž3`C hdj1H7×Yˆk,à/#`Μqœ’#;Œa·ç#l3#;êß#=}¸•Ê|©LÐÔN$„+ƒÖøýNO½ª”ÈÁR#(Ð²î¡Ø\»Ã=pc#)Ç©3ô’B OU4ŒHÀ‹!" #H¶)lNЇ°`Â%r©P¨”45¡'E¶Ÿ#XRðP:øO5©!”ÊCè;à\îU;êÆh6#;±‹MØREUD™'íõ ¾öJ{ ®ÕÈÞÊÊÅ—Nq˜&E^î!èó }DµŸJ¨'ðbnûµS(N4H0#$„!<Ý1RJ`¤˜Ž&×P5°Pœæ=¾ÁõèO 3óu%Xõ“w*¬p¤U)Âk}Ç&°`Ciq @¤ü|cëCM³*¥pyr™Ÿ.3{ž{s‘#;i½1´ÝÒww&Ó“»v‹ª™n žøÂK'lï[Þ\‹°:'š>‰càtžqóõ#;Ò*•œoÊŠÊjŽ…>Û€ýÇo—†FpîÞ3ÏHH5‡º&ç©Q ë$X’5  Œe[¥%]ªe+i†¶ËË®mbÚ5±bÛ¥ãZòkfV̉#A(ª  $_‚à#;ýÿ¾ÃZ'¡W¡¨£õo¯#<Ýñï¶1Ñò$‰"IÕI6“ZØlcHFHÄL”i¢i(Z¦VÂ[ɰm«”UVJ0Å6Tlb‘–…4Ù&”›%(Ó(„•¡-ˆ”‰hÌS4YM"•J0´“#;°Ãf)BI¡R’EDü†Æg‘o\2TÌ‹{áÖMÇ Ú†Á}ñ2:P|zçc6o? ëw‹ºæ€Ÿ ¯ÈÐõ˜ÚÛ¦Ù#;¡µæX¼T{¥1HÐ,~ò$‘‚Ci'£ÙMï‡‘Ææ9|0T£¤’ÒZ*EÂhìª#)b ;ï¹¹‡ÔfaªäHÓ7Ö&Ü8ذÜŽD2¤k¶ÿ?ß Õ &-¹#;¸û)) Ü”äLš2oZŸF‡+Ó}Û‚Àœ1#¸õÕHXÀ¢#;[¸f¿#Ï s8…GÅ;xx wcr*¦LEž{±MTÁ!âq˳ð>Ï—Ãt>}½ç7—8À½Tjš7ÚSȼ…}•kN—)½ÊÄÎùÙ˜mù#)õˆ=PB@B)5PV+HÊ#U&ªŠ?A­k¥iÊšˆ£±Œû.ÕÌÕKRòíU»°÷%›Eø"ÿµÇ7Ü+Û”\âT$Y R É#<=¾Ì‹fKQB˜>ê£úK‰°ø‡ R¢'aE¨kpÒ4X~Œ.‡ªÆŠ}..™WLE+Ü‘ÊOZíÛØ½Nî’œç0jxç³nöé²@´2À…°4f#„¤“ [Õ7¥·¯o·ŒtÞ—“!%ÙȽ¼íyš(¥5Ù·´¯ ”aFA%´ÁK÷PlÌf‰Mä¼#Æ#lŸeäfz À{ŒL´árRÍJ-Î*#ƒÛM ¨gh*1‚š0„¦¢Óp„ˆ/Æ[ŸEÖ¼Èô´æl´"û}‡ºÁ²'_œ=\ƒŒÊP{%;ñ¯rtN]yå™)áç>EuàÊsS!"U‘|K+Gž´Óõ0ö”ù.Á~fu#; b>#;ãðð„$/×ešó«Xç#,¢vˆ´-?«å1 žuxƒšKMgÑÓï„Ä>H4a~”:IDú~sÁPˆEA@‘‚¬L™ù}º˜áµ”YQdø°ñ#0x}/£3/‘¥û°œ,oeÒ1ryŽa¿ÑÛ/‹Ó4œ$AÐéaÓ1Ü)aþ?‡ñVa˜ý1Y¨L6O¢‚pwîéÍf%Ê®š,0!’c­ÙSTw¼ó@'“¼éhçA³8I#< m  *É#;1F acM ïÒµí£)´ZV## œâ‘ ÄR¹ÎÜžÍÌ€4Иث€@œçzï[™1´“P´¡04!LFD!ì†âÌÖ£#<Æ1B•lúAß-¤K½]ϧŒû›c}¶â–àüš\ ç¨gÍZ‡i09ÑJï&l„ÈÃÁ¼#<.µ™‚ZH¨™@œ“î¹­oœþ?ù¤òÛ¿‰—l뛪§vëžJ”Ìì.¹#)¶CåÁC¿žÛõRg –RŸOÓÀ9ç¡l‡ §ï}R°Ü¹%äÈÅ„Ð@ÒÜpÄá’l0ãÌòéƒF$t‰#;!0#;uMXØPƒ"™Å)/BV#)¬˜ €,"›6ÄŠÀ4ŵÐàSŽ3F¨Í)™JØÏm©&ž*T¥4ΦÑJêÞhï‚^#;²ƒÞcx曃NN–Ù¢D6%>ñ*¥°2f+‚@¤!lõ,b´À\™Ý§:btA@bà¹wÂÉYy/ÛŸ K†¨q8åL®`˜>U52% ÈÙ%Ì;ŒÀæ90Õ«ÒÒ†¡ô¡h¤;¶È“hÎ.YÂz ‰®qTäᛌßYÌF#8’àÍŒY`I»kd\°=æÉb±cÀN”ãœÔˆ±ã2Ò’·Îí6šÖ%V‰—0:ÇžØ0m.ôÛ=dx†¥ÉQ20eÃ|Ó˜‰d´à;J‹áÆ†È íë¦ÄHÂïJä(|#;¶Æq†›-L23lìPKfm3©§Sˆ,vC´½Ë§½Yu³˜…¶Ó§–²æPè¨yX’.]7ù«TL2kîï‹>eðÉÍâω¾X¬ *#<í¶LD­ã6©‡Š#wÌQ©|í.å)˪t ôqaß™¬G%œÌ3¤´š00¹âC”šE¶Ï”4£V½”D»Åñ-B®ŽòèyÄÕWJÝ…Še†î±â[ôGEbdÈšDô‡(X\õÅmA-0æPÕ§„$OEjq8ÍÙnaA·‘ic×]fZZ<”#<œRàiÈŠúæª!È·®ƒëhŒV,¬ØÑNf*Sëv̳bƒªîî\W]±¤o°‡¾‡&Ý&!@ù¬Å#<77žŠ„Í“ b·1Ó¨a¹:S¶EÍj©SŠçl›ªƒ@㬻™:>sl鎸Úƒ§5+#† œ8Ñ‘É;š7ÚÓ!?6¶ÊŽGJ½ÎÃ……¤xW+W boH·Y¹H&[ ¢²1 é¦äHi¹6H"ÄzJ’Ä. #e°nÓ1ÊìÔË‚Œ/®DËÏd3ÒœQ‡4:ºº½³M”kA}ÂjÝz1’¿ UÓ¾Ø3Mƒ‡f•R3;g&Æ1‹ïdY.™&.-ù¦Ðb]hÌm°Æ#<}œò`´rØs¨»™·G(“õÿ/Ò—¶Ë™ptÙQsGÇ\f`ôùlréžÄT$Àr›Üð^Ùy'ˬ¼XÀàʈJHP*N D¶êd³Q2J+#)Ù[°Ë;dÒ{Kj8v#; <úrÌäæF$ƒXЦêíÕJÂÌ •ÚÓ±çŽ,4Ë8'3DÁÄ3ƒ ¤ &bžT1*1(Ѧ#]ûï“Ø¼H³‡½#;•`¼yá£6µšF°ØàÍè­m|õ2\Á/emÈÏ¬áØ§3P‡Os\¶(p[Ä`y-èÔŽœ™%uEuz¬Q-Ö@yhg)‘À¸íÖ—]¯jvÁ³UË\³°â ò¡YÇbðÒl,6§”%6s½»ÞÞ¼óWÇ›|zK\G²†hv ]õF ´bõ­Ì¿.b:,;jj)g‰ªâÙ,¤é¦™ 1pÈ.)hPçA¸x¸7#°Ì’H«ŠHLMG8"f1³&ó&o0Ð4ÎI%%¸kžnЫ*dÀë…9û*£†Ù$Pm¨¡> 3$pcgxÇ®*”46ªY5ñÛ»fšqj꘾,´e²Œ0#@ÔXñ×`ÌÁ·Gf®bq…’#‘sQ¸\Ml¶D¹J–”R ®¦Cq¸hI²UBˆu9õ™-léarM¡±`§@ŠÆ®Â.rÎ&¾h¤b±ˆl!lRšºCMØ)¬Á¡ºÉPâp#;˜âfeЍ•°#;*Bš;°Çkº]t)lØ"&ÁÅbHÙhlL\â…Lªã¢Ù$»AˆÌA±rQ8샃¶Ì#;ƒäg<¶Æ+aQPS±³1 Òi´rpT)MISŽÝÕdd„jÂì©suSS!Ø›…H`Á8q†…Íž¤,¤V:%NQ’ܘ(2ç3#ÀÓAWE‚4“ÖÆšT l1%Z &šK0é®i+ŽŽ;)4Ä©-C4”R»*ÀÕ»Òœ뎽ÜqÕÀÅÔ‹€Pœ¨š t4À”}Æ¢b#;4È@]NÀ‘Î#‘ ÈȈŒ#<0Mƒ4,ˆ3bé^÷µ|DlTlš“[õi¶œŸGüO‘ŸckPeTD©Tôh·l°Æ˜ã>x#;áÉtäo>„GìQA‡¶¶z… ÒÜn5‹aæq²–7#§±é÷L+'ÚhóDy¬«eNÔ×O®V]ò$aóålJÞPÇïI“³HbÄCz‡)ýl\zê÷Ú”]6¯†?Xn 9G\„ˆP72êˆ5¢YašG>[ª¢7.Iõd.F›Ítú{úÔ@Úi¶®Jª}–¢/è‚pŒœ¹”¯u"²"ò'ê&¾jÂ^©’‚ŠFJBr¤àêq뵚 þx&™8…¼Yq%÷ܲÇyr¤N¬ ±g24Š­Tûl²M1Ývh¼­"ùÈšdÉß“µÛsÍZhñèïm™­(êD8‰Î0Àf -JüBâ ÝTÙ|õS±n‰^G#nuã#;¹£ê{Üò³Öq³E‘sw¦p‘¾ú³(xÀ#)ùIò¯\|TU í£.<_‰ô)ÔsË„‰•ft¾Í¬{Ñ$Sß#)°3눬†üœ¡Ðøé’ö‚>‰ lÍѪBŸÀ]8œ#‰Á¨˜ÒöŽé^òc5TÌâåq™#)iØ©c¬X0*ÚQ+·¤ðñÏݶ<³L ’P˜ñà ÈÒ2ÖÉ ÑT€ÛX0ƒarZVWZ´™†L)a@cxð”¥Ì̆f ‘ÌÅ1#)–AJ£44 Ä4Ò ÈADJ¢*ª`Pj˜­F‡ßIš¬Θðdm’YA¤:è[˜ª‰¬nàî‚*ž0ÅhK³! ÌÐÑùŽŠ¶¡2ª$@ùˆ@(ÌaÆz{{§{îðó„æ—|+)äZ–P6&»xR$¶ ½CØ ‘ƒ8m~âùŽjnM8ÊÕ½ {6´ÑS)‚lÈ4:HñÜÄW™˜ˆbÀl°ˆˆ¬Jì‘1 %0¯#;Ä7ÆÓx†´„ˆbd Š‹B#;öñÀ€gÛí ¨\2#<ÞØ,ŒáhÂÑžOzBÀú`ÆJxy£nM±›jHü¢ÊIñÒEýOøHp#;=|½I¡@ü‡¸ œ&×bˆ%»,0©ÅyI©ÇjFoeËÔŸ-V±K1#<†&#äâ[”3>Q§ ¦æ.Ɉ›…ø@˜!Þ ‚q{ªš j Ò¨øDöŒz•Ï«<ðy.äåH{þSEyw!”†ç<Ú¯|ÓÇ+Þ#ëË6Ù™| –Òl¡hÛ[oLa· É#)ØÇõq×›4ÚL’‘ŠÄOm#óšÎ8gL†½M(É+§i _„Õú˨4oÕmu©5ÍUùÚ­ÖÔIEªZ«)¨¤Öƪ,Ë_j[IkX«R’*†ŠB݈.4€ƒ€ˆZRHZ@Æ ÜR#)°’H*˜HŽ(DŠ„‰ê]Â!Ù#)ôE€<#)žþýž½”R©a’iý·Èr º–áèθó5`t‰Ù²çt) ˆ‘dY"ÁD@›ãüCÝÏ›é#<Å0#)bÞÐI!ㄱ˜§êV)dDŒ RA@lðÂÛ{"õ<0Qtø"­S0,’E(o¢#)2üä—‰îF¼3Ì«‰'é0é— †³Ø´š61Â! Iû¼ÒlÐ÷Ê|„h¶Ф¤ŒlQ %Q´“h¬Ì1’áM™™#<`ÛLÈ2ÇQj¤nÉ H‰EI`&%i¸4®dB–#;µ ±ª„†Ö8ÁÄÒ1 jŠ4`ÄT…*#m!RZðccÓslíÅ£kwv·ŠÕxÚMVJ¶ÍåÓÍÆ¹ä¨µkA€7Äê1¬X‚2:Ö l*;pä¤Ìॠ@ndY€æikO¹P ûƒ±ê+ÕÅêŒIz±îë÷{èĬÕ\§Z:#sˆdsN2¹ÄÑFOË8u1®ªËÔíëlã¸w¹ b@Œ€ s^¼4B^š„»äQö4YïTY¾†$8¬ºE‘GZ…!((ŽQ·Úh/#;ŒoJ92‚ÓóŽÀo÷ƒnïÂl±#)\#)8´#u@G"RO“Û¦™&+ôØoõvö* §be»ÛŠ`2"“>‚6O§\ºƒ ©Û´J–ž<®—’#)!@ €‡ÄS¼Oyð¸*ddP#)ȨH¢ó@«$#)¢—'dìq’4n+犢k}àáÏСUan76\2ðDš–—¨ P|¹ò¦ÚeÃÛOÓ`µ)m¢j‡F0ìî$‘…#;¸òoXk妓»r2'$m1ÙIeÌÝ»h\RÁ—€Åà]j6;39 Õ íEƒ·ÀƒàX ‘˜Lk\/9WŒÔíDZ²H#;ƒ.´š•¦”•¶ ÄŽ- ˆ£0×”ôJàEK„¤&‘¬F·½´LßzwdÃ#;QÀAB¬’R Zexh‚Ó¢&Šx ç¡Ê#1(*ck}Þ¸©Àò@=!"Ä!'€ZÔàÁM˜¾‘”ÊÈdå(Öƒ¥ZÐÀø½-inµ‡' ~&e~Æ]“d]S“÷#<`@6$°Þ·ˆ6K*½aÞ[Ág‡Z¡»þ@ÿ[@À‚/ꑊÂf«eM6½Ö¤ø|µ?U4™~_¸[ðñóóº³Í“Ð]3hˆo@è˜@@‘ÑŠ©±D#) ÁåË¥Ž?#ͰC螢=p[z9¼§­êÏŠZdmÁ ö£c‘Á†²j¨)øy¿_7ìÀ¡ˆƒ 4 °„°´ƒJ‚M ÊXk¾ç;oôþ‘û ;)F6½üq(vXPì5(3=™ÜÃE®"0Fä5‘Ìàä yǤ-½ÞíÃ®åˆ ãD,læõâ‹ðŠé#)žÍáÀt¦dªiŸ}¨Úœ#)­S”Ì#)G#)À k®Œ½ô˜lúíóÙC]¯:/¥mÀíh-#<<àànƒò€ï Šè;bB¼”{)³e‰Ý·b _772‰ !$2c2zë¨Ñ£4™wWF$ÆÎ—NÓ>}å‘úÝֽʉâIYª[ ÒM>šÆ•ROî”9ݸb½]Å÷vûÈ£3Q¶Mi±¬Ñ´À$ €œõrw(&}²!I±!QiHÔkB–dÚÉJc_Ô~“L×j}½WÕ¿.AFˆcE›EQµÊY²Ú¯Ãªv‰){ׄO¨€"«h §Û K[”=ƒ2.¤.§œ‰Î$%Å8E]ôI]Ù74jøÊõ¦î¹²Å6,[ÉŬÆÚ(ÑhÉLŒ•“+M)-š#<&õë¼[i4$!À‚§u(–€$^"ˆ^ÝSgû~{éùÿiwgy$è¯A5Ô8…=éë=~;m1òÌ©²ä,#Ç/«vA 1fÙ}Ý[Ú‰Ú#)xH!ÜÀr}¤!ÚêuÕ›2EÔò=`¾wùB5>™ýŒÊbHr‹ÑƼÇý« ÒÉ”N¸—!sU)#<%¢»#<#<ŠP€Ü?2åáÁù`-€aRÒ´M^ˆ¦¢0>ìi=°S$S±=ÊÔ ëE€UÑv¶#<"¤›\ÜõÝeª›jM(Õ3Ê•ƒ&›$dAO¬@0|MÎ87Òu„'“IxtŒ…Î̱‘kzf2DÈ &õ#)Ýý0‘ aZ€ùïЂT§ÓLˆ€Cßk$²ú6™k·Â|xRÔw_1€Ÿ–Úõ°Ûõ0Ö^œÒÀŽn&= V…³«[âæ€ççBrƒk÷XÐö M:ZËZdƒŒâß30äÆò™é“à»÷â´3å}ÆéÄÖ™Ã"Û™š!æMp±ä‚‡Ì'¦~õ° ’› ÆR¥SA$€H¤T¥šT•&ifl¥¨ÅEÅ6©6ØSF‚ükWî–¼’ÂÅhÚ’¦aX0!þìhãÞtñí³¶Y¤„m‚†caKX¤ªa=BÜ« }w|"Æ*b·Üã³5m›{å×éõÛ”Zwj”8ê)ê|Óìâ…3N+½ÝÅ+(”2#ÌT´–‹M1KoäððK%Û2Ä6(à°²1:46ÄåYKQc‚ïþJ#;ÅI8A O,WWMã­ô-Öi54R¥½f»iÝ–»»+ƼÞk]Sh²kzUÉQo7u™»3*ºæí¨«¨e²,µywcM·wWw[I²¤©‘)±­dPÑ`Ea"Jœ”« #šL¥´fŠYø$Ë‚Š/|˜&ñŒPÀ`è˜v`ÊŽß«ÂóhàñAã›™—#ä]ßÜrþ;‚(IvàG·Ù±=¹‡¡Œ[¡`W=½ØÀ¤ÞOA¶³‹QÆlêccż·âBÝÚñÏ'Çöº©Ý45U*qt¾£:ż8[Þð4Æš+ùx™˜zä)Øaõm䈃"ÀcW(£&ÎkÜÌ¥“ÔiÂdÂTáüß"#)cAˆ*°M%Ò˜F‚¨_^˜á‡&„D5Ýø˜ï´’þò+ CB†ñ_ÀUô:}d­ªž™Áå¿}ˆi O.  ò¡BØRÕ•éc6ü:¿:¬[3~Ìš(uKFÑJŽøm­Tì|Mœ(€}66s9›/ÐÖ5Ó"øgètóa7T“¹!÷hiµ^Ñ(ñ¦êÉB‘=P´ú¥‹0,#;TÝhA²‚•¤êe°TR:ì921ƒðcÔcp@ÑØÂ!»v¬À!FòƒÌè¶2Éïú€½Ï’=¾(¤É#„I•Ä8B Љ¦n!ÛtÕvÛµJZí‘\n·v&Âû•ê;NzÁí%BEŽaJVGÆ–‹º&áõ[‰]|Oj„Ì„†hÖµ Âf̆y>4h!¤ÔÊ'#;±Žt>„ëú|Kóý‘ýMíì›pÕ‚T?f°ˆ’+ñÑÑäQO©4¿Ï˜U#;‘É–õP9Ã;–C#)zfßYhBÆî§¶ä<ùÑ“H€<¤¥E’=;?£î{£9ªÐ0ãŒgX1ñ½8¬#㊸8ßÑEY“˜FNQÂ}y‡=D@f¿@ˆŒPû¿ÍþíïÿÓ?õÿîÿ«þoø«ÿÿŸíÿùðgŸoþÿ³þßöÿÇû_ƒòøýÿ'ƒE¾¿þÞªG§ýŸÛþˆ§Ãý—û¿Õþ¿ú?÷ÕÍÿïþÞ_ÿ6Ü#ýŸêêþ_Ùùücû?oöuý¿èþÖÿoÿÙ¿û/óÿ“üÿîÿòÿú~ÿ§ý<Ÿ÷ßþ¿åüòÿÁÿ÷ÿÇüü–ÿþŸíÿ‡ÿßüÿãþÉÿ¯íÿŸðû¿¯ü;¼ÿҠܣü„Àæ,Gïþ¡Úˆ ¿&QÛÌ,pÿ‡ô‰"ªp"µÞEÃèr5˜?spL¿Ams1P @ÿž2Ñ#;B}¨CðU`˜#<¿YÿZ?çªj¨‰$ 33çmôj›kí«]ö|¨6TÕcIqÜ F;Ž6é’aÊÇe4(³#žÐõ\ŒöºÀqH×v«ã¶ÍÜ4njƱ¦†AP{a÷8öôÎ’*8É9¼:ù>Ë8o‰†âÀáÆ¸*ƒ7`ÚÙx0*-&‰¤tËIcá“׉5v4Óêm˜&S³aKo7žÏ#;V8f`ԛ͢‹ˆsOBá„;eƉ#<ñâthèùglœof+U—Á#;ŸY|£ÿ3^³ ¹;)a²…qHf˜úF#‘CÝï…ßÊðŽáLkcTQi&Š]ÿæzøúçºé$xC'ØŸl±LÖÌ(V#< z³R #‡ç"šE?ëûq[÷U7E[ÌAKbÉÐ÷\Jj€!ðëú#)ùïÆ½}w-$E¢ŸáÑ_ªXôÛêú/Änë©‹çQ­ü—ÌÆ9•søuÞuñìI¬#ïGqëìôŒóÿ¶xáãÇ9áù>rM7½ÆA?H»Ã$tå’Ærÿý#ù@Z>G³•dz8èÒ½&áFòãñw‰`ÙÒà #5šï$ו@::¢h,1ªÔ±TèLÑ0f&]—.S`¨—ôwÖþ¬º*ì6LBR³<ÍSawŠä%‡5Õum°l¶pȘŖÊLʇM3Å¡pH‹Jfï@¼C,@D¦…“"C ö&’hQvA®3ik¿uîY!§"Œõf€Ý“pÀ#<Âv @4ø$8´#<#)$‡½!˧¤áFëÅžÎpž¨C‚S'Åmõ5Ùc[ÓÓY·Ü[š“hù6ܬVŒW¶öQ[ÝmoSêåjM‚Ú¾­rÚ½þ9£#;2ew+ë¸ RõP#bzƒV#;ð0‡#<«µàäXMláBÒ(š#;’Y ( Ÿ,X6Ü_øˆdC—J#;‚Ƚ$€äeÝ¢…Š{Å“k\; Œ#™†êB"YÐ#<,ŽÂ­Ùlj&¸$1ÜQF^<Um›M§÷D¡æ>ˆûñCíÏ‹±xÐk$Y#)‘›Ç5JülñòûYG^Ç­êø=Í`A  ‰þû#) ä¡èçØˆñU`@ˆ@kµçâú+]mr¥ªÌ¶¦l×4KDˆ0Ct±A»¨–‚I"$¢äÇÝð§êMðôÙö=|#<  #)_\ù¨‡¯ÉÙÒD£ ôk´?$æîâˆi?Ùê©Ï ¹‚| ü Xƒ"þ>ïƒòî9ò¼Ý»ñÙˆH ŒŠHŽEl°¦§² ׈œ€öÂÂÿƒÅ(ã'Š#>!Ê¿®Áðyù†¿\iùB®‘ÄÊÚ*#)=#äP÷ÈJRh#¢îm), ´0þOöêÉ?Raž‡×ǬyiVd2ðuHrfÉ75دuHýÛm3¿ù­£úÑD©µì\¡]b:;¿ßìâùÃ*€÷÷³ðh5ÅÔ¤ƒýÑ(þš”ûQk8+·‰ÅQÎ ¿#;W Œ³˜50ï‘Ä>?§NÚ gA••ëç,Má†/-„±¿ ºØ¬ÜI@±¥½j»îÎÓeºàNÊO]Ï7uy¶í&Y¨{obÊŸû&f]j‡Ì“$%ãp€ÇN~(úk×É犪¿>|¹ïý§#<_ÊÃåÄZ>A¶ýfÓ_Íûö*¡þ.äŠp¡!®†sf +#<== diff --git a/waf.bat b/waf.bat new file mode 100644 index 00000000..59e3af07 --- /dev/null +++ b/waf.bat @@ -0,0 +1,97 @@ +@echo off + +rem from issue #964 + +Setlocal EnableDelayedExpansion + +rem Check Windows Version +set TOKEN=tokens=3* +ver | findstr /i "5\.0\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "5\.1\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "5\.2\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "6\.0\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* +ver | findstr /i "6\.1\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* + +rem Start calculating PYTHON and PYTHON_DIR +set PYTHON= +set PYTHON_DIR= + +Setlocal EnableDelayedExpansion + +set PYTHON_DIR_OK=FALSE +set REGPATH= + +for %%i in (3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) do ( +for %%j in (HKCU HKLM) do ( +for %%k in (SOFTWARE\Wow6432Node SOFTWARE) do ( +for %%l in (Python\PythonCore IronPython) do ( +set REG_PYTHON_EXE=python.exe +if "%%l"=="IronPython" ( +set REG_PYTHON_EXE=ipy.exe +) + +@echo on + +set REGPATH=%%j\%%k\%%l\%%i\InstallPath +rem @echo Regpath !REGPATH! +REG QUERY "!REGPATH!" /ve 1>nul 2>nul +if !ERRORLEVEL! equ 0 ( + for /F "%TOKEN% delims= " %%A IN ('REG QUERY "!REGPATH!" /ve') do @set REG_PYTHON_DIR=%%B + if exist !REG_PYTHON_DIR! ( + IF NOT "!REG_PYTHON_DIR:~-1!"=="\" SET REG_PYTHON_DIR=!REG_PYTHON_DIR!\ + set REG_PYTHON=!REG_PYTHON_DIR!!REG_PYTHON_EXE! + rem set PYTHON_DIR_OK=TRUE + if "!PYTHON_DIR_OK!"=="FALSE" ( + set PYTHON_DIR=!REG_PYTHON_DIR! + set PYTHON=!REG_PYTHON! + set PYTHON_DIR_OK=TRUE + ) + + rem set PYTHON_DIR_OK=FALSE + rem @echo Find !REG_PYTHON! + rem goto finished + ) +) + +echo off + +) +rem for l +) +rem for k +) +rem for j +) +rem for i + + + +:finished + +Endlocal & SET PYTHON_DIR=%PYTHON_DIR% & SET PYTHON=%PYTHON% + +if "%PYTHON_DIR%" == "" ( +rem @echo No Python dir +set PYTHON=python +goto running +) + +rem @echo %PYTHON_DIR% + +if "%PYTHON%" == "" ( +rem @echo No Python +set PYTHON=python +goto running +) + +:running + +@echo Using %PYTHON% + +"%PYTHON%" -x "%~dp0waf" %* & Endlocal & exit /b %ERRORLEVEL% + From f23029efe043e00dfa6b574091a5e3257f1094d4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 17 Jun 2018 14:30:59 +0300 Subject: [PATCH 024/205] Refactoring wscript. Add execute flag on waf --- waf | 0 wscript | 113 ++++++++++++++++++++++++++++++++------------------------ 2 files changed, 64 insertions(+), 49 deletions(-) mode change 100644 => 100755 waf diff --git a/waf b/waf old mode 100644 new mode 100755 diff --git a/wscript b/wscript index 679c8dec..918510b2 100644 --- a/wscript +++ b/wscript @@ -3,92 +3,104 @@ # a1batross, mittorn, 2018 from __future__ import print_function -from waflib import Logs - +from waflib import Logs, Options import os import sys -def get_git_version(): - # try grab the current version number from git - version = "notset" - if os.path.exists(".git"): - try: - version = os.popen("git describe --dirty --always").read().strip() - except Exception as e: - print(e) - return version - VERSION = '0.99' APPNAME = 'xash3d-fwgs' -GIT_SHA = get_git_version() -SUBDIRS = [ 'game_launch', 'vgui_support', 'engine', 'mainui' ] - +SUBDIRS = [ 'game_launch', 'mainui', 'vgui_support', 'engine' ] top = '.' +def get_git_version(): + # try grab the current version number from git + version = 'notset' + if os.path.exists('.git'): + try: + version = os.popen('git describe --dirty --always').read().strip() + except Exception as e: + pass + + if(len(version) == 0): + version = 'notset' + + return version + def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': opt.load('msvc msvs') opt.add_option( - '--dedicated', action = 'store_true', dest = 'DEDICATED', default=False, + '--dedicated', action = 'store_true', dest = 'DEDICATED', default = False, help = 'build Xash Dedicated Server(XashDS)') opt.add_option( - '--64bits', action = 'store_true', dest = 'ALLOW64', default=False, + '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, help = 'allow targetting 64-bit engine') opt.add_option( - '--release', action = 'store_true', dest = 'RELEASE', default=False, + '--release', action = 'store_true', dest = 'RELEASE', default = False, help = 'strip debug info from binary and enable optimizations') + + opt.add_option( + '--no-download-deps', action = 'store_false', dest = 'AUTODL', default = True, + help = 'don\'t try to download dependencies from network') opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') opt.recurse(SUBDIRS) - + def configure(conf): - conf.env.MSVC_TARGETS = ['x86'] + conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC conf.load('compiler_cxx compiler_c') - if(conf.env.COMPILER_CC != 'msvc'): + if sys.platform == 'win32': + conf.load('msvc msvs') + + # Check if we have 64-bit toolchain + conf.env.DEST_64BIT = False # predict state + try: conf.check_cc( fragment=''' - #include - int main( void ) { printf("%ld", sizeof( void * )); return 0; } + int main( void ) + { + int check[sizeof(void*) == 4 ? 1: -1]; + return 0; + } ''', - execute = True, - define_ret = True, - uselib_store = 'SIZEOF_VOID_P', - msg = 'Checking sizeof(void*)') - else: - conf.env.SIZEOF_VOID_P = '4' # TODO: detect target + msg = 'Checking if compiler create 32 bit code') + except conf.errors.ConfigurationError: + # Program not compiled, we have 64 bit + conf.env.DEST_64BIT = True - if(int(conf.env.SIZEOF_VOID_P) != 4): + if(conf.env.DEST_64BIT): if(not conf.options.ALLOW64): - conf.env.append_value('LINKFLAGS', '-m32') - conf.env.append_value('CFLAGS', '-m32') - conf.env.append_value('CXXFLAGS', '-m32') + conf.env.append_value('LINKFLAGS', ['-m32']) + conf.env.append_value('CFLAGS', ['-m32']) + conf.env.append_value('CXXFLAGS', ['-m32']) Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') else: Logs.warn('WARNING: 64-bit engine may be unstable') if(conf.env.COMPILER_CC != 'msvc'): if(conf.env.COMPILER_CC == 'gcc'): - conf.env.append_value('LINKFLAGS', '-Wl,--no-undefined') + conf.env.append_unique('LINKFLAGS', ['-Wl,--no-undefined']) if(conf.options.RELEASE): - conf.env.append_unique('CFLAGS', '-O2') - conf.env.append_unique('CXXFLAGS', '-O2') + conf.env.append_unique('CFLAGS', ['-O2']) + conf.env.append_unique('CXXFLAGS', ['-O2']) else: - conf.env.append_unique('CFLAGS', '-Og') - conf.env.append_unique('CFLAGS', '-g') - conf.env.append_unique('CXXFLAGS', '-Og') - conf.env.append_unique('CXXFLAGS', '-g') + conf.env.append_unique('CFLAGS', ['-Og', '-g']) + conf.env.append_unique('CXXFLAGS', ['-Og', '-g']) else: - if(not conf.options.RELEASE): - conf.env.append_unique('CFLAGS', '/Z7') - conf.env.append_unique('CXXFLAGS', '/Z7') - conf.env.append_unique('LINKFLAGS', '/DEBUG') + if(conf.options.RELEASE): + conf.env.append_unique('CFLAGS', ['/O2']) + conf.env.append_unique('CXXFLAGS', ['/O2']) + else: + conf.env.append_unique('CFLAGS', ['/Z7']) + conf.env.append_unique('CXXFLAGS', ['/Z7']) + conf.env.append_unique('LINKFLAGS', ['/DEBUG']) if(conf.env.DEST_OS != 'win32'): conf.check( lib='dl' ) @@ -96,7 +108,7 @@ def configure(conf): conf.check( lib='pthread' ) conf.env.DEDICATED = conf.options.DEDICATED - conf.env.SINGLE_BINARY = conf.options.DEDICATED + conf.env.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated # indicate if we are packaging for Linux/BSD if(not conf.options.WIN_INSTALL and @@ -106,16 +118,19 @@ def configure(conf): else: # prefix is ignored conf.env.LIBDIR = conf.env.BINDIR = '/' - - # global - conf.env.append_unique('XASH_BUILD_COMMIT', GIT_SHA) + + conf.start_msg('Checking git hash') + git_version = get_git_version() + conf.end_msg(git_version) + conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="' + git_version + '"') for i in SUBDIRS: conf.setenv(i, conf.env) # derive new env from global one conf.env.ENVNAME = i - Logs.info('Configuring ' + i) + conf.msg(msg='Configuring ' + i, result='in progress', color='BLUE') # configure in standalone env conf.recurse(i) + conf.msg(msg='Configuring ' + i, result='done', color='BLUE') conf.setenv('') def build(bld): From 76e01f490c9654faee7d3726408ac8920379d213 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 19 Jun 2018 16:17:27 +0300 Subject: [PATCH 025/205] Update mainui. Make VGUI search message more friendly. Update gitignore --- .gitignore | 1 + mainui | 2 +- vgui_support/wscript | 13 ++++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index cac86b0e..e56161d8 100644 --- a/.gitignore +++ b/.gitignore @@ -313,5 +313,6 @@ build-* *.files # Waf +build_current .waf-* .lock-waf* diff --git a/mainui b/mainui index a61e1845..0c354a1d 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit a61e18458446f54fca061b5040628aae407144c1 +Subproject commit 0c354a1d9c6a7443f85ba4c7a8f93141420f281d diff --git a/vgui_support/wscript b/vgui_support/wscript index b4f59c29..66dcaeae 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -19,15 +19,19 @@ def configure(conf): if conf.options.DEDICATED: return + conf.start_msg('Checking for VGUI') + if not conf.options.VGUI_DEV: + conf.end_msg('no') conf.fatal("Provide a path to vgui-dev repository using --vgui key") - if conf.env.DEST_CPU != 'x86' and not (conf.env.DEST_CPU == 'x86_64' and not conf.options.ALLOW64): # multilib case + if conf.env.DEST_CPU != 'x86' and not (conf.env.DEST_CPU == 'x86_64' and not conf.options.ALLOW64): + conf.end_msg('no') conf.fatal('vgui is not supported on this CPU: ' + conf.env.DEST_CPU) if conf.env.DEST_OS == 'win32': - conf.env.LIB_VGUI = ['vgui.lib'] - conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib/win32_vc6/'))] + conf.env.LIB_VGUI = ['vgui.lib'] + conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib/win32_vc6/'))] else: if conf.env.DEST_OS == 'linux': conf.env.LIB_VGUI = [':vgui.so'] @@ -39,8 +43,7 @@ def configure(conf): conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'include'))] conf.env.HAVE_VGUI = 1 - - conf.msg('Checking VGUI', '{0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) + conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) From 1e7f9d00c3bc87b0c60d6d42a294efac811a82ea Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 19 Jun 2018 16:22:30 +0300 Subject: [PATCH 026/205] Apply 4150 update --- common/render_api.h | 2 +- engine/client/cl_debug.c | 242 +++++++ engine/client/cl_demo.c | 263 ++++++-- engine/client/cl_frame.c | 13 +- engine/client/cl_game.c | 2 +- engine/client/cl_main.c | 49 +- engine/client/cl_parse.c | 272 +------- engine/client/cl_pmove.c | 4 + engine/client/cl_qparse.c | 1007 ++++++++++++++++++++++++++++++ engine/client/cl_scrn.c | 1 + engine/client/cl_tent.c | 4 +- engine/client/cl_video.c | 3 + engine/client/client.h | 33 +- engine/client/gl_backend.c | 31 +- engine/client/gl_image.c | 2 +- engine/client/gl_local.h | 2 + engine/client/gl_refrag.c | 6 +- engine/client/gl_rmain.c | 4 +- engine/client/gl_rmisc.c | 5 +- engine/client/gl_rsurf.c | 25 +- engine/client/gl_studio.c | 4 +- engine/client/gl_vidnt.c | 36 +- engine/client/s_main.c | 20 +- engine/common/build.c | 2 +- engine/common/common.h | 1 + engine/common/filesystem.c | 13 +- engine/common/input.c | 42 +- engine/common/mod_bmodel.c | 8 +- engine/common/net_buffer.c | 4 +- engine/common/net_encode.c | 30 +- engine/common/net_encode.h | 16 +- engine/common/protocol.h | 61 ++ engine/common/soundlib/snd_mp3.c | 8 +- engine/server/server.h | 25 +- engine/server/sv_cmds.c | 3 +- engine/server/sv_frame.c | 44 ++ engine/server/sv_game.c | 112 ++-- engine/server/sv_init.c | 17 +- engine/server/sv_main.c | 4 +- engine/server/sv_save.c | 67 +- 40 files changed, 1976 insertions(+), 511 deletions(-) create mode 100644 engine/client/cl_debug.c create mode 100644 engine/client/cl_qparse.c diff --git a/common/render_api.h b/common/render_api.h index 1deb14a7..246d7512 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -40,7 +40,7 @@ GNU General Public License for more details. #define PARM_TEX_MIPCOUNT 15 // count of mipmaps (0 - autogenerated, 1 - disabled of mipmapping) #define PARM_BSP2_SUPPORTED 16 // tell custom renderer what engine is support BSP2 in this build #define PARM_SKY_SPHERE 17 // sky is quake sphere ? -//reserved +#define PARAM_GAMEPAUSED 18 // game is paused #define PARM_MAP_HAS_DELUXE 19 // map has deluxedata #define PARM_MAX_ENTITIES 20 #define PARM_WIDESCREEN 21 diff --git a/engine/client/cl_debug.c b/engine/client/cl_debug.c new file mode 100644 index 00000000..4f740a3c --- /dev/null +++ b/engine/client/cl_debug.c @@ -0,0 +1,242 @@ +/* +cl_debug.c - server message debugging +Copyright (C) 2018 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "client.h" +#include "net_encode.h" +#include "particledef.h" +#include "gl_local.h" +#include "cl_tent.h" +#include "shake.h" +#include "hltv.h" +#include "input.h" + +#define MSG_COUNT 32 // last 32 messages parsed +#define MSG_MASK (MSG_COUNT - 1) + +const char *svc_strings[svc_lastmsg+1] = +{ + "svc_bad", + "svc_nop", + "svc_disconnect", + "svc_event", + "svc_changing", + "svc_setview", + "svc_sound", + "svc_time", + "svc_print", + "svc_stufftext", + "svc_setangle", + "svc_serverdata", + "svc_lightstyle", + "svc_updateuserinfo", + "svc_deltatable", + "svc_clientdata", + "svc_resource", + "svc_pings", + "svc_particle", + "svc_restoresound", + "svc_spawnstatic", + "svc_event_reliable", + "svc_spawnbaseline", + "svc_temp_entity", + "svc_setpause", + "svc_signonnum", + "svc_centerprint", + "svc_unused27", + "svc_unused28", + "svc_unused29", + "svc_intermission", + "svc_finale", + "svc_cdtrack", + "svc_restore", + "svc_cutscene", + "svc_weaponanim", + "svc_bspdecal", + "svc_roomtype", + "svc_addangle", + "svc_usermessage", + "svc_packetentities", + "svc_deltapacketentities", + "svc_choke", + "svc_resourcelist", + "svc_deltamovevars", + "svc_resourcerequest", + "svc_customization", + "svc_crosshairangle", + "svc_soundfade", + "svc_filetxferfailed", + "svc_hltv", + "svc_director", + "svc_voiceinit", + "svc_voicedata", + "svc_unused54", + "svc_unused55", + "svc_resourcelocation", + "svc_querycvarvalue", + "svc_querycvarvalue2", +}; + +typedef struct +{ + int command; + int starting_offset; + int frame_number; +} oldcmd_t; + +typedef struct +{ + oldcmd_t oldcmd[MSG_COUNT]; + int currentcmd; + qboolean parsing; +} msg_debug_t; + +static msg_debug_t cls_message_debug; + +const char *CL_MsgInfo( int cmd ) +{ + static string sz; + + Q_strcpy( sz, "???" ); + + if( cmd >= 0 && cmd <= svc_lastmsg ) + { + // get engine message name + Q_strncpy( sz, svc_strings[cmd], sizeof( sz )); + } + else if( cmd > svc_lastmsg && cmd <= ( svc_lastmsg + MAX_USER_MESSAGES )) + { + int i; + + for( i = 0; i < MAX_USER_MESSAGES; i++ ) + { + if( clgame.msg[i].number == cmd ) + { + Q_strncpy( sz, clgame.msg[i].name, sizeof( sz )); + break; + } + } + } + return sz; +} + +/* +===================== +CL_Parse_Debug + +enable message debugging +===================== +*/ +void CL_Parse_Debug( qboolean enable ) +{ + cls_message_debug.parsing = enable; +} + +/* +===================== +CL_Parse_RecordCommand + +record new message params into debug buffer +===================== +*/ +void CL_Parse_RecordCommand( int cmd, int startoffset ) +{ + int slot; + + if( cmd == svc_nop ) return; + + slot = ( cls_message_debug.currentcmd++ & MSG_MASK ); + cls_message_debug.oldcmd[slot].command = cmd; + cls_message_debug.oldcmd[slot].starting_offset = startoffset; + cls_message_debug.oldcmd[slot].frame_number = host.framecount; +} + +/* +===================== +CL_ResetFrame +===================== +*/ +void CL_ResetFrame( frame_t *frame ) +{ + memset( &frame->graphdata, 0, sizeof( netbandwidthgraph_t )); + frame->receivedtime = host.realtime; + frame->valid = true; + frame->choked = false; + frame->latency = 0.0; + frame->time = cl.mtime[0]; +} + +/* +===================== +CL_WriteErrorMessage + +write net_message into buffer.dat for debugging +===================== +*/ +static void CL_WriteErrorMessage( int current_count, sizebuf_t *msg ) +{ + const char *buffer_file = "buffer.dat"; + file_t *fp; + + fp = FS_Open( buffer_file, "wb", false ); + if( !fp ) return; + + FS_Write( fp, &cls.starting_count, sizeof( int )); + FS_Write( fp, ¤t_count, sizeof( int )); + FS_Write( fp, MSG_GetData( msg ), MSG_GetMaxBytes( msg )); + FS_Close( fp ); + + Con_Printf( "Wrote erroneous message to %s\n", buffer_file ); +} + +/* +===================== +CL_WriteMessageHistory + +list last 32 messages for debugging net troubleshooting +===================== +*/ +void CL_WriteMessageHistory( void ) +{ + oldcmd_t *old, *failcommand; + sizebuf_t *msg = &net_message; + int i, thecmd; + + if( !cls.initialized || cls.state == ca_disconnected ) + return; + + if( !cls_message_debug.parsing ) + return; + + Con_Printf( "Last %i messages parsed.\n", MSG_COUNT ); + + // finish here + thecmd = cls_message_debug.currentcmd - 1; + thecmd -= ( MSG_COUNT - 1 ); // back up to here + + for( i = 0; i < MSG_COUNT - 1; i++ ) + { + thecmd &= MSG_MASK; + old = &cls_message_debug.oldcmd[thecmd]; + Con_Printf( "%i %04i %s\n", old->frame_number, old->starting_offset, CL_MsgInfo( old->command )); + thecmd++; + } + + failcommand = &cls_message_debug.oldcmd[thecmd]; + Con_Printf( "BAD: %3i:%s\n", MSG_GetNumBytesRead( msg ) - 1, CL_MsgInfo( failcommand->command )); + if( host_developer.value >= DEV_EXTENDED ) + CL_WriteErrorMessage( MSG_GetNumBytesRead( msg ) - 1, msg ); + cls_message_debug.parsing = false; +} \ No newline at end of file diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 47cf1b4b..505db0c8 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -626,6 +626,74 @@ void CL_ReadDemoSequence( qboolean discard ) cls.netchan.last_reliable_sequence = last_reliable_sequence; } +/* +================= +CL_DemoStartPlayback +================= +*/ +void CL_DemoStartPlayback( int mode ) +{ + if( cls.changedemo ) + { + S_StopAllSounds( true ); + SCR_BeginLoadingPlaque( false ); + + CL_ClearState (); + CL_InitEdicts (); // re-arrange edicts + } + else + { + // NOTE: at this point demo is still valid + CL_Disconnect(); + Host_ShutdownServer(); + + Con_FastClose(); + UI_SetActiveMenu( false ); + } + + cls.demoplayback = mode; + cls.state = ca_connected; + cl.background = (cls.demonum != -1) ? true : false; + cls.spectator = false; + cls.signon = 0; + + demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message + + Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, CL_GetFragmentSize ); + + memset( demo.cmds, 0, sizeof( demo.cmds )); + demo.angle_position = 1; + demo.framecount = 0; + cls.lastoutgoingcommand = -1; + cls.nextcmdtime = host.realtime; + cl.last_command_ack = -1; +} + +/* +================= +CL_PlayDemoQuake +================= +*/ +void CL_PlayDemoQuake( const char *demoname ) +{ + int c, neg = false; + + cls.demofile = FS_Open( demoname, "rb", true ); + Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); + Q_strncpy( gameui.globals->demoname, demoname, sizeof( gameui.globals->demoname )); + demo.header.host_fps = host_maxfps->value; + cls.forcetrack = 0; + + while(( c = FS_Getc( cls.demofile )) != '\n' ) + { + if( c == '-' ) neg = true; + else cls.forcetrack = cls.forcetrack * 10 + (c - '0'); + } + + if( neg ) cls.forcetrack = -cls.forcetrack; + CL_DemoStartPlayback( DEMO_QUAKE1 ); +} + /* ================= CL_DemoAborted @@ -735,6 +803,94 @@ qboolean CL_ReadRawNetworkData( byte *buffer, size_t *length ) return true; } +/* +================= +CL_DemoReadMessageQuake + +reads demo data and write it to client +================= +*/ +qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) +{ + int msglen = 0; + demoangle_t *a; + + *length = 0; // assume we fail + + // decide if it is time to grab the next message + if( cls.signon == SIGNONS ) // allways grab until fully connected + { + if( cls.timedemo ) + { + if( host.framecount == cls.td_lastframe ) + return false; // already read this frame's message + + cls.td_lastframe = host.framecount; + + // if this is the second frame, grab the real td_starttime + // so the bogus time on the first frame doesn't count + if( host.framecount == cls.td_startframe + 1 ) + cls.td_starttime = host.realtime; + } + else if( cl.time <= cl.mtime[0] ) + { + // don't need another message yet + return false; + } + } + + // get the next message + FS_Read( cls.demofile, &msglen, sizeof( int )); + FS_Read( cls.demofile, &cl.viewangles[0], sizeof( float )); + FS_Read( cls.demofile, &cl.viewangles[1], sizeof( float )); + FS_Read( cls.demofile, &cl.viewangles[2], sizeof( float )); + cls.netchan.incoming_sequence++; + + // make sure what interp info contain angles from different frames + // or lerping will stop working + if( demo.lasttime != demo.timestamp ) + { + // select entry into circular buffer + demo.angle_position = (demo.angle_position + 1) & ANGLE_MASK; + a = &demo.cmds[demo.angle_position]; + + // record update + a->starttime = demo.timestamp; + VectorCopy( cl.viewangles, a->viewangles ); + demo.lasttime = demo.timestamp; + } + + if( msglen < 0 ) + { + MsgDev( D_ERROR, "Demo message length < 0\n" ); + CL_DemoCompleted(); + return false; + } + + if( msglen > MAX_INIT_MSG ) + { + MsgDev( D_ERROR, "Demo message %i > %i\n", msglen, MAX_INIT_MSG ); + CL_DemoCompleted(); + return false; + } + + if( msglen > 0 ) + { + if( FS_Read( cls.demofile, buffer, msglen ) != msglen ) + { + MsgDev( D_ERROR, "Error reading demo message data\n" ); + CL_DemoCompleted(); + return false; + } + } + + *length = msglen; + + if( cls.state != ca_active ) + Cbuf_Execute(); + return true; +} + /* ================= CL_DemoReadMessage @@ -765,6 +921,9 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) return false; // paused } + if( cls.demoplayback == DEMO_QUAKE1 ) + return CL_DemoReadMessageQuake( buffer, length ); + do { qboolean bSkipMessage = false; @@ -933,7 +1092,8 @@ void CL_DemoInterpolateAngles( void ) QuaternionSlerp( q2, q1, frac, q ); QuaternionAngle( q, cl.viewangles ); } - else VectorCopy( cl.cmd->viewangles, cl.viewangles ); + else if( cls.demoplayback != DEMO_QUAKE1 ) + VectorCopy( cl.cmd->viewangles, cl.viewangles ); } /* @@ -976,7 +1136,8 @@ void CL_StopPlayback( void ) cls.demofile = NULL; cls.olddemonum = Q_max( -1, cls.demonum - 1 ); - Mem_Free( demo.directory.entries ); + if( demo.directory.entries != NULL ) + Mem_Free( demo.directory.entries ); cls.td_lastframe = host.framecount; demo.directory.numentries = 0; demo.directory.entries = NULL; @@ -1110,6 +1271,35 @@ qboolean CL_NextDemo( void ) return true; } +/* +================== +CL_CheckStartupDemos + +queue demos loop after movie playing +================== +*/ +void CL_CheckStartupDemos( void ) +{ + if( !cls.demos_pending ) + return; // no demos in loop + + if( cls.movienum != -1 ) + return; // wait until movies finished + + if( GameState->nextstate != STATE_RUNFRAME || cls.demoplayback ) + { + // commandline override + cls.demos_pending = false; + cls.demonum = -1; + return; + } + + // run demos loop in background mode + Cvar_SetValue( "v_dark", 1.0f ); + cls.demonum = 0; + CL_NextDemo (); +} + /* ================== CL_DemoGetName @@ -1230,8 +1420,9 @@ playdemo */ void CL_PlayDemo_f( void ) { - string filename; - string demoname; + char filename1[MAX_QPATH]; + char filename2[MAX_QPATH]; + char demoname[MAX_QPATH]; int i; if( Cmd_Argc() != 2 ) @@ -1251,17 +1442,24 @@ void CL_PlayDemo_f( void ) return; } - Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname ) - 1 ); - Q_snprintf( filename, sizeof( filename ), "demos/%s.dem", demoname ); + Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname )); + COM_StripExtension( demoname ); + Q_snprintf( filename1, sizeof( filename1 ), "%s.dem", demoname ); + Q_snprintf( filename2, sizeof( filename2 ), "demos/%s.dem", demoname ); - if( !FS_FileExists( filename, true )) + if( FS_FileExists( filename1, true )) { - MsgDev( D_ERROR, "couldn't open %s\n", filename ); + CL_PlayDemoQuake( filename1 ); + return; + } + else if( !FS_FileExists( filename2, true )) + { + MsgDev( D_ERROR, "couldn't open %s\n", filename2 ); CL_DemoAborted(); return; } - cls.demofile = FS_Open( filename, "rb", true ); + cls.demofile = FS_Open( filename2, "rb", true ); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( gameui.globals->demoname, demoname, sizeof( gameui.globals->demoname )); @@ -1270,7 +1468,7 @@ void CL_PlayDemo_f( void ) if( demo.header.id != IDEMOHEADER ) { - MsgDev( D_ERROR, "%s is not a demo file\n", filename ); + MsgDev( D_ERROR, "%s is not a demo file\n", demoname ); CL_DemoAborted(); return; } @@ -1297,24 +1495,6 @@ void CL_PlayDemo_f( void ) return; } - if( cls.changedemo ) - { - S_StopAllSounds( true ); - SCR_BeginLoadingPlaque( false ); - - CL_ClearState (); - CL_InitEdicts (); // re-arrange edicts - } - else - { - // NOTE: at this point demo is still valid - CL_Disconnect(); - Host_ShutdownServer(); - - Con_FastClose(); - UI_SetActiveMenu( false ); - } - // allocate demo entries demo.directory.entries = Mem_Malloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); @@ -1328,22 +1508,7 @@ void CL_PlayDemo_f( void ) FS_Seek( cls.demofile, demo.entry->offset, SEEK_SET ); - cls.demoplayback = true; - cls.state = ca_connected; - cl.background = (cls.demonum != -1) ? true : false; - cls.spectator = false; - cls.signon = 0; - - demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message - - Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, CL_GetFragmentSize ); - - memset( demo.cmds, 0, sizeof( demo.cmds )); - demo.angle_position = 1; - demo.framecount = 0; - cls.lastoutgoingcommand = -1; - cls.nextcmdtime = host.realtime; - cl.last_command_ack = -1; + CL_DemoStartPlayback( DEMO_XASH3D ); // g-cont. is this need? Q_strncpy( cls.servername, demoname, sizeof( cls.servername )); @@ -1402,15 +1567,7 @@ void CL_StartDemos_f( void ) for( i = 1; i < c + 1; i++ ) Q_strncpy( cls.demos[i-1], Cmd_Argv( i ), sizeof( cls.demos[0] )); - - if( !SV_Active() && !cls.demoplayback ) - { - // run demos loop in background mode - Cvar_SetValue( "v_dark", 1.0f ); - cls.demonum = 0; - CL_NextDemo (); - } - else cls.demonum = -1; + cls.demos_pending = true; } /* diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 576405d4..216b70c5 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -603,13 +603,13 @@ void CL_FlushEntityPacket( sizebuf_t *msg ) // read it all, but ignore it while( 1 ) { - newnum = MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ); + newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); if( newnum == LAST_EDICT ) break; // done if( MSG_CheckOverflow( msg )) Host_Error( "CL_FlushEntityPacket: overflow\n" ); - MSG_ReadDeltaEntity( msg, &from, &to, newnum, CL_IsPlayerIndex( newnum ), cl.mtime[0] ); + MSG_ReadDeltaEntity( msg, &from, &to, newnum, CL_IsPlayerIndex( newnum ) ? DELTA_PLAYER : DELTA_ENTITY, cl.mtime[0] ); } } @@ -626,17 +626,18 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t entity_state_t *state; qboolean newent = (old) ? false : true; int pack = frame->num_entities; - qboolean player = CL_IsPlayerIndex( newnum ); + int delta_type = DELTA_ENTITY; qboolean alive = true; // alloc next slot to store update state = &cls.packet_entities[cls.next_client_entities % cls.num_client_entities]; + if( CL_IsPlayerIndex( newnum )) delta_type = DELTA_PLAYER; if(( newnum < 0 ) || ( newnum >= clgame.maxEntities )) { MsgDev( D_ERROR, "CL_DeltaEntity: invalid newnum: %d\n", newnum ); if( has_update ) - MSG_ReadDeltaEntity( msg, old, state, newnum, player, cl.mtime[0] ); + MSG_ReadDeltaEntity( msg, old, state, newnum, delta_type, cl.mtime[0] ); return; } @@ -645,7 +646,7 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t if( newent ) old = &ent->baseline; if( has_update ) - alive = MSG_ReadDeltaEntity( msg, old, state, newnum, player, cl.mtime[0] ); + alive = MSG_ReadDeltaEntity( msg, old, state, newnum, delta_type, cl.mtime[0] ); else memcpy( state, old, sizeof( entity_state_t )); if( !alive ) @@ -1074,7 +1075,7 @@ void CL_LinkPacketEntities( frame_t *frame ) if( ent->curstate.rendermode == kRenderNormal ) { // auto 'solid' faces - if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && CL_IsQuakeCompatible( )) { ent->curstate.rendermode = kRenderTransAlpha; ent->curstate.renderamt = 255; diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 1f701166..d9bbab68 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1867,7 +1867,7 @@ int pfnDrawConsoleString( int x, int y, char *string ) int drawLen; if( !COM_CheckString( string )) - return 0; // silent ignore + return 0; // silent ignore Con_SetFont( con_fontsize->value ); clgame.ds.adjust_size = true; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 21901591..307eff59 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -146,6 +146,19 @@ qboolean CL_IsBackgroundMap( void ) return ( cl.background && !cls.demoplayback ); } +qboolean CL_IsQuakeCompatible( void ) +{ + // feature set + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + return true; + + // quake demo playing + if( cls.demoplayback == DEMO_QUAKE1 ) + return true; + + return false; +} + char *CL_Userinfo( void ) { return cls.userinfo; @@ -242,14 +255,31 @@ static float CL_LerpPoint( void ) if( f == 0.0f || cls.timedemo ) { cl.time = cl.mtime[0]; - - // g-cont. probably this is redundant - if( cls.demoplayback ) - cl.oldtime = cl.mtime[0] - cl_clientframetime(); - return 1.0f; } + if( f > 0.1f ) + { + // dropped packet, or start of demo + cl.mtime[1] = cl.mtime[0] - 0.1f; + f = 0.1f; + } +#if 1 + frac = (cl.time - cl.mtime[1]) / f; + + if( frac < 0.0f ) + { + if( frac < -0.01 ) + cl.time = cl.mtime[1]; + frac = 0.0f; + } + else if( frac > 1.0f ) + { + if( frac > 1.01 ) + cl.time = cl.mtime[0]; + frac = 1.0f; + } +#else if( cl_interp->value > 0.001f ) { // manual lerp value (goldsrc mode) @@ -260,7 +290,7 @@ static float CL_LerpPoint( void ) // automatic lerp (classic mode) frac = ( cl.time - cl.mtime[1] ) / f; } - +#endif return frac; } @@ -2001,7 +2031,10 @@ void CL_ReadNetMessage( void ) if( !cls.demoplayback && !Netchan_Process( &cls.netchan, &net_message )) continue; // wasn't accepted for some reason - CL_ParseServerMessage( &net_message, true ); + // run special handler for quake demos + if( cls.demoplayback == DEMO_QUAKE1 ) + CL_ParseQuakeMessage( &net_message, true ); + else CL_ParseServerMessage( &net_message, true ); cl.send_reply = true; } @@ -2044,7 +2077,7 @@ void CL_ReadPackets( void ) // decide the simulation time cl.oldtime = cl.time; - if( !cls.demoplayback && !cl.paused ) + if( cls.demoplayback != DEMO_XASH3D && !cl.paused ) cl.time += host.frametime; // demo time diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index b43a0183..9e54a943 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -23,200 +23,8 @@ GNU General Public License for more details. #include "hltv.h" #include "input.h" -#define MSG_COUNT 32 // last 32 messages parsed -#define MSG_MASK (MSG_COUNT - 1) - int CL_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; -const char *svc_strings[svc_lastmsg+1] = -{ - "svc_bad", - "svc_nop", - "svc_disconnect", - "svc_event", - "svc_changing", - "svc_setview", - "svc_sound", - "svc_time", - "svc_print", - "svc_stufftext", - "svc_setangle", - "svc_serverdata", - "svc_lightstyle", - "svc_updateuserinfo", - "svc_deltatable", - "svc_clientdata", - "svc_resource", - "svc_pings", - "svc_particle", - "svc_restoresound", - "svc_spawnstatic", - "svc_event_reliable", - "svc_spawnbaseline", - "svc_temp_entity", - "svc_setpause", - "svc_signonnum", - "svc_centerprint", - "svc_unused27", - "svc_unused28", - "svc_unused29", - "svc_intermission", - "svc_finale", - "svc_cdtrack", - "svc_restore", - "svc_cutscene", - "svc_weaponanim", - "svc_bspdecal", - "svc_roomtype", - "svc_addangle", - "svc_usermessage", - "svc_packetentities", - "svc_deltapacketentities", - "svc_choke", - "svc_resourcelist", - "svc_deltamovevars", - "svc_resourcerequest", - "svc_customization", - "svc_crosshairangle", - "svc_soundfade", - "svc_filetxferfailed", - "svc_hltv", - "svc_director", - "svc_voiceinit", - "svc_voicedata", - "svc_unused54", - "svc_unused55", - "svc_resourcelocation", - "svc_querycvarvalue", - "svc_querycvarvalue2", -}; - -typedef struct -{ - int command; - int starting_offset; - int frame_number; -} oldcmd_t; - -typedef struct -{ - oldcmd_t oldcmd[MSG_COUNT]; - int currentcmd; - qboolean parsing; -} msg_debug_t; - -static msg_debug_t cls_message_debug; -static int starting_count; - -const char *CL_MsgInfo( int cmd ) -{ - static string sz; - - Q_strcpy( sz, "???" ); - - if( cmd >= 0 && cmd <= svc_lastmsg ) - { - // get engine message name - Q_strncpy( sz, svc_strings[cmd], sizeof( sz )); - } - else if( cmd > svc_lastmsg && cmd <= ( svc_lastmsg + MAX_USER_MESSAGES )) - { - int i; - - for( i = 0; i < MAX_USER_MESSAGES; i++ ) - { - if( clgame.msg[i].number == cmd ) - { - Q_strncpy( sz, clgame.msg[i].name, sizeof( sz )); - break; - } - } - } - return sz; -} - -/* -===================== -CL_Parse_RecordCommand - -record new message params into debug buffer -===================== -*/ -void CL_Parse_RecordCommand( int cmd, int startoffset ) -{ - int slot; - - if( cmd == svc_nop ) return; - - slot = ( cls_message_debug.currentcmd++ & MSG_MASK ); - cls_message_debug.oldcmd[slot].command = cmd; - cls_message_debug.oldcmd[slot].starting_offset = startoffset; - cls_message_debug.oldcmd[slot].frame_number = host.framecount; -} - -/* -===================== -CL_WriteErrorMessage - -write net_message into buffer.dat for debugging -===================== -*/ -void CL_WriteErrorMessage( int current_count, sizebuf_t *msg ) -{ - const char *buffer_file = "buffer.dat"; - file_t *fp; - - fp = FS_Open( buffer_file, "wb", false ); - if( !fp ) return; - - FS_Write( fp, &starting_count, sizeof( int )); - FS_Write( fp, ¤t_count, sizeof( int )); - FS_Write( fp, MSG_GetData( msg ), MSG_GetMaxBytes( msg )); - FS_Close( fp ); - - Con_Printf( "Wrote erroneous message to %s\n", buffer_file ); -} - -/* -===================== -CL_WriteMessageHistory - -list last 32 messages for debugging net troubleshooting -===================== -*/ -void CL_WriteMessageHistory( void ) -{ - oldcmd_t *old, *failcommand; - sizebuf_t *msg = &net_message; - int i, thecmd; - - if( !cls.initialized || cls.state == ca_disconnected ) - return; - - if( !cls_message_debug.parsing ) - return; - - Con_Printf( "Last %i messages parsed.\n", MSG_COUNT ); - - // finish here - thecmd = cls_message_debug.currentcmd - 1; - thecmd -= ( MSG_COUNT - 1 ); // back up to here - - for( i = 0; i < MSG_COUNT - 1; i++ ) - { - thecmd &= MSG_MASK; - old = &cls_message_debug.oldcmd[thecmd]; - Con_Printf( "%i %04i %s\n", old->frame_number, old->starting_offset, CL_MsgInfo( old->command )); - thecmd++; - } - - failcommand = &cls_message_debug.oldcmd[thecmd]; - Con_Printf( "BAD: %3i:%s\n", MSG_GetNumBytesRead( msg ) - 1, CL_MsgInfo( failcommand->command )); - if( host_developer.value >= DEV_EXTENDED ) - CL_WriteErrorMessage( MSG_GetNumBytesRead( msg ) - 1, msg ); - cls_message_debug.parsing = false; -} - /* =============== CL_UserMsgStub @@ -387,6 +195,9 @@ void CL_ParseServerTime( sizebuf_t *msg ) cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat( msg ); + if( cls.demoplayback == DEMO_QUAKE1 ) + return; // don't mess the time + if( cl.maxclients == 1 ) cl.time = cl.mtime[0]; @@ -502,46 +313,27 @@ static client entity */ void CL_ParseStaticEntity( sizebuf_t *msg ) { - entity_state_t state; + int i, newnum; + entity_state_t from, to; cl_entity_t *ent; - int i; - memset( &state, 0, sizeof( state )); - - state.modelindex = MSG_ReadShort( msg ); - state.sequence = MSG_ReadWord( msg ); - state.frame = MSG_ReadWord( msg ) * (1.0f / 128.0f); - state.colormap = MSG_ReadWord( msg ); - state.skin = MSG_ReadByte( msg ); - state.body = MSG_ReadByte( msg ); - state.scale = MSG_ReadCoord( msg ); - MSG_ReadVec3Coord( msg, state.origin ); - MSG_ReadVec3Angles( msg, state.angles ); - state.rendermode = MSG_ReadByte( msg ); - - if( state.rendermode != kRenderNormal ) - { - state.renderamt = MSG_ReadByte( msg ); - state.rendercolor.r = MSG_ReadByte( msg ); - state.rendercolor.g = MSG_ReadByte( msg ); - state.rendercolor.b = MSG_ReadByte( msg ); - state.renderfx = MSG_ReadByte( msg ); - } + memset( &from, 0, sizeof( from )); + newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + MSG_ReadDeltaEntity( msg, &from, &to, 0, DELTA_STATIC, cl.mtime[0] ); i = clgame.numStatics; if( i >= MAX_STATIC_ENTITIES ) { - MsgDev( D_ERROR, "CL_ParseStaticEntity: static entities limit exceeded!\n" ); + Con_Printf( S_ERROR, "MAX_STATIC_ENTITIES limit exceeded!\n" ); return; } ent = &clgame.static_entities[i]; clgame.numStatics++; - ent->index = 0; // ??? - ent->baseline = state; - ent->curstate = state; - ent->prevstate = state; + // all states are same + ent->baseline = ent->curstate = ent->prevstate = to; + ent->index = 0; // static entities doesn't has the numbers // statics may be respawned in game e.g. for demo recording if( cls.state == ca_connected || cls.state == ca_validate ) @@ -550,14 +342,14 @@ void CL_ParseStaticEntity( sizebuf_t *msg ) // setup the new static entity VectorCopy( ent->curstate.origin, ent->origin ); VectorCopy( ent->curstate.angles, ent->angles ); - ent->model = CL_ModelHandle( state.modelindex ); + ent->model = CL_ModelHandle( to.modelindex ); ent->curstate.framerate = 1.0f; CL_ResetLatchedVars( ent, true ); if( ent->curstate.rendermode == kRenderNormal && ent->model != NULL ) { // auto 'solid' faces - if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && CL_IsQuakeCompatible( )) { ent->curstate.rendermode = kRenderTransAlpha; ent->curstate.renderamt = 255; @@ -2181,21 +1973,6 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num ) } } -/* -===================== -CL_ResetFrame -===================== -*/ -void CL_ResetFrame( frame_t *frame ) -{ - memset( &frame->graphdata, 0, sizeof( netbandwidthgraph_t )); - frame->receivedtime = host.realtime; - frame->valid = true; - frame->choked = false; - frame->latency = 0.0; - frame->time = cl.mtime[0]; -} - /* ===================================================================== @@ -2216,8 +1993,8 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) int cmd, param1, param2; int old_background; - cls_message_debug.parsing = true; // begin parsing - starting_count = MSG_GetNumBytesRead( msg ); // updates each frame + cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame + CL_Parse_Debug( true ); // begin parsing if( normal_message ) { @@ -2251,6 +2028,8 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) cmd = MSG_ReadServerCmd( msg ); +// Msg( "%s\n", CL_MsgInfo( cmd )); + // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); @@ -2278,7 +2057,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) cls.changelevel = true; S_StopAllSounds( true ); - MsgDev( D_INFO, "Server changing, reconnecting\n" ); + Con_Printf( "Server changing, reconnecting\n" ); if( cls.demoplayback ) { @@ -2289,7 +2068,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) CL_ClearState (); CL_InitEdicts (); // re-arrange edicts } - else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); + else Con_Printf( "Server disconnected, reconnecting\n" ); if( cls.demoplayback ) { @@ -2299,7 +2078,8 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) else { // g-cont. local client skip the challenge - if( SV_Active()) cls.state = ca_disconnected; + if( SV_Active( )) + cls.state = ca_disconnected; else cls.state = ca_connecting; cl.background = old_background; cls.connect_time = MAX_HEARTBEAT; @@ -2477,8 +2257,8 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) } } - cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - starting_count; - cls_message_debug.parsing = false; // done + cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - cls.starting_count; + CL_Parse_Debug( false ); // done // we don't know if it is ok to save a demo message until // after we have parsed the frame @@ -2486,11 +2266,11 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) { if( cls.demorecording && !cls.demowaiting ) { - CL_WriteDemoMessage( false, starting_count, msg ); + CL_WriteDemoMessage( false, cls.starting_count, msg ); } else if( cls.state != ca_active ) { - CL_WriteDemoMessage( true, starting_count, msg ); + CL_WriteDemoMessage( true, cls.starting_count, msg ); } } } \ No newline at end of file diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 8afa0df2..5d706939 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -115,6 +115,10 @@ qboolean CL_IsPredicted( void ) { if( cl_nopred->value || cl.intermission ) return false; + + // never predict the quake demos + if( cls.demoplayback == DEMO_QUAKE1 ) + return false; return true; } diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c new file mode 100644 index 00000000..45c9959f --- /dev/null +++ b/engine/client/cl_qparse.c @@ -0,0 +1,1007 @@ +/* +cl_qparse.c - parse a message received from the Quake demo +Copyright (C) 2018 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "client.h" +#include "net_encode.h" +#include "particledef.h" +#include "gl_local.h" +#include "cl_tent.h" +#include "shake.h" +#include "hltv.h" +#include "input.h" + +#define STAT_HEALTH 0 +#define STAT_FRAGS 1 +#define STAT_WEAPON 2 +#define STAT_AMMO 3 +#define STAT_ARMOR 4 +#define STAT_WEAPONFRAME 5 +#define STAT_SHELLS 6 +#define STAT_NAILS 7 +#define STAT_ROCKETS 8 +#define STAT_CELLS 9 +#define STAT_ACTIVEWEAPON 10 +#define STAT_TOTALSECRETS 11 +#define STAT_TOTALMONSTERS 12 +#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret +#define STAT_MONSTERS 14 // bumped by svc_killedmonster +#define MAX_STATS 32 + +static char msg_buf[8192]; +static sizebuf_t msg_demo; + +/* +================== +CL_DispatchQuakeMessage + +================== +*/ +static void CL_DispatchQuakeMessage( const char *name ) +{ + CL_DispatchUserMessage( name, msg_demo.iCurBit >> 3, msg_demo.pData ); + MSG_Clear( &msg_demo ); // don't forget to clear buffer +} + +/* +================== +CL_ParseQuakeStats + +redirect to qwrap->client +================== +*/ +static void CL_ParseQuakeStats( sizebuf_t *msg ) +{ + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); // stat num + MSG_WriteLong( &msg_demo, MSG_ReadLong( msg )); // stat value + CL_DispatchQuakeMessage( "Stats" ); +} + +/* +================== +CL_ParseQuakeStats + +redirect to qwrap->client +================== +*/ +static int CL_UpdateQuakeStats( sizebuf_t *msg, int statnum, qboolean has_update ) +{ + int value = 0; + + MSG_WriteByte( &msg_demo, statnum ); // stat num + + if( has_update ) + { + if( statnum == STAT_HEALTH ) + value = MSG_ReadShort( msg ); + else value = MSG_ReadByte( msg ); + } + + MSG_WriteLong( &msg_demo, value ); + CL_DispatchQuakeMessage( "Stats" ); + + return value; +} + +/* +================== +CL_ParseQuakeSound + +================== +*/ +static void CL_ParseQuakeSound( sizebuf_t *msg ) +{ + int channel, sound; + int flags, entnum; + float volume, attn; + sound_t handle; + vec3_t pos; + + flags = MSG_ReadByte( msg ); + + if( FBitSet( flags, SND_VOLUME )) + volume = (float)MSG_ReadByte( msg ) / 255.0f; + else volume = VOL_NORM; + + if( FBitSet( flags, SND_ATTENUATION )) + attn = (float)MSG_ReadByte( msg ) / 64.0f; + else attn = ATTN_NONE; + + channel = MSG_ReadWord( msg ); + sound = MSG_ReadByte( msg ); // Quake1 have max 255 precached sounds. erm + + // positioned in space + MSG_ReadVec3Coord( msg, pos ); + + entnum = channel >> 3; // entity reletive + channel &= 7; + + // see precached sound + handle = cl.sound_index[sound]; + + if( !cl.audio_prepped ) + { + Con_Printf( S_WARN "CL_StartSoundPacket: ignore sound message: too early\n" ); + return; // too early + } + + S_StartSound( pos, entnum, channel, handle, volume, attn, PITCH_NORM, flags ); +} + +/* +================== +CL_ParseQuakeServerInfo + +================== +*/ +static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) +{ + resource_t *pResource; + const char *pResName; + int gametype; + int i; + + Con_Reportf( "Serverdata packet received.\n" ); + cls.timestart = Sys_DoubleTime(); + + cls.demowaiting = false; // server is changed + + // wipe the client_t struct + if( !cls.changelevel && !cls.changedemo ) + CL_ClearState (); + cl.background = (cls.demonum != -1) ? true : false; + cls.state = ca_connected; + + // parse protocol version number + i = MSG_ReadLong( msg ); + + if( i != PROTOCOL_VERSION_QUAKE ) + Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION_QUAKE ); + + cl.maxclients = MSG_ReadByte( msg ); + gametype = MSG_ReadByte( msg ); // FIXME: tell the client about gametype + clgame.maxEntities = GI->max_edicts; + clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS ); + clgame.maxModels = MAX_MODELS; + Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); + + // Re-init hud video, especially if we changed game directories + clgame.dllFuncs.pfnVidInit(); + + if( Con_FixedFont( )) + { + // seperate the printfs so the server message can have a color + Con_Print( "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n" ); + Con_Print( va( "%c%s\n\n", 2, clgame.maptitle )); + } + + // multiplayer game? + if( cl.maxclients > 1 ) + { + // allow console in multiplayer games + host.allow_console = true; + + // loading user settings + CSCR_LoadDefaultCVars( "user.scr" ); + + if( r_decals->value > mp_decals.value ) + Cvar_SetValue( "r_decals", mp_decals.value ); + } + else Cvar_Reset( "r_decals" ); + + // re-init mouse + if( cl.background ) + host.mouse_visible = false; + + if( cl.background ) // tell the game parts about background state + Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); + else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); + + S_StopBackgroundTrack (); + + if( !cls.changedemo ) + UI_SetActiveMenu( cl.background ); + else if( !cls.demoplayback ) + Key_SetKeyDest( key_menu ); + + // don't reset cursor in background mode + if( cl.background ) + IN_MouseRestorePos(); + + // will be changed later + cl.viewentity = cl.playernum + 1; + gameui.globals->maxClients = cl.maxclients; + Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle )); + + if( !cls.changelevel && !cls.changedemo ) + CL_InitEdicts (); // re-arrange edicts + + // Quake just have a large packet of initialization data + for( i = 1; i < MAX_MODELS; i++ ) + { + pResName = MSG_ReadString( msg ); + + if( !COM_CheckString( pResName )) + break; // end of list + + pResource = Mem_Calloc( cls.mempool, sizeof( resource_t )); + pResource->type = t_model; + + Q_strncpy( pResource->szFileName, pResName, sizeof( pResource->szFileName )); + if( i == 1 ) Q_strncpy( clgame.mapname, pResName, sizeof( clgame.mapname )); + pResource->nDownloadSize = -1; + pResource->nIndex = i; + + CL_AddToResourceList( pResource, &cl.resourcesneeded ); + } + + for( i = 1; i < MAX_SOUNDS; i++ ) + { + pResName = MSG_ReadString( msg ); + + if( !COM_CheckString( pResName )) + break; // end of list + + pResource = Mem_Calloc( cls.mempool, sizeof( resource_t )); + pResource->type = t_sound; + + Q_strncpy( pResource->szFileName, pResName, sizeof( pResource->szFileName )); + pResource->nDownloadSize = -1; + pResource->nIndex = i; + + CL_AddToResourceList( pResource, &cl.resourcesneeded ); + } + + // get splash name + if( cls.demoplayback && ( cls.demonum != -1 )) + Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, glState.wideScreen ? "16x9" : "4x3" )); + else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, glState.wideScreen ? "16x9" : "4x3" )); + Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar + + if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background ) + { + if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true )) + Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen + cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime) + } + + memset( &clgame.movevars, 0, sizeof( clgame.movevars )); + memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); + memset( &clgame.centerPrint, 0, sizeof( clgame.centerPrint )); + cl.video_prepped = false; + cl.audio_prepped = false; + + // now we can start to precache + CL_BatchResourceRequest( true ); + + clgame.movevars.wateralpha = 1.0f; + clgame.entities->curstate.scale = 0.0f; + clgame.movevars.waveHeight = 0.0f; + clgame.movevars.zmax = 14172.0f; // 8192 * 1.74 + clgame.movevars.gravity = 800.0f; // quake doesn't write gravity in demos + + memcpy( &clgame.oldmovevars, &clgame.movevars, sizeof( movevars_t )); +} + +/* +================== +CL_ParseQuakeClientData + +================== +*/ +static void CL_ParseQuakeClientData( sizebuf_t *msg ) +{ + int i, bits = MSG_ReadWord( msg ); + frame_t *frame; + + // this is the frame update that this message corresponds to + i = cls.netchan.incoming_sequence; + + cl.parsecount = i; // ack'd incoming messages. + cl.parsecountmod = cl.parsecount & CL_UPDATE_MASK; // index into window. + frame = &cl.frames[cl.parsecountmod]; // frame at index. + frame->time = cl.mtime[0]; // mark network received time + frame->receivedtime = host.realtime; // time now that we are parsing. + memset( &frame->graphdata, 0, sizeof( netbandwidthgraph_t )); + memset( frame->flags, 0, sizeof( frame->flags )); + frame->first_entity = cls.next_client_entities; + frame->num_entities = 0; + frame->valid = true; // assume valid + + if( FBitSet( bits, SU_VIEWHEIGHT )) + frame->clientdata.view_ofs[2] = MSG_ReadChar( msg ); + else frame->clientdata.view_ofs[2] = 22.0f; + + if( FBitSet( bits, SU_IDEALPITCH )) + cl.local.idealpitch = MSG_ReadChar( msg ); + else cl.local.idealpitch = 0; + + for( i = 0; i < 3; i++ ) + { + if( FBitSet( bits, SU_PUNCH1 << i )) + frame->clientdata.punchangle[i] = (float)MSG_ReadChar( msg ); + else frame->clientdata.punchangle[i] = 0.0f; + + if( FBitSet( bits, ( SU_VELOCITY1 << i ))) + frame->clientdata.velocity[i] = MSG_ReadChar( msg ) * 16.0f; + else frame->clientdata.velocity[i] = 0; + } + + if( FBitSet( bits, SU_ONGROUND )) + SetBits( frame->clientdata.flags, FL_ONGROUND ); + if( FBitSet( bits, SU_INWATER )) + SetBits( frame->clientdata.flags, FL_INWATER ); + + // [always sent] + MSG_WriteLong( &msg_demo, MSG_ReadLong( msg )); + CL_DispatchQuakeMessage( "Items" ); + + if( FBitSet( bits, SU_WEAPONFRAME )) + CL_UpdateQuakeStats( msg, STAT_WEAPONFRAME, true ); + else CL_UpdateQuakeStats( msg, STAT_WEAPONFRAME, false ); + + if( FBitSet( bits, SU_ARMOR )) + CL_UpdateQuakeStats( msg, STAT_ARMOR, true ); + else CL_UpdateQuakeStats( msg, STAT_ARMOR, false ); + + if( FBitSet( bits, SU_WEAPON )) + frame->clientdata.viewmodel = CL_UpdateQuakeStats( msg, STAT_WEAPON, true ); + else frame->clientdata.viewmodel = CL_UpdateQuakeStats( msg, STAT_WEAPON, false ); + + cl.local.health = CL_UpdateQuakeStats( msg, STAT_HEALTH, true ); + CL_UpdateQuakeStats( msg, STAT_AMMO, true ); + CL_UpdateQuakeStats( msg, STAT_SHELLS, true ); + CL_UpdateQuakeStats( msg, STAT_NAILS, true ); + CL_UpdateQuakeStats( msg, STAT_ROCKETS, true ); + CL_UpdateQuakeStats( msg, STAT_CELLS, true ); + CL_UpdateQuakeStats( msg, STAT_ACTIVEWEAPON, true ); +} + +/* +================== +CL_ParseQuakeEntityData + +Parse an entity update message from the server +If an entities model or origin changes from frame to frame, it must be +relinked. Other attributes can change without relinking. +================== +*/ +void CL_ParseQuakeEntityData( sizebuf_t *msg, int bits ) +{ + int i, newnum, pack; + qboolean forcelink; + entity_state_t *state; + frame_t *frame; + cl_entity_t *ent; + + // first update is the final signon stage where we actually receive an entity (i.e., the world at least) + if( cls.signon == ( SIGNONS - 1 )) + { + // we are done with signon sequence. + cls.signon = SIGNONS; + + // Clear loading plaque. + CL_SignonReply (); + } + + // alloc next slot to store update + state = &cls.packet_entities[cls.next_client_entities % cls.num_client_entities]; + cl.validsequence = cls.netchan.incoming_sequence; + frame = &cl.frames[cl.parsecountmod]; + pack = frame->num_entities; + + if( FBitSet( bits, U_MOREBITS )) + { + i = MSG_ReadByte( msg ); + SetBits( bits, i << 8 ); + } + + if( FBitSet( bits, U_LONGENTITY )) + newnum = MSG_ReadWord( msg ); + else newnum = MSG_ReadByte( msg ); + + memset( state, 0, sizeof( *state )); + SetBits( state->entityType, ENTITY_NORMAL ); + state->number = newnum; + + // mark all the players + ent = CL_EDICT_NUM( newnum ); + ent->index = newnum; // enumerate entity index + ent->player = CL_IsPlayerIndex( newnum ); + + if( ent->curstate.msg_time != cl.mtime[1] ) + forcelink = true; // no previous frame to lerp from + else forcelink = false; + + if( FBitSet( bits, U_MODEL )) + state->modelindex = MSG_ReadByte( msg ); + else state->modelindex = ent->baseline.modelindex; + + if( FBitSet( bits, U_FRAME )) + state->frame = MSG_ReadByte( msg ); + else state->frame = ent->baseline.frame; + + if( FBitSet( bits, U_COLORMAP )) + state->colormap = MSG_ReadByte( msg ); + else state->colormap = ent->baseline.colormap; + + if( FBitSet( bits, U_SKIN )) + state->skin = MSG_ReadByte( msg ); + else state->skin = ent->baseline.skin; + + if( FBitSet( bits, U_EFFECTS )) + state->effects = MSG_ReadByte( msg ); + else state->effects = ent->baseline.effects; + + if( FBitSet( bits, U_ORIGIN1 )) + state->origin[0] = MSG_ReadCoord( msg ); + else state->origin[0] = ent->baseline.origin[0]; + + if( FBitSet( bits, U_ANGLE1 )) + state->angles[0] = MSG_ReadAngle( msg ); + else state->angles[0] = ent->baseline.angles[0]; + + if( FBitSet( bits, U_ORIGIN2 )) + state->origin[1] = MSG_ReadCoord( msg ); + else state->origin[1] = ent->baseline.origin[1]; + + if( FBitSet( bits, U_ANGLE2 )) + state->angles[1] = MSG_ReadAngle( msg ); + else state->angles[1] = ent->baseline.angles[1]; + + if( FBitSet( bits, U_ORIGIN3 )) + state->origin[2] = MSG_ReadCoord( msg ); + else state->origin[2] = ent->baseline.origin[2]; + + if( FBitSet( bits, U_ANGLE3 )) + state->angles[2] = MSG_ReadAngle( msg ); + else state->angles[2] = ent->baseline.angles[2]; + + if( FBitSet( bits, U_TRANS )) + { + int temp = MSG_ReadFloat( msg ); + float alpha = MSG_ReadFloat( msg ); + + if( alpha == 0.0f ) alpha = 1.0f; + + if( alpha < 1.0f ) + { + state->rendermode = kRenderTransTexture; + state->renderamt = (int)(alpha * 255.0f); + } + + if( temp == 2 && MSG_ReadFloat( msg )) + SetBits( state->effects, EF_FULLBRIGHT ); + } + + if( FBitSet( bits, U_NOLERP )) + forcelink = true; + + if( FBitSet( state->effects, 16 )) + SetBits( state->effects, EF_NODRAW ); + + if(( newnum - 1 ) == cl.playernum ) + VectorCopy( state->origin, frame->clientdata.origin ); + + if( forcelink ) + { + // interpolation must be reset + SETVISBIT( frame->flags, pack ); + + // release beams from previous entity + CL_KillDeadBeams( ent ); + } + + // add entity to packet + cls.next_client_entities++; + frame->num_entities++; +} + +/* +================== +CL_ParseQuakeParticles + +================== +*/ +void CL_ParseQuakeParticle( sizebuf_t *msg ) +{ + int count, color; + vec3_t org, dir; + + MSG_ReadVec3Coord( msg, org ); + dir[0] = MSG_ReadChar( msg ) * 0.0625f; + dir[1] = MSG_ReadChar( msg ) * 0.0625f; + dir[2] = MSG_ReadChar( msg ) * 0.0625f; + count = MSG_ReadByte( msg ); + color = MSG_ReadByte( msg ); + if( count == 255 ) count = 1024; + + R_RunParticleEffect( org, dir, color, count ); +} + +/* +=================== +CL_ParseQuakeStaticSound + +=================== +*/ +void CL_ParseQuakeStaticSound( sizebuf_t *msg ) +{ + int sound_num; + float vol, attn; + vec3_t org; + + MSG_ReadVec3Coord( msg, org ); + sound_num = MSG_ReadByte( msg ); + vol = (float)MSG_ReadByte( msg ) / 255.0f; + attn = (float)MSG_ReadByte( msg ) / 64.0f; + + S_StartSound( org, 0, CHAN_STATIC, cl.sound_index[sound_num], vol, attn, PITCH_NORM, 0 ); +} + +/* +================== +CL_ParseQuakeDamage + +redirect to qwrap->client +================== +*/ +static void CL_ParseQuakeDamage( sizebuf_t *msg ) +{ + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); // armor + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); // blood + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); // direction + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); // direction + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); // direction + CL_DispatchQuakeMessage( "Damage" ); +} + +/* +=================== +CL_ParseQuakeStaticEntity + +=================== +*/ +static void CL_ParseQuakeStaticEntity( sizebuf_t *msg ) +{ + entity_state_t state; + cl_entity_t *ent; + int i; + + memset( &state, 0, sizeof( state )); + + state.modelindex = MSG_ReadByte( msg ); + state.frame = MSG_ReadByte( msg ); + state.colormap = MSG_ReadByte( msg ); + state.skin = MSG_ReadByte( msg ); + state.origin[0] = MSG_ReadCoord( msg ); + state.angles[0] = MSG_ReadAngle( msg ); + state.origin[1] = MSG_ReadCoord( msg ); + state.angles[1] = MSG_ReadAngle( msg ); + state.origin[2] = MSG_ReadCoord( msg ); + state.angles[2] = MSG_ReadAngle( msg ); + + i = clgame.numStatics; + if( i >= MAX_STATIC_ENTITIES ) + { + Con_Printf( S_ERROR, "CL_ParseStaticEntity: static entities limit exceeded!\n" ); + return; + } + + ent = &clgame.static_entities[i]; + clgame.numStatics++; + + ent->index = 0; // ??? + ent->baseline = state; + ent->curstate = state; + ent->prevstate = state; + + // statics may be respawned in game e.g. for demo recording + if( cls.state == ca_connected || cls.state == ca_validate ) + ent->trivial_accept = INVALID_HANDLE; + + // setup the new static entity + VectorCopy( ent->curstate.origin, ent->origin ); + VectorCopy( ent->curstate.angles, ent->angles ); + ent->model = CL_ModelHandle( state.modelindex ); + ent->curstate.framerate = 1.0f; + CL_ResetLatchedVars( ent, true ); + + if( ent->model != NULL ) + { + // auto 'solid' faces + if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && CL_IsQuakeCompatible( )) + { + ent->curstate.rendermode = kRenderTransAlpha; + ent->curstate.renderamt = 255; + } + } + + R_AddEfrags( ent ); // add link +} + +/* +=================== +CL_ParseQuakeBaseline + +=================== +*/ +static void CL_ParseQuakeBaseline( sizebuf_t *msg ) +{ + entity_state_t state; + cl_entity_t *ent; + int newnum; + + memset( &state, 0, sizeof( state )); + newnum = MSG_ReadWord( msg ); // entnum + + if( newnum >= clgame.maxEntities ) + Host_Error( "CL_AllocEdict: no free edicts\n" ); + + ent = CL_EDICT_NUM( newnum ); + memset( &ent->prevstate, 0, sizeof( ent->prevstate )); + ent->index = newnum; + + // parse baseline + state.modelindex = MSG_ReadByte( msg ); + state.frame = MSG_ReadByte( msg ); + state.colormap = MSG_ReadByte( msg ); + state.skin = MSG_ReadByte( msg ); + state.origin[0] = MSG_ReadCoord( msg ); + state.angles[0] = MSG_ReadAngle( msg ); + state.origin[1] = MSG_ReadCoord( msg ); + state.angles[1] = MSG_ReadAngle( msg ); + state.origin[2] = MSG_ReadCoord( msg ); + state.angles[2] = MSG_ReadAngle( msg ); + ent->player = CL_IsPlayerIndex( newnum ); + + memcpy( &ent->baseline, &state, sizeof( entity_state_t )); + memcpy( &ent->prevstate, &state, sizeof( entity_state_t )); +} + +/* +=================== +CL_ParseQuakeTempEntity + +=================== +*/ +static void CL_ParseQuakeTempEntity( sizebuf_t *msg ) +{ + int type = MSG_ReadByte( msg ); + + MSG_WriteByte( &msg_demo, type ); + + // TE_LIGHTNING1, TE_LIGHTNING2, TE_LIGHTNING3, TE_BEAM, TE_LIGHTNING4 + if( type == 5 || type == 6 || type == 9 || type == 13 || type == 17 ) + MSG_WriteWord( &msg_demo, MSG_ReadWord( msg )); + + // all temp ents have position at beginning + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + + // TE_LIGHTNING1, TE_LIGHTNING2, TE_LIGHTNING3, TE_BEAM, TE_EXPLOSION3, TE_LIGHTNING4 + if( type == 5 || type == 6 || type == 9 || type == 13 || type == 16 || type == 17 ) + { + // write endpos for beams + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + MSG_WriteCoord( &msg_demo, MSG_ReadCoord( msg )); + } + + // TE_EXPLOSION2 + if( type == 12 ) + { + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); + } + + if( type == 17 ) + MSG_WriteString( &msg_demo, MSG_ReadString( msg )); + + // TE_SMOKE (nehahra) + if( type == 18 ) + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); + + CL_DispatchQuakeMessage( "TempEntity" ); +} + +/* +=================== +CL_ParseQuakeSignon + +very important message +=================== +*/ +static void CL_ParseQuakeSignon( sizebuf_t *msg ) +{ + int i = MSG_ReadByte( msg ); + + if( i == 3 ) cls.signon = SIGNONS - 1; + Msg( "CL_Signon: %d\n", i ); +} + +/* +================== +CL_ParseNehahraShowLMP + +redirect to qwrap->client +================== +*/ +static void CL_ParseNehahraShowLMP( sizebuf_t *msg ) +{ + MSG_WriteString( &msg_demo, MSG_ReadString( msg )); + MSG_WriteString( &msg_demo, MSG_ReadString( msg )); + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); + MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); + CL_DispatchQuakeMessage( "Stats" ); +} + +/* +================== +CL_ParseNehahraHideLMP + +redirect to qwrap->client +================== +*/ +static void CL_ParseNehahraHideLMP( sizebuf_t *msg ) +{ + MSG_WriteString( &msg_demo, MSG_ReadString( msg )); + CL_DispatchQuakeMessage( "Stats" ); +} + +/* +================== +CL_ParseQuakeMessage + +================== +*/ +void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) +{ + int cmd, param1, param2; + size_t bufStart; + const char *str; + + cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame + CL_Parse_Debug( true ); // begin parsing + + // init excise buffer + MSG_Init( &msg_demo, "UserMsg", msg_buf, sizeof( msg_buf )); + + if( normal_message ) + { + // assume no entity/player update this packet + if( cls.state == ca_active ) + { + cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].valid = false; + cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = false; + } + else + { + CL_ResetFrame( &cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK] ); + } + } + + // parse the message + while( 1 ) + { + if( MSG_CheckOverflow( msg )) + { + Host_Error( "CL_ParseServerMessage: overflow!\n" ); + return; + } + + // mark start position + bufStart = MSG_GetNumBytesRead( msg ); + + // end of message (align bits) + if( MSG_GetNumBitsLeft( msg ) < 8 ) + break; + + cmd = MSG_ReadServerCmd( msg ); + + // if the high bit of the command byte is set, it is a fast update + if( FBitSet( cmd, 128 )) + { + CL_ParseQuakeEntityData( msg, cmd & 127 ); + continue; + } + +// Msg( "%s\n", CL_MsgInfo( cmd )); + + // record command for debugging spew on parse problem + CL_Parse_RecordCommand( cmd, bufStart ); + + // other commands + switch( cmd ) + { + case svc_nop: + // this does nothing + break; + case svc_disconnect: + CL_DemoCompleted (); + break; + case svc_updatestat: + CL_ParseQuakeStats( msg ); + break; + case svc_version: + param1 = MSG_ReadLong( msg ); + if( param1 != PROTOCOL_VERSION_QUAKE ) + Host_Error( "Server is protocol %i instead of %i\n", param1, PROTOCOL_VERSION_QUAKE ); + break; + case svc_setview: + CL_ParseViewEntity( msg ); + break; + case svc_sound: + CL_ParseQuakeSound( msg ); + cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_time: + CL_ParseServerTime( msg ); + break; + case svc_print: + Con_Printf( "%s", MSG_ReadString( msg )); + break; + case svc_stufftext: + str = MSG_ReadString( msg ); + Msg( "%s\n", str ); + Cbuf_AddText( str ); + break; + case svc_setangle: + cl.viewangles[0] = MSG_ReadAngle( msg ); + cl.viewangles[1] = MSG_ReadAngle( msg ); + cl.viewangles[2] = MSG_ReadAngle( msg ); + break; + case svc_serverdata: + Cbuf_Execute(); // make sure any stuffed commands are done + CL_ParseQuakeServerInfo( msg ); + break; + case svc_lightstyle: + param1 = MSG_ReadByte( msg ); + str = MSG_ReadString( msg ); + CL_SetLightstyle( param1, str, cl.mtime[0] ); + break; + case svc_updatename: + param1 = MSG_ReadByte( msg ); + Q_strncpy( cl.players[param1].name, MSG_ReadString( msg ), sizeof( cl.players[0].name )); + Q_strncpy( cl.players[param1].model, "player", sizeof( cl.players[0].name )); + break; + case svc_updatefrags: + param1 = MSG_ReadByte( msg ); + param2 = MSG_ReadShort( msg ); + // FIXME: tell the client about scores + break; + case svc_clientdata: + CL_ParseQuakeClientData( msg ); + cl.frames[cl.parsecountmod].graphdata.client += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_stopsound: + param1 = MSG_ReadWord( msg ); + S_StopSound( param1 >> 3, param1 & 7, NULL ); + cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_updatecolors: + param1 = MSG_ReadByte( msg ); + param2 = MSG_ReadByte( msg ); + cl.players[param1].topcolor = param2 & 0xF0; + cl.players[param1].bottomcolor = (param2 & 15) << 4; + break; + case svc_particle: + CL_ParseQuakeParticle( msg ); + break; + case svc_damage: + CL_ParseQuakeDamage( msg ); + break; + case svc_spawnstatic: + CL_ParseQuakeStaticEntity( msg ); + break; + case svc_spawnbinary: + // never used in Quake + break; + case svc_spawnbaseline: + CL_ParseQuakeBaseline( msg ); + break; + case svc_temp_entity: + CL_ParseQuakeTempEntity( msg ); + cl.frames[cl.parsecountmod].graphdata.tentities += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_setpause: + cl.paused = MSG_ReadByte( msg ); + break; + case svc_signonnum: + CL_ParseQuakeSignon( msg ); + break; + case svc_centerprint: + str = MSG_ReadString( msg ); + CL_DispatchUserMessage( "HudText", Q_strlen( str ), (void *)str ); + break; + case svc_killedmonster: + CL_DispatchQuakeMessage( "KillMonster" ); // just an event + break; + case svc_foundsecret: + CL_DispatchQuakeMessage( "FoundSecret" ); // just an event + break; + case svc_spawnstaticsound: + CL_ParseQuakeStaticSound( msg ); + break; + case svc_intermission: + cl.intermission = 1; + break; + case svc_finale: + CL_ParseFinaleCutscene( msg, 2 ); + break; + case svc_cdtrack: + param1 = MSG_ReadByte( msg ); + param1 = bound( 0, param1, MAX_CDTRACKS ); // tracknum + param2 = MSG_ReadByte( msg ); + param2 = bound( 0, param2, MAX_CDTRACKS ); // loopnum + Msg( "main track %d, loop track %d\n", param1, param2 ); + // FIXME: allow cls.forcetrack from demo + S_StartBackgroundTrack( clgame.cdtracks[param1], clgame.cdtracks[param2], 0, false ); + break; + case svc_sellscreen: + Cmd_ExecuteString( "help" ); + break; + case svc_cutscene: + CL_ParseFinaleCutscene( msg, 3 ); + break; + case svc_hidelmp: + CL_ParseNehahraHideLMP( msg ); + break; + case svc_showlmp: + CL_ParseNehahraShowLMP( msg ); + break; + case svc_skybox: + Q_strncpy( clgame.movevars.skyName, MSG_ReadString( msg ), sizeof( clgame.movevars.skyName )); + break; + case svc_skyboxsize: + MSG_ReadCoord( msg ); // obsolete + break; + case svc_fog: + if( MSG_ReadByte( msg )) + { + float fog_settings[4]; + int packed_fog[4]; + + fog_settings[3] = MSG_ReadFloat( msg ); // density + fog_settings[0] = MSG_ReadByte( msg ); // red + fog_settings[1] = MSG_ReadByte( msg ); // green + fog_settings[2] = MSG_ReadByte( msg ); // blue + packed_fog[0] = fog_settings[0] * 255; + packed_fog[1] = fog_settings[1] * 255; + packed_fog[2] = fog_settings[2] * 255; + packed_fog[3] = fog_settings[3] * 255; + clgame.movevars.fog_settings = (packed_fog[1]<<24)|(packed_fog[2]<<16)|(packed_fog[3]<<8)|packed_fog[0]; + } + else + { + clgame.movevars.fog_settings = 0; + } + break; + default: + Host_Error( "CL_ParseServerMessage: Illegible server message\n" ); + break; + } + } + + cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - cls.starting_count; + CL_Parse_Debug( false ); // done + + // now process packet. + CL_ProcessPacket( &cl.frames[cl.parsecountmod] ); + + // add new entities into physic lists + CL_SetSolidEntities(); +} \ No newline at end of file diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 4f067930..d3a2dac5 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -721,6 +721,7 @@ void SCR_Init( void ) // register our commands Cmd_AddCommand( "timerefresh", SCR_TimeRefresh_f, "turn quickly and print rendering statistcs" ); Cmd_AddCommand( "skyname", CL_SetSky_f, "set new skybox by basename" ); + Cmd_AddCommand( "loadsky", CL_SetSky_f, "set new skybox by basename" ); Cmd_AddCommand( "viewpos", SCR_Viewpos_f, "prints current player origin" ); Cmd_AddCommand( "sizeup", SCR_SizeUp_f, "screen size up to 10 points" ); Cmd_AddCommand( "sizedown", SCR_SizeDown_f, "screen size down to 10 points" ); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 1398660c..d2ad1459 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -151,7 +151,7 @@ void CL_AddClientResources( void ) int i; // don't request resources from localhost or in quake-compatibility mode - if( cl.maxclients <= 1 || FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( cl.maxclients <= 1 || CL_IsQuakeCompatible( )) return; // check sprites first @@ -2808,7 +2808,7 @@ void CL_AddEntityEffects( cl_entity_t *ent ) if( FBitSet( ent->curstate.effects, EF_DIMLIGHT )) { - if( ent->player && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( ent->player && !CL_IsQuakeCompatible( )) { CL_UpdateFlashlight( ent ); } diff --git a/engine/client/cl_video.c b/engine/client/cl_video.c index 55ef8975..d2861158 100644 --- a/engine/client/cl_video.c +++ b/engine/client/cl_video.c @@ -48,6 +48,7 @@ qboolean SCR_NextMovie( void ) { S_StopAllSounds( true ); SCR_StopCinematic(); + CL_CheckStartupDemos(); return false; // don't play movies } @@ -56,6 +57,7 @@ qboolean SCR_NextMovie( void ) S_StopAllSounds( true ); SCR_StopCinematic(); cls.movienum = -1; + CL_CheckStartupDemos(); return false; } @@ -90,6 +92,7 @@ void SCR_CheckStartupVids( void ) { // don't run movies where we in developer-mode cls.movienum = -1; + CL_CheckStartupDemos(); return; } diff --git a/engine/client/client.h b/engine/client/client.h index 446401da..df9f30a4 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -53,6 +53,13 @@ GNU General Public License for more details. typedef int sound_t; +typedef enum +{ + DEMO_INACTIVE = 0, + DEMO_XASH3D, + DEMO_QUAKE1 +} demo_mode; + //============================================================================= typedef struct netbandwithgraph_s { @@ -593,6 +600,7 @@ typedef struct float packet_loss; double packet_loss_recalc_time; + int starting_count; // message num readed bits float nextcmdtime; // when can we send the next command packet? int lastoutgoingcommand; // sequence number of last outgoing command @@ -601,6 +609,7 @@ typedef struct int td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start double td_starttime; // realtime at second frame of timedemo + int forcetrack; // -1 = use normal cd track // game images int pauseIcon; // draw 'paused' when game in-pause @@ -630,7 +639,8 @@ typedef struct // demo loop control int demonum; // -1 = don't play demos int olddemonum; // restore playing - string demos[MAX_DEMOS]; // when not playing + char demos[MAX_DEMOS][MAX_QPATH]; // when not playing + qboolean demos_pending; // movie playlist int movienum; @@ -738,6 +748,15 @@ void CL_RemoveFromResourceList( resource_t *pResource ); void CL_MoveToOnHandList( resource_t *pResource ); void CL_ClearResourceLists( void ); +// +// cl_debug.c +// +void CL_Parse_Debug( qboolean enable ); +void CL_Parse_RecordCommand( int cmd, int startoffset ); +void CL_ResetFrame( frame_t *frame ); +void CL_WriteMessageHistory( void ); +const char *CL_MsgInfo( int cmd ); + // // cl_main.c // @@ -765,8 +784,10 @@ void CL_WriteDemoMessage( qboolean startup, int start, sizebuf_t *msg ); void CL_WriteDemoUserMessage( const byte *buffer, size_t size ); qboolean CL_DemoReadMessage( byte *buffer, size_t *length ); void CL_DemoInterpolateAngles( void ); +void CL_CheckStartupDemos( void ); void CL_WriteDemoJumpTime( void ); void CL_CloseDemoHeader( void ); +void CL_DemoCompleted( void ); void CL_StopPlayback( void ); void CL_StopRecord( void ); void CL_PlayDemo_f( void ); @@ -776,7 +797,6 @@ void CL_Demos_f( void ); void CL_DeleteDemo_f( void ); void CL_Record_f( void ); void CL_Stop_f( void ); -void CL_FreeDemo( void ); // // cl_events.c @@ -847,6 +867,8 @@ void CL_StartResourceDownloading( const char *pszMessage, qboolean bCustom ); qboolean CL_DispatchUserMessage( const char *pszName, int iSize, void *pbuf ); qboolean CL_RequestMissingResources( void ); void CL_RegisterResources ( sizebuf_t *msg ); +void CL_ParseViewEntity( sizebuf_t *msg ); +void CL_ParseServerTime( sizebuf_t *msg ); // // cl_scrn.c @@ -908,6 +930,11 @@ void CL_PushPMStates( void ); void CL_PopPMStates( void ); void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient ); +// +// cl_qparse.c +// +void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ); + // // cl_studio.c // @@ -922,7 +949,7 @@ void CL_ResetLatchedVars( cl_entity_t *ent, qboolean full_reset ); qboolean CL_GetEntitySpatialization( struct channel_s *ch ); qboolean CL_GetMovieSpatialization( struct rawchan_s *ch ); void CL_ComputePlayerOrigin( cl_entity_t *clent ); -void CL_UpdateEntityFields( cl_entity_t *ent ); +void CL_ProcessPacket( frame_t *frame ); void CL_MoveThirdpersonCamera( void ); qboolean CL_IsPlayerIndex( int idx ); void CL_SetIdealPitch( void ); diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index 25c8b241..71a94888 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -43,6 +43,25 @@ qboolean R_SpeedsMessage( char *out, size_t size ) return true; } +/* +============== +R_Speeds_Printf + +helper to print into r_speeds message +============== +*/ +void R_Speeds_Printf( const char *msg, ... ) +{ + va_list argptr; + char text[2048]; + + va_start( argptr, msg ); + Q_vsprintf( text, msg, argptr ); + va_end( argptr ); + + Q_strncat( r_speeds_msg, text, sizeof( r_speeds_msg )); +} + /* ============== GL_BackendStartFrame @@ -60,9 +79,17 @@ GL_BackendEndFrame */ void GL_BackendEndFrame( void ) { + mleaf_t *curleaf; + if( r_speeds->value <= 0 || !RI.drawWorld ) return; + if( !RI.viewleaf ) + curleaf = cl.worldmodel->leafs; + else curleaf = RI.viewleaf; + + R_Speeds_Printf( "Renderer: ^1Engine^7\n\n" ); + switch( (int)r_speeds->value ) { case 1: @@ -70,8 +97,8 @@ void GL_BackendEndFrame( void ) r_stats.c_world_polys, r_stats.c_alias_polys, r_stats.c_studio_polys, r_stats.c_sprite_polys ); break; case 2: - Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "visible leafs:\n%3i leafs\ncurrent leaf %3i", - r_stats.c_world_leafs, Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ) - cl.worldmodel->leafs ); + R_Speeds_Printf( "visible leafs:\n%3i leafs\ncurrent leaf %3i\n", r_stats.c_world_leafs, curleaf - cl.worldmodel->leafs ); + R_Speeds_Printf( "ReciusiveWorldNode: %3lf secs\nDrawTextureChains %lf\n", r_stats.t_world_node, r_stats.t_world_draw ); break; case 3: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i alias models drawn\n%3i studio models drawn\n%3i sprites drawn", diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 33858d3a..699cca8c 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -883,7 +883,7 @@ byte *GL_ApplyFilter( const byte *source, int width, int height ) byte *out = (byte *)source; int i; - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ) || glConfig.max_multisamples > 1 ) + if( CL_IsQuakeCompatible() || glConfig.max_multisamples > 1 ) return in; for( i = 0; source && i < width * height; i++, in += 4 ) diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 75e69504..a92b740a 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -229,6 +229,8 @@ typedef struct uint c_particle_count; uint c_client_ents; // entities that moved to client + double t_world_node; + double t_world_draw; } ref_speeds_t; extern ref_speeds_t r_stats; diff --git a/engine/client/gl_refrag.c b/engine/client/gl_refrag.c index 66071ed1..86e4d835 100644 --- a/engine/client/gl_refrag.c +++ b/engine/client/gl_refrag.c @@ -81,7 +81,6 @@ R_SplitEntityOnNode static void R_SplitEntityOnNode( mnode_t *node ) { efrag_t *ef; - mplane_t *splitplane; mleaf_t *leaf; int sides; @@ -100,7 +99,7 @@ static void R_SplitEntityOnNode( mnode_t *node ) ef = clgame.free_efrags; if( !ef ) { - MsgDev( D_ERROR, "too many efrags!\n" ); + Con_Printf( S_ERROR "too many efrags!\n" ); return; // no free fragments... } @@ -120,8 +119,7 @@ static void R_SplitEntityOnNode( mnode_t *node ) } // NODE_MIXED - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE( r_emins, r_emaxs, splitplane ); + sides = BOX_ON_PLANE_SIDE( r_emins, r_emaxs, node->plane ); if( sides == 3 ) { diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index eb3d0dd6..fa9b4380 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -658,7 +658,7 @@ static void R_CheckFog( void ) int i, cnt, count; // quake global fog - if( clgame.movevars.fog_settings != 0 && FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( clgame.movevars.fog_settings != 0 && CL_IsQuakeCompatible( )) { // quake-style global fog RI.fogColor[0] = ((clgame.movevars.fog_settings & 0xFF000000) >> 24) / 255.0f; @@ -1207,6 +1207,8 @@ static int GL_RenderGetParm( int parm, int arg ) return tr.lightmapTextures[arg]; case PARM_SKY_SPHERE: return FBitSet( world.flags, FWORLD_SKYSPHERE ) && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX ); + case PARAM_GAMEPAUSED: + return cl.paused; case PARM_WIDESCREEN: return glState.wideScreen; case PARM_FULLSCREEN: diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index 9868867f..96ef53c6 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -465,9 +465,12 @@ void R_NewMap( void ) if( v_dark->value ) { screenfade_t *sf = &clgame.fade; + float fadetime = 5.0f; client_textmessage_t *title; title = CL_TextMessageGet( "GAMETITLE" ); + if( CL_IsQuakeCompatible( )) + fadetime = 1.0f; if( title ) { @@ -475,7 +478,7 @@ void R_NewMap( void ) sf->fadeEnd = title->holdtime + title->fadeout; sf->fadeReset = title->fadeout; } - else sf->fadeEnd = sf->fadeReset = 5.0f; + else sf->fadeEnd = sf->fadeReset = fadetime; sf->fadeFlags = FFADE_IN; sf->fader = sf->fadeg = sf->fadeb = 0; diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 5437f64c..9feef4cc 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -1232,7 +1232,7 @@ void R_DrawTextureChains( void ) if(( s->flags & SURF_DRAWTURB ) && clgame.movevars.wateralpha < 1.0f ) continue; // draw translucent water later - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ) && FBitSet( s->flags, SURF_TRANSPARENT )) + if( CL_IsQuakeCompatible() && FBitSet( s->flags, SURF_TRANSPARENT )) { draw_alpha_surfaces = true; continue; // draw transparent surfaces later @@ -1412,7 +1412,7 @@ void R_SetRenderMode( cl_entity_t *e ) case kRenderTransAlpha: pglEnable( GL_ALPHA_TEST ); pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( CL_IsQuakeCompatible( )) { pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); pglColor4f( 1.0f, 1.0f, 1.0f, tr.blend ); @@ -1482,7 +1482,7 @@ void R_DrawBrushModel( cl_entity_t *e ) if( rotated ) R_RotateForEntity( e ); else R_TranslateForEntity( e ); - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ) && FBitSet( clmodel->flags, MODEL_TRANSPARENT )) + if( CL_IsQuakeCompatible() && FBitSet( clmodel->flags, MODEL_TRANSPARENT )) e->curstate.rendermode = kRenderTransAlpha; e->visframe = tr.realframecount; // visible @@ -1515,7 +1515,7 @@ void R_DrawBrushModel( cl_entity_t *e ) for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) { - if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !CL_IsQuakeCompatible( )) { if( psurf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES )) continue; @@ -1591,14 +1591,14 @@ void R_RecursiveWorldNode( mnode_t *node, uint clipflags ) mleaf_t *pleaf; int c, side; float dot; - +loc0: if( node->contents == CONTENTS_SOLID ) return; // hit a solid leaf if( node->visframe != tr.visframecount ) return; - if( clipflags && !r_nocull->value ) + if( clipflags && !CVAR_TO_BOOL( r_nocull )) { for( i = 0; i < 6; i++ ) { @@ -1667,7 +1667,8 @@ void R_RecursiveWorldNode( mnode_t *node, uint clipflags ) } // recurse down the back side - R_RecursiveWorldNode( node->children[!side], clipflags ); + node = node->children[!side]; + goto loc0; } /* @@ -1864,6 +1865,8 @@ R_DrawWorld */ void R_DrawWorld( void ) { + double start, end; + // paranoia issues: when gl_renderer is "0" we need have something valid for currententity // to prevent crashing until HeadShield drawing. RI.currententity = clgame.entities; @@ -1884,10 +1887,15 @@ void R_DrawWorld( void ) R_ClearSkyBox (); + start = Sys_DoubleTime(); if( RI.drawOrtho ) R_DrawWorldTopView( cl.worldmodel->nodes, RI.frustum.clipFlags ); else R_RecursiveWorldNode( cl.worldmodel->nodes, RI.frustum.clipFlags ); + end = Sys_DoubleTime(); + r_stats.t_world_node = end - start; + + start = Sys_DoubleTime(); R_DrawTextureChains(); if( !CL_IsDevOverviewMode( )) @@ -1902,6 +1910,9 @@ void R_DrawWorld( void ) R_DrawSkyBox(); } + end = Sys_DoubleTime(); + + r_stats.t_world_draw = end - start; tr.num_draw_decals = 0; skychain = NULL; diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 4a5862d1..58216213 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -844,8 +844,8 @@ void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, cons { if( abs( pcontroller1[i] - pcontroller2[i] ) > 128 ) { - int a = (pcontroller1[j] + 128) % 256; - int b = (pcontroller2[j] + 128) % 256; + int a = (pcontroller1[i] + 128) % 256; + int b = (pcontroller2[i] + 128) % 256; value = (( a * dadt ) + ( b * ( 1.0f - dadt )) - 128) * (360.0f / 256.0f) + pbonecontroller[j].start; } else diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 41c3a9e3..55e7693c 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -26,6 +26,8 @@ GNU General Public License for more details. #define WINDOW_STYLE (WS_OVERLAPPED|WS_BORDER|WS_SYSMENU|WS_CAPTION|WS_VISIBLE) #define WINDOW_EX_STYLE (0) #define WINDOW_NAME "Xash3D Window" // Half-Life +#define FCONTEXT_CORE_PROFILE BIT( 0 ) +#define FCONTEXT_DEBUG_ARB BIT( 1 ) convar_t *gl_extensions; convar_t *gl_texture_anisotropy; @@ -82,7 +84,7 @@ glwstate_t glw_state; static HWND hWndFake; static HDC hDCFake; static HGLRC hGLRCFake; -static qboolean debug_context; +static int context_flags; typedef enum { @@ -531,8 +533,10 @@ static void GL_SetDefaultState( void ) GL_SetDefaultTexState (); if( Sys_CheckParm( "-gldebug" )) - debug_context = true; - else debug_context = false; + SetBits( context_flags, FCONTEXT_DEBUG_ARB ); + + if( Sys_CheckParm( "-glcore" )) + SetBits( context_flags, FCONTEXT_CORE_PROFILE ); // init draw stack tr.draw_list = &tr.draw_stack[0]; @@ -572,7 +576,9 @@ GL_CreateContext */ qboolean GL_CreateContext( void ) { - HGLRC hBaseRC; + HGLRC hBaseRC; + int profile_mask; + int arb_flags; glw_state.extended = false; @@ -582,19 +588,27 @@ qboolean GL_CreateContext( void ) if(!( pwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ))) return GL_DeleteContext(); - if( !debug_context ) // debug bit kill the perfomance + if( !context_flags ) // debug bit kill the perfomance return true; pwglCreateContextAttribsARB = GL_GetProcAddress( "wglCreateContextAttribsARB" ); - if( debug_context && pwglCreateContextAttribsARB != NULL ) + if( FBitSet( context_flags, FCONTEXT_CORE_PROFILE )) + profile_mask = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + else profile_mask = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + + if( FBitSet( context_flags, FCONTEXT_DEBUG_ARB )) + arb_flags = WGL_CONTEXT_DEBUG_BIT_ARB; + else arb_flags = 0; + + if( pwglCreateContextAttribsARB != NULL ) { int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 2, WGL_CONTEXT_MINOR_VERSION_ARB, 0, - WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, -// WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, arb_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, profile_mask, 0 }; @@ -1441,7 +1455,7 @@ qboolean R_Init_OpenGL( void ) if( !opengl_dll.link ) return false; - if( debug_context || CVAR_TO_BOOL( gl_wgl_msaa_samples )) + if( context_flags || CVAR_TO_BOOL( gl_wgl_msaa_samples )) GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS ); return VID_SetMode(); @@ -1583,7 +1597,7 @@ void GL_InitCommands( void ) r_dynamic = Cvar_Get( "r_dynamic", "1", FCVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" ); r_traceglow = Cvar_Get( "r_traceglow", "1", FCVAR_ARCHIVE, "cull flares behind models" ); r_lightmap = Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); - r_drawentities = Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); + r_drawentities = Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT|FCVAR_ARCHIVE, "render entities" ); r_decals = Cvar_Get( "r_decals", "4096", FCVAR_ARCHIVE, "sets the maximum number of decals" ); window_xpos = Cvar_Get( "_window_xpos", "130", FCVAR_RENDERINFO, "window position by horizontal" ); window_ypos = Cvar_Get( "_window_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" ); @@ -1680,7 +1694,7 @@ void GL_InitExtensions( void ) else glConfig.hardware_type = GLHW_GENERIC; // initalize until base opengl functions loaded (old-context) - if( !debug_context && !CVAR_TO_BOOL( gl_wgl_msaa_samples )) + if( !context_flags && !CVAR_TO_BOOL( gl_wgl_msaa_samples )) GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS ); // windows-specific extensions diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 3e37cdfc..49dc6bfd 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1298,7 +1298,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size ) looped = ( channels[i].use_loop && channels[i].sfx->cache->loopStart != -1 ); - if( channels[i].entchannel == CHAN_STATIC && looped && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( channels[i].entchannel == CHAN_STATIC && looped && !CL_IsQuakeCompatible()) continue; // never serialize static looped sounds. It will be restoring in game code if( channels[i].isSentence && channels[i].name[0] ) @@ -2014,6 +2014,23 @@ void S_Play_f( void ) S_StartLocalSound( Cmd_Argv( 1 ), VOL_NORM, false ); } +void S_Play2_f( void ) +{ + int i = 1; + + if( Cmd_Argc() == 1 ) + { + Con_Printf( S_USAGE "play \n" ); + return; + } + + while( i < Cmd_Argc( )) + { + S_StartLocalSound( Cmd_Argv( i ), VOL_NORM, true ); + i++; + } +} + void S_PlayVol_f( void ) { if( Cmd_Argc() == 1 ) @@ -2180,6 +2197,7 @@ qboolean S_Init( void ) s_phs = Cvar_Get( "s_phs", "0", FCVAR_ARCHIVE, "cull sounds by PHS" ); Cmd_AddCommand( "play", S_Play_f, "playing a specified sound file" ); + Cmd_AddCommand( "play2", S_Play2_f, "playing a group of specified sound files" ); // nehahra stuff Cmd_AddCommand( "playvol", S_PlayVol_f, "playing a specified sound file with specified volume" ); Cmd_AddCommand( "stopsound", S_StopSound_f, "stop all sounds" ); Cmd_AddCommand( "music", S_Music_f, "starting a background track" ); diff --git a/engine/common/build.c b/engine/common/build.c index 04ffdb2e..a978fd07 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 4143; + return 4150; #endif } diff --git a/engine/common/common.h b/engine/common/common.h index ae187ff6..49082dc4 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -935,6 +935,7 @@ qboolean CL_IsTimeDemo( void ); qboolean CL_IsPlaybackDemo( void ); qboolean CL_IsBackgroundDemo( void ); qboolean CL_IsBackgroundMap( void ); +qboolean CL_IsQuakeCompatible( void ); qboolean SV_Initialized( void ); qboolean CL_LoadProgs( const char *name ); qboolean SV_GetSaveComment( const char *savename, char *comment ); diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index bc84b86a..2cc940f8 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -2333,18 +2333,18 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) COM_DefaultExtension( dllpath, ".dll" ); // apply ext if forget search = FS_FindFile( dllpath, &index, false ); - if( !search ) + if( !search && !directpath ) { fs_ext_path = false; - if( directpath ) return NULL; // direct paths fails here // trying check also 'bin' folder for indirect paths Q_strncpy( dllpath, dllname, sizeof( dllpath )); search = FS_FindFile( dllpath, &index, false ); - if( !search ) return NULL; // unable to find + if( !search ) return NULL; // unable to find } - // all done, create dll_user_t struct + // NOTE: for libraries we not fail even if search is NULL + // let the OS find library himself hInst = Mem_Calloc( host.mempool, sizeof( dll_user_t )); // save dllname for debug purposes @@ -2355,15 +2355,16 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) hInst->encrypted = FS_CheckForCrypt( dllpath ); - if( index < 0 && !hInst->encrypted ) + if( index < 0 && !hInst->encrypted && search ) { Q_snprintf( hInst->fullPath, sizeof( hInst->fullPath ), "%s%s", search->filename, dllpath ); hInst->custom_loader = false; // we can loading from disk and use normal debugging } else { + // NOTE: if search is NULL let the OS found library himself Q_strncpy( hInst->fullPath, dllpath, sizeof( hInst->fullPath )); - hInst->custom_loader = true; // loading from pack or wad - for release, debug don't working + hInst->custom_loader = (search) ? true : false; } fs_ext_path = false; // always reset direct paths diff --git a/engine/common/input.c b/engine/common/input.c index c4a246cd..7bddbf4c 100644 --- a/engine/common/input.c +++ b/engine/common/input.c @@ -226,6 +226,33 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) } } +/* +=========== +IN_RecalcCenter + +Recalc the center of screen +=========== +*/ +void IN_RecalcCenter( qboolean setpos ) +{ + int width, height; + + if( host.status != HOST_FRAME ) + return; + + width = GetSystemMetrics( SM_CXSCREEN ); + height = GetSystemMetrics( SM_CYSCREEN ); + GetWindowRect( host.hWnd, &window_rect ); + if( window_rect.left < 0 ) window_rect.left = 0; + if( window_rect.top < 0 ) window_rect.top = 0; + if( window_rect.right >= width ) window_rect.right = width - 1; + if( window_rect.bottom >= height - 1 ) window_rect.bottom = height - 1; + + host.window_center_x = (window_rect.right + window_rect.left) / 2; + host.window_center_y = (window_rect.top + window_rect.bottom) / 2; + if( setpos ) SetCursorPos( host.window_center_x, host.window_center_y ); +} + /* =========== IN_ActivateMouse @@ -235,7 +262,6 @@ Called when the window gains focus or changes in some way */ void IN_ActivateMouse( qboolean force ) { - int width, height; static int oldstate; if( !in_mouseinitialized ) @@ -281,18 +307,7 @@ void IN_ActivateMouse( qboolean force ) clgame.dllFuncs.IN_ActivateMouse(); } - width = GetSystemMetrics( SM_CXSCREEN ); - height = GetSystemMetrics( SM_CYSCREEN ); - - GetWindowRect( host.hWnd, &window_rect ); - if( window_rect.left < 0 ) window_rect.left = 0; - if( window_rect.top < 0 ) window_rect.top = 0; - if( window_rect.right >= width ) window_rect.right = width - 1; - if( window_rect.bottom >= height - 1 ) window_rect.bottom = height - 1; - - host.window_center_x = (window_rect.right + window_rect.left) / 2; - host.window_center_y = (window_rect.top + window_rect.bottom) / 2; - SetCursorPos( host.window_center_x, host.window_center_y ); + IN_RecalcCenter( true ); SetCapture( host.hWnd ); ClipCursor( &window_rect ); @@ -498,6 +513,7 @@ LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ) S_Activate( fActivate, host.hWnd ); IN_ActivateMouse( fActivate ); Key_ClearStates(); + IN_RecalcCenter( false ); if( host.status == HOST_FRAME ) { diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 3bf58bc7..016bfb1e 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -1472,6 +1472,12 @@ static void Mod_LoadSubmodels( dbspmodel_t *bmod ) { for( j = 0; j < 3; j++ ) { + // reset empty bounds to prevent error + if( in->mins[j] == 999999.0f ) + in->mins[j] = 0.0f; + if( in->maxs[j] == -999999.0f) + in->maxs[j] = 0.0f; + // spread the mins / maxs by a unit out->mins[j] = in->mins[j] - 1.0f; out->maxs[j] = in->maxs[j] + 1.0f; @@ -2188,7 +2194,7 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) if(( tex->name[0] == '*' && Q_stricmp( tex->name, "*default" )) || tex->name[0] == '!' ) SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED ); - if( !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( !CL_IsQuakeCompatible( )) { if( !Q_strncmp( tex->name, "water", 5 ) || !Q_strnicmp( tex->name, "laser", 5 )) SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED ); diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index e8cfbc48..9488a2c3 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -284,7 +284,7 @@ void MSG_WriteCoord( sizebuf_t *sb, float val ) { // g-cont. we loose precision here but keep old size of coord variable! if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) - MSG_WriteShort( sb, (int)( val * 2.0f )); + MSG_WriteShort( sb, Q_rint( val )); else MSG_WriteShort( sb, (int)( val * 8.0f )); } @@ -598,7 +598,7 @@ float MSG_ReadCoord( sizebuf_t *sb ) { // g-cont. we loose precision here but keep old size of coord variable! if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) - return (float)(MSG_ReadShort( sb ) * ( 1.0f / 2.0f )); + return (float)(MSG_ReadShort( sb )); return (float)(MSG_ReadShort( sb ) * ( 1.0f / 8.0f )); } diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 73c8da19..d1d3550d 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -804,7 +804,7 @@ void Delta_Init( void ) Delta_AddField( "movevars_t", "stepsize", DT_FLOAT|DT_SIGNED, 16, 16.0f, 1.0f ); Delta_AddField( "movevars_t", "maxvelocity", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); - if( host.features & ENGINE_WRITE_LARGE_COORD ) + if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) Delta_AddField( "movevars_t", "zmax", DT_FLOAT|DT_SIGNED, 18, 1.0f, 1.0f ); else Delta_AddField( "movevars_t", "zmax", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); @@ -1703,7 +1703,7 @@ If force is not set, then nothing at all will be generated if the entity is identical, under the assumption that the in-order delta code will catch it. ================== */ -void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean player, float timebase, int baseline ) +void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, int delta_type, float timebase, int baseline ) { delta_info_t *dt = NULL; delta_t *pField; @@ -1757,7 +1757,7 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * { dt = Delta_FindStruct( "custom_entity_state_t" ); } - else if( player ) + else if( delta_type == DELTA_PLAYER ) { dt = Delta_FindStruct( "entity_state_player_t" ); } @@ -1771,8 +1771,17 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * pField = dt->pFields; Assert( pField != NULL ); - // activate fields and call custom encode func - Delta_CustomEncode( dt, from, to ); + if( delta_type == DELTA_STATIC ) + { + // static entities won't to be custom encoded + for( i = 0; i < dt->numFields; i++ ) + dt->pFields[i].bInactive = false; + } + else + { + // activate fields and call custom encode func + Delta_CustomEncode( dt, from, to ); + } // process fields for( i = 0; i < dt->numFields; i++, pField++ ) @@ -1796,7 +1805,7 @@ If the delta removes the entity, entity_state_t->number will be set to MAX_EDICT Can go from either a baseline or a previous packet_entity ================== */ -qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, qboolean player, float timebase ) +qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, int delta_type, float timebase ) { delta_info_t *dt = NULL; delta_t *pField; @@ -1834,7 +1843,12 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state if( baseline_offset != 0 ) { - if( baseline_offset > 0 ) + if( delta_type == DELTA_STATIC ) + { + int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset )); + from = &clgame.static_entities[backup].baseline; + } + else if( baseline_offset > 0 ) { int backup = cls.next_client_entities - baseline_offset; from = &cls.packet_entities[backup % cls.num_client_entities]; @@ -1858,7 +1872,7 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state { dt = Delta_FindStruct( "custom_entity_state_t" ); } - else if( player ) + else if( delta_type == DELTA_PLAYER ) { dt = Delta_FindStruct( "entity_state_player_t" ); } diff --git a/engine/common/net_encode.h b/engine/common/net_encode.h index aa7bf02f..0a8bc599 100644 --- a/engine/common/net_encode.h +++ b/engine/common/net_encode.h @@ -40,8 +40,16 @@ GNU General Public License for more details. enum { CUSTOM_NONE = 0, - CUSTOM_SERVER_ENCODE, // keyword "gamedll" - CUSTOM_CLIENT_ENCODE, // keyword "client" + CUSTOM_SERVER_ENCODE, // known as "gamedll" + CUSTOM_CLIENT_ENCODE, // known as "client" +}; + +// don't change order! +enum +{ + DELTA_ENTITY = 0, + DELTA_PLAYER, + DELTA_STATIC, }; // struct info (filled by engine) @@ -114,8 +122,8 @@ void MSG_WriteClientData( sizebuf_t *msg, struct clientdata_s *from, struct clie void MSG_ReadClientData( sizebuf_t *msg, struct clientdata_s *from, struct clientdata_s *to, float timebase ); void MSG_WriteWeaponData( sizebuf_t *msg, struct weapon_data_s *from, struct weapon_data_s *to, float timebase, int index ); void MSG_ReadWeaponData( sizebuf_t *msg, struct weapon_data_s *from, struct weapon_data_s *to, float timebase ); -void MSG_WriteDeltaEntity( struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, qboolean force, qboolean pl, float tbase, int bl ); -qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, struct entity_state_s *from, struct entity_state_s *to, int num, qboolean player, float timebase ); +void MSG_WriteDeltaEntity( struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, qboolean force, int type, float tbase, int ofs ); +qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, struct entity_state_s *from, struct entity_state_s *to, int num, int type, float timebase ); int Delta_TestBaseline( struct entity_state_s *from, struct entity_state_s *to, qboolean player, float timebase ); #endif//NET_ENCODE_H \ No newline at end of file diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 7c01e303..41fe15ef 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -179,6 +179,67 @@ GNU General Public License for more details. #define FRAGMENT_MAX_SIZE 64000 // maximal fragment size #define FRAGMENT_LOCAL_SIZE FRAGMENT_MAX_SIZE // local connection +// Quake1 Protocol +#define PROTOCOL_VERSION_QUAKE 15 + +// listed only unmatched ops +#define svc_updatestat 3 // [byte] [long] (svc_event) +#define svc_version 4 // [long] server version (svc_changing) +#define svc_updatename 13 // [byte] [string] (svc_updateuserinfo) +#define svc_updatefrags 14 // [byte] [short] (svc_deltatable) +#define svc_stopsound 16 // (svc_resource) +#define svc_updatecolors 17 // [byte] [byte] (svc_pings) +#define svc_damage 19 // (svc_restoresound) +#define svc_spawnbinary 21 // (svc_event_reliable) +#define svc_killedmonster 27 +#define svc_foundsecret 28 +#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten +#define svc_sellscreen 33 // (svc_restore) +// Nehahra added +#define svc_showlmp 35 // [string] slotname [string] lmpfilename [coord] x [coord] y +#define svc_hidelmp 36 // [string] slotname +#define svc_skybox 37 // [string] skyname +#define svc_skyboxsize 50 // [coord] size (default is 4096) +#define svc_fog 51 // [byte] enable + // [float] density [byte] red [byte] green [byte] blue + +// if the high bit of the servercmd is set, the low bits are fast update flags: +#define U_MOREBITS (1<<0) +#define U_ORIGIN1 (1<<1) +#define U_ORIGIN2 (1<<2) +#define U_ORIGIN3 (1<<3) +#define U_ANGLE2 (1<<4) +#define U_NOLERP (1<<5) // don't interpolate movement +#define U_FRAME (1<<6) +#define U_SIGNAL (1<<7) // just differentiates from other updates + +// svc_update can pass all of the fast update bits, plus more +#define U_ANGLE1 (1<<8) +#define U_ANGLE3 (1<<9) +#define U_MODEL (1<<10) +#define U_COLORMAP (1<<11) +#define U_SKIN (1<<12) +#define U_EFFECTS (1<<13) +#define U_LONGENTITY (1<<14) +#define U_TRANS (1<<15) // nehahra + +// clientdata flags +#define SU_VIEWHEIGHT (1<<0) +#define SU_IDEALPITCH (1<<1) +#define SU_PUNCH1 (1<<2) +#define SU_PUNCH2 (1<<3) +#define SU_PUNCH3 (1<<4) +#define SU_VELOCITY1 (1<<5) +#define SU_VELOCITY2 (1<<6) +#define SU_VELOCITY3 (1<<7) +//define SU_AIMENT (1<<8) AVAILABLE BIT +#define SU_ITEMS (1<<9) +#define SU_ONGROUND (1<<10) // no data follows, the bit is it +#define SU_INWATER (1<<11) // no data follows, the bit is it +#define SU_WEAPONFRAME (1<<12) +#define SU_ARMOR (1<<13) +#define SU_WEAPON (1<<14) + extern const char *svc_strings[svc_lastmsg+1]; extern const char *clc_strings[clc_lastmsg+1]; diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index b6c27a5b..66d2780f 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -58,7 +58,7 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) size_t pos = 0; size_t bytesWrite = 0; char out[OUTBUF_SIZE]; - size_t outsize; + size_t outsize, padsize; int ret; wavinfo_t sc; @@ -91,7 +91,8 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) sound.width = 2; // always 16-bit PCM sound.loopstart = -1; sound.size = ( sound.channels * sound.rate * sound.width ) * ( sc.playtime / 1000 ); // in bytes - pos += FRAME_SIZE; // evaluate pos + padsize = sound.size % FRAME_SIZE; + pos += FRAME_SIZE; // evaluate pos if( !sound.size ) { @@ -101,8 +102,9 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) return false; } + // add sentinel make sure we not overrun + sound.wav = (byte *)Mem_Calloc( host.soundpool, sound.size + padsize ); sound.type = WF_PCMDATA; - sound.wav = (byte *)Mem_Malloc( host.soundpool, sound.size ); // decompress mpg into pcm wav format while( bytesWrite < sound.size ) diff --git a/engine/server/server.h b/engine/server/server.h index 61df4704..735021f1 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -113,24 +113,6 @@ typedef struct file_t *file; } server_log_t; -// like as entity_state_t in Quake -typedef struct -{ - char model[MAX_QPATH]; // name of static-entity model for right precache - vec3_t origin; - vec3_t angles; - short sequence; - short frame; - short colormap; - byte skin; // can't set contents! only real skin! - byte body; - float scale; - byte rendermode; - byte renderamt; - color24 rendercolor; - byte renderfx; -} sv_static_entity_t; - typedef struct server_s { sv_state_t state; // precache commands are only valid during load @@ -159,8 +141,6 @@ typedef struct server_s char event_precache[MAX_EVENTS][MAX_QPATH]; byte model_precache_flags[MAX_MODELS]; model_t *models[MAX_MODELS]; - - sv_static_entity_t static_entities[MAX_STATIC_ENTITIES]; int num_static_entities; // run local lightstyles to let SV_LightPoint grab the actual information @@ -386,6 +366,7 @@ typedef struct int next_client_entities; // next client_entity to use entity_state_t *packet_entities; // [num_client_entities] entity_state_t *baselines; // [GI->max_edicts] + entity_state_t *static_entities; // [MAX_STATIC_ENTITIES]; double last_heartbeat; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting @@ -576,6 +557,7 @@ void SV_RequestMissingResources( void ); // sv_frame.c // void SV_InactivateClients( void ); +int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to ); void SV_WriteFrameToClient( sv_client_t *client, sizebuf_t *msg ); void SV_BuildClientFrame( sv_client_t *client ); void SV_SendMessagesToAll( void ); @@ -612,8 +594,8 @@ const char *SV_GetString( string_t iString ); sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only ); int SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); -void SV_CreateStaticEntity( struct sizebuf_s *msg, sv_static_entity_t *ent ); edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname ); +qboolean SV_CreateStaticEntity( struct sizebuf_s *msg, int index ); void SV_SendUserReg( sizebuf_t *msg, sv_user_message_t *user ); edict_t* pfnPEntityOfEntIndex( int iEntIndex ); int pfnIndexOfEdict( const edict_t *pEdict ); @@ -622,6 +604,7 @@ void SV_UpdateBaseVelocity( edict_t *ent ); byte *pfnSetFatPVS( const float *org ); byte *pfnSetFatPAS( const float *org ); int pfnPrecacheModel( const char *s ); +int pfnModelIndex( const char *m ); void pfnRemoveEntity( edict_t* e ); void SV_RestartAmbientSounds( void ); void SV_RestartDecals( void ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 5498c279..692fe5f1 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -800,6 +800,7 @@ void SV_InitHostCommands( void ) Cmd_AddCommand( "map_background", SV_MapBackground_f, "set background map" ); Cmd_AddCommand( "load", SV_Load_f, "load a saved game file" ); Cmd_AddCommand( "loadquick", SV_QuickLoad_f, "load a quick-saved game file" ); + Cmd_AddCommand( "reload", SV_Reload_f, "continue from latest save or restart level" ); } } @@ -818,7 +819,6 @@ void SV_InitOperatorCommands( void ) Cmd_AddCommand( "clientinfo", SV_ClientInfo_f, "print user infostring (player num required)" ); Cmd_AddCommand( "playersonly", SV_PlayersOnly_f, "freezes time, except for players" ); Cmd_AddCommand( "restart", SV_Restart_f, "restarting current level" ); - Cmd_AddCommand( "reload", SV_Reload_f, "continue from latest save or restart level" ); Cmd_AddCommand( "entpatch", SV_EntPatch_f, "write entity patch to allow external editing" ); Cmd_AddCommand( "edict_usage", SV_EdictUsage_f, "show info about edicts usage" ); Cmd_AddCommand( "entity_info", SV_EntityInfo_f, "show more info about edicts" ); @@ -852,7 +852,6 @@ void SV_KillOperatorCommands( void ) Cmd_RemoveCommand( "clientinfo" ); Cmd_RemoveCommand( "playersonly" ); Cmd_RemoveCommand( "restart" ); - Cmd_RemoveCommand( "reload" ); Cmd_RemoveCommand( "entpatch" ); Cmd_RemoveCommand( "edict_usage" ); Cmd_RemoveCommand( "entity_info" ); diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 339b106b..e87e5ab0 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -172,6 +172,13 @@ Encode a client frame onto the network channel ============================================================================= */ +/* +============= +SV_FindBestBaseline + +trying to deltas with previous entities +============= +*/ int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player ) { int bestBitCount; @@ -205,6 +212,43 @@ int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **baseline, return index - bestfound; } +/* +============= +SV_FindBestBaselineForStatic + +trying to deltas with previous static entities +============= +*/ +int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to ) +{ + int bestBitCount; + int i, bitCount; + int bestfound, j; + + bestBitCount = j = Delta_TestBaseline( *baseline, to, false, sv.time ); + bestfound = index; + + // lookup backward for previous 64 states and try to interpret current delta as baseline + for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- ) + { + // don't worry about underflow in circular buffer + entity_state_t *test = &svs.static_entities[i]; + + bitCount = Delta_TestBaseline( test, to, false, sv.time ); + + if( bitCount < bestBitCount ) + { + bestBitCount = bitCount; + bestfound = i; + } + } + + // using delta from previous entity as baseline for current + if( index != bestfound ) + *baseline = &svs.static_entities[bestfound]; + return index - bestfound; +} + /* ============= SV_EmitPacketEntities diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index a49a752b..c15374ad 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -497,39 +497,47 @@ SV_CreateStaticEntity NOTE: static entities only accepted when game is loading ======================= */ -void SV_CreateStaticEntity( sizebuf_t *msg, sv_static_entity_t *ent ) +qboolean SV_CreateStaticEntity( sizebuf_t *msg, int index ) { - int index; + entity_state_t nullstate, *baseline; + entity_state_t *state; + int offset; + + if( index >= ( MAX_STATIC_ENTITIES - 1 )) + { + if( !sv.static_ents_overflow ) + { + Con_Printf( S_WARN "MAX_STATIC_ENTITIES limit exceeded (%d)\n", MAX_STATIC_ENTITIES ); + sv.static_ents_overflow = true; + } + + sv.ignored_static_ents++; // continue overflowed entities + return false; + } // this can happens if serialized map contain too many static entities... - if( MSG_GetNumBytesLeft( msg ) < 35 ) + if( MSG_GetNumBytesLeft( msg ) < 50 ) { sv.ignored_static_ents++; - return; + return false; } - index = SV_ModelIndex( ent->model ); + state = &svs.static_entities[index]; // allocate a new one + memset( &nullstate, 0, sizeof( nullstate )); + baseline = &nullstate; + + // restore modelindex from modelname (already precached) + state->modelindex = pfnModelIndex( STRING( state->messagenum )); + state->entityType = ENTITY_NORMAL; // select delta-encode + state->number = 0; + + // trying to compress with previous delta's + offset = SV_FindBestBaselineForStatic( index, &baseline, state ); MSG_BeginServerCmd( msg, svc_spawnstatic ); - MSG_WriteShort( msg, index ); - MSG_WriteWord( msg, ent->sequence ); - MSG_WriteWord( msg, ent->frame ); - MSG_WriteWord( msg, ent->colormap ); - MSG_WriteByte( msg, ent->skin ); - MSG_WriteByte( msg, ent->body ); - MSG_WriteCoord( msg, ent->scale ); - MSG_WriteVec3Coord( msg, ent->origin ); - MSG_WriteVec3Angles( msg, ent->angles ); - MSG_WriteByte( msg, ent->rendermode ); + MSG_WriteDeltaEntity( baseline, state, msg, true, DELTA_STATIC, sv.time, offset ); - if( ent->rendermode != kRenderNormal ) - { - MSG_WriteByte( msg, ent->renderamt ); - MSG_WriteByte( msg, ent->rendercolor.r ); - MSG_WriteByte( msg, ent->rendercolor.g ); - MSG_WriteByte( msg, ent->rendercolor.b ); - MSG_WriteByte( msg, ent->renderfx ); - } + return true; } /* @@ -541,18 +549,14 @@ Write all the static ents into demo */ void SV_RestartStaticEnts( void ) { - sv_static_entity_t *clent; - int i; + int i; // remove all the static entities on the client R_ClearStaticEntities(); // resend them again for( i = 0; i < sv.num_static_entities; i++ ) - { - clent = &sv.static_entities[i]; - SV_CreateStaticEntity( &sv.reliable_datagram, clent ); - } + SV_CreateStaticEntity( &sv.reliable_datagram, i ); } /* @@ -1835,42 +1839,18 @@ move entity to client */ static void pfnMakeStatic( edict_t *ent ) { - sv_static_entity_t *clent; + entity_state_t *state; if( !SV_IsValidEdict( ent )) return; - if( sv.num_static_entities >= MAX_STATIC_ENTITIES ) - { - if( !sv.static_ents_overflow ) - { - Con_Printf( S_WARN "MAX_STATIC_ENTITIES limit exceeded (%d)\n", MAX_STATIC_ENTITIES ); - sv.static_ents_overflow = true; - } - sv.ignored_static_ents++; // continue overflowed entities - return; - } + // fill the entity state + state = &svs.static_entities[sv.num_static_entities]; // allocate a new one + svgame.dllFuncs.pfnCreateBaseline( false, NUM_FOR_EDICT( ent ), state, ent, 0, vec3_origin, vec3_origin ); + state->messagenum = ent->v.model; // member modelname - clent = &sv.static_entities[sv.num_static_entities++]; - - Q_strncpy( clent->model, STRING( ent->v.model ), sizeof( clent->model )); - VectorCopy( ent->v.origin, clent->origin ); - VectorCopy( ent->v.angles, clent->angles ); - - clent->sequence = ent->v.sequence; - clent->frame = ent->v.frame * 128; - clent->colormap = ent->v.colormap; - clent->skin = ent->v.skin; - clent->body = ent->v.body; - clent->scale = ent->v.scale; - clent->rendermode = ent->v.rendermode; - clent->renderamt = ent->v.renderamt; - clent->rendercolor.r = ent->v.rendercolor[0]; - clent->rendercolor.g = ent->v.rendercolor[1]; - clent->rendercolor.b = ent->v.rendercolor[2]; - clent->renderfx = ent->v.renderfx; - - SV_CreateStaticEntity( &sv.signon, clent ); + if( SV_CreateStaticEntity( &sv.signon, sv.num_static_entities )) + sv.num_static_entities++; // remove at end of the frame SetBits( ent->v.flags, FL_KILLME ); @@ -1986,31 +1966,31 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample if( vol < 0 || vol > 255 ) { - Con_Printf( S_ERROR "SV_StartSound: volume = %i\n", vol ); + Con_Reportf( S_ERROR "SV_StartSound: volume = %i\n", vol ); vol = bound( 0, vol, 255 ); } if( attn < 0.0f || attn > 4.0f ) { - Con_Printf( S_ERROR "SV_StartSound: attenuation %g must be in range 0-4\n", attn ); + Con_Reportf( S_ERROR "SV_StartSound: attenuation %g must be in range 0-4\n", attn ); attn = bound( 0.0f, attn, 4.0f ); } if( chan < 0 || chan > 7 ) { - Con_Printf( S_ERROR "SV_StartSound: channel must be in range 0-7\n" ); + Con_Reportf( S_ERROR "SV_StartSound: channel must be in range 0-7\n" ); chan = bound( 0, chan, 7 ); } if( pitch < 0 || pitch > 255 ) { - Con_Printf( S_ERROR "SV_StartSound: pitch = %i\n", pitch ); + Con_Reportf( S_ERROR "SV_StartSound: pitch = %i\n", pitch ); pitch = bound( 0, pitch, 255 ); } if( !COM_CheckString( sample )) { - Con_Printf( S_ERROR "SV_StartSound: passed NULL sample\n" ); + Con_Reportf( S_ERROR "SV_StartSound: passed NULL sample\n" ); return 0; } @@ -4744,6 +4724,7 @@ void SV_UnloadProgs( void ) Cvar_FullSet( "sv_background", "0", FCVAR_READ_ONLY ); // free entity baselines + Z_Free( svs.static_entities ); Z_Free( svs.baselines ); svs.baselines = NULL; @@ -4872,6 +4853,7 @@ qboolean SV_LoadProgs( const char *name ) svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = svs.maxclients; svgame.edicts = Mem_Calloc( svgame.mempool, sizeof( edict_t ) * GI->max_edicts ); + svs.static_entities = Z_Calloc( sizeof( entity_state_t ) * MAX_STATIC_ENTITIES ); svs.baselines = Z_Calloc( sizeof( entity_state_t ) * GI->max_edicts ); svgame.numEntities = svs.maxclients + 1; // clients + world diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index a76bc358..812960a9 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -378,7 +378,7 @@ void SV_CreateBaseline( void ) { entity_state_t nullstate, *base; int playermodel; - qboolean player; + int delta_type; int entnum; if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) @@ -396,13 +396,13 @@ void SV_CreateBaseline( void ) if( entnum != 0 && entnum <= svs.maxclients ) { - player = true; + delta_type = DELTA_PLAYER; } else { if( !pEdict->v.modelindex ) continue; // invisible - player = false; + delta_type = DELTA_ENTITY; } // take current state as baseline @@ -415,7 +415,7 @@ void SV_CreateBaseline( void ) base->entityType = ENTITY_BEAM; else base->entityType = ENTITY_NORMAL; - svgame.dllFuncs.pfnCreateBaseline( player, entnum, base, pEdict, playermodel, host.player_mins[0], host.player_maxs[0] ); + svgame.dllFuncs.pfnCreateBaseline( delta_type, entnum, base, pEdict, playermodel, host.player_mins[0], host.player_maxs[0] ); sv.last_valid_baseline = entnum; } @@ -434,19 +434,19 @@ void SV_CreateBaseline( void ) if( entnum != 0 && entnum <= svs.maxclients ) { - player = true; + delta_type = DELTA_PLAYER; } else { if( !pEdict->v.modelindex ) continue; // invisible - player = false; + delta_type = DELTA_ENTITY; } // take current state as baseline base = &svs.baselines[entnum]; - MSG_WriteDeltaEntity( &nullstate, base, &sv.signon, true, player, 1.0f, 0 ); + MSG_WriteDeltaEntity( &nullstate, base, &sv.signon, true, delta_type, 1.0f, 0 ); } MSG_WriteUBitLong( &sv.signon, LAST_EDICT, MAX_ENTITY_BITS ); // end of baselines @@ -455,7 +455,7 @@ void SV_CreateBaseline( void ) for( entnum = 0; entnum < sv.num_instanced; entnum++ ) { base = &sv.instanced[entnum].baseline; - MSG_WriteDeltaEntity( &nullstate, base, &sv.signon, true, false, 1.0f, 0 ); + MSG_WriteDeltaEntity( &nullstate, base, &sv.signon, true, DELTA_ENTITY, 1.0f, 0 ); } } @@ -783,6 +783,7 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba MSG_Init( &sv.spec_datagram, "Spectator Datagram", sv.spectator_buf, sizeof( sv.spectator_buf )); // clearing all the baselines + memset( svs.static_entities, 0, sizeof( entity_state_t ) * MAX_STATIC_ENTITIES ); memset( svs.baselines, 0, sizeof( entity_state_t ) * GI->max_edicts ); // make cvars consistant diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index e749e2ad..f70ba8c2 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -158,7 +158,7 @@ void SV_UpdateMovevars( qboolean initialize ) if( sv_zmax.value < 256.0f ) Cvar_SetValue( "sv_zmax", 256.0f ); // clamp it right - if( host.features & ENGINE_WRITE_LARGE_COORD ) + if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) { if( sv_zmax.value > 131070.0f ) Cvar_SetValue( "sv_zmax", 131070.0f ); @@ -599,7 +599,7 @@ void Host_ServerFrame( void ) // if server is not active, do nothing if( !svs.initialized ) return; - if( sv.simulating || sv.state != ss_active ) + if( sv_fps.value != 0.0f && ( sv.simulating || sv.state != ss_active )) sv.time_residual += host.frametime; if( sv_fps.value == 0.0f ) diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 14b1f076..daf4b66d 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -30,7 +30,7 @@ half-life implementation of saverestore system #define SAVEFILE_HEADER (('V'<<24)+('L'<<16)+('A'<<8)+'V') // little-endian "VALV" #define SAVEGAME_HEADER (('V'<<24)+('A'<<16)+('S'<<8)+'J') // little-endian "JSAV" #define SAVEGAME_VERSION 0x0071 // Version 0.71 GoldSrc compatible -#define CLIENT_SAVEGAME_VERSION 0x0065 // Version 0.65 +#define CLIENT_SAVEGAME_VERSION 0x0067 // Version 0.67 #define SAVE_HEAPSIZE 0x400000 // reserve 4Mb for now #define SAVE_HASHSTRINGS 0xFFF // 4095 unique strings @@ -160,19 +160,40 @@ static TYPEDESCRIPTION gDecalEntry[] = static TYPEDESCRIPTION gStaticEntry[] = { - DEFINE_ARRAY( sv_static_entity_t, model, FIELD_CHARACTER, 64 ), - DEFINE_FIELD( sv_static_entity_t, origin, FIELD_VECTOR ), - DEFINE_FIELD( sv_static_entity_t, angles, FIELD_VECTOR ), - DEFINE_FIELD( sv_static_entity_t, sequence, FIELD_SHORT ), - DEFINE_FIELD( sv_static_entity_t, frame, FIELD_SHORT ), - DEFINE_FIELD( sv_static_entity_t, colormap, FIELD_SHORT ), - DEFINE_FIELD( sv_static_entity_t, skin, FIELD_CHARACTER ), - DEFINE_FIELD( sv_static_entity_t, body, FIELD_CHARACTER ), - DEFINE_FIELD( sv_static_entity_t, scale, FIELD_FLOAT ), - DEFINE_FIELD( sv_static_entity_t, rendermode, FIELD_CHARACTER ), - DEFINE_FIELD( sv_static_entity_t, renderamt, FIELD_CHARACTER ), - DEFINE_ARRAY( sv_static_entity_t, rendercolor, FIELD_CHARACTER, sizeof( color24 )), - DEFINE_FIELD( sv_static_entity_t, renderfx, FIELD_CHARACTER ), + DEFINE_FIELD( entity_state_t, messagenum, FIELD_MODELNAME ), // HACKHACK: store model into messagenum + DEFINE_FIELD( entity_state_t, origin, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, angles, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, sequence, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, frame, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, colormap, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, skin, FIELD_SHORT ), + DEFINE_FIELD( entity_state_t, body, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, scale, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, effects, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, framerate, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, mins, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, maxs, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, rendermode, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, renderamt, FIELD_FLOAT ), + DEFINE_ARRAY( entity_state_t, rendercolor, FIELD_CHARACTER, sizeof( color24 )), + DEFINE_FIELD( entity_state_t, renderfx, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, controller, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, blending, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, solid, FIELD_SHORT ), + DEFINE_FIELD( entity_state_t, animtime, FIELD_TIME ), + DEFINE_FIELD( entity_state_t, movetype, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, vuser1, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, vuser2, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, vuser3, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, vuser4, FIELD_VECTOR ), + DEFINE_FIELD( entity_state_t, iuser1, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, iuser2, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, iuser3, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, iuser4, FIELD_INTEGER ), + DEFINE_FIELD( entity_state_t, fuser1, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, fuser2, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, fuser3, FIELD_FLOAT ), + DEFINE_FIELD( entity_state_t, fuser4, FIELD_FLOAT ), }; static TYPEDESCRIPTION gSoundEntry[] = @@ -1145,7 +1166,7 @@ static void SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int // write client entities for( i = 0; i < header.entityCount; i++ ) - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "STATICENTITY", &sv.static_entities[i], gStaticEntry, ARRAYSIZE( gStaticEntry )); + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "STATICENTITY", &svs.static_entities[i], gStaticEntry, ARRAYSIZE( gStaticEntry )); // write sounds for( i = 0; i < header.soundCount; i++ ) @@ -1188,7 +1209,6 @@ static void LoadClientState( SAVERESTOREDATA *pSaveData, const char *level, qboo int i, size, id, version; sv_client_t *cl = svs.clients; char name[MAX_QPATH]; - sv_static_entity_t staticEntry; soundlist_t soundEntry; decallist_t decalEntry; SAVE_CLIENT header; @@ -1248,22 +1268,19 @@ static void LoadClientState( SAVERESTOREDATA *pSaveData, const char *level, qboo // clear old entities if( !adjacent ) { - memset( sv.static_entities, 0, sizeof( sv.static_entities )); + memset( svs.static_entities, 0, sizeof( entity_state_t ) * MAX_STATIC_ENTITIES ); sv.num_static_entities = 0; } // restore client entities for( i = 0; i < header.entityCount; i++ ) { - svgame.dllFuncs.pfnSaveReadFields( pSaveData, "STATICENTITY", &staticEntry, gStaticEntry, ARRAYSIZE( gStaticEntry )); + id = sv.num_static_entities; + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "STATICENTITY", &svs.static_entities[id], gStaticEntry, ARRAYSIZE( gStaticEntry )); if( adjacent ) continue; // static entities won't loading from adjacent levels - if( i >= MAX_STATIC_ENTITIES ) - continue; // silently overflowed - - SV_CreateStaticEntity( &sv.signon, &staticEntry ); - sv.static_entities[i] = staticEntry; - sv.num_static_entities++; + if( SV_CreateStaticEntity( &sv.signon, id )) + sv.num_static_entities++; } // restore sounds @@ -1558,7 +1575,7 @@ static int LoadGameState( char const *level, qboolean changelevel ) if( pent != NULL ) { - if( svgame.dllFuncs.pfnRestore( pent, pSaveData, false ) < 0 ) + if( svgame.dllFuncs.pfnRestore( pent, pSaveData, 0 ) < 0 ) { SetBits( pent->v.flags, FL_KILLME ); pTable->pent = NULL; From 2c0a03704cd171e4cf850a2003b55680d65366b7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 10 Jul 2018 17:29:05 +0300 Subject: [PATCH 027/205] Update mainui. Add --enable-bsp2 switch into wscript --- engine/wscript | 8 ++++++-- mainui | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/engine/wscript b/engine/wscript index 9c7584b8..4be3ff93 100644 --- a/engine/wscript +++ b/engine/wscript @@ -8,8 +8,9 @@ import os top = '.' def options(opt): - # stub - return + opt.add_option( + '--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, + help = 'build engine with BSP2 map support(recommended for Quake, breaks compability!)') def configure(conf): # check for dedicated server build @@ -31,6 +32,9 @@ def configure(conf): conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') + if(conf.options.SUPPORT_BSP2_FORMAT): + conf.env.append_unique('DEFINES', 'SUPPORT_BSP2_FORMAT') + if conf.env.DEST_OS == 'win32': conf.check( lib='USER32' ) conf.check( lib='SHELL32' ) diff --git a/mainui b/mainui index 0c354a1d..89a82727 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 0c354a1d9c6a7443f85ba4c7a8f93141420f281d +Subproject commit 89a827276eecfeff1c9d97f24913f8757693dbce From fdb2055b62b5de453407c13fe5e6ddbee1fb8a6f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 10 Jul 2018 23:03:27 +0300 Subject: [PATCH 028/205] Add RoDir support --- engine/common/common.h | 18 ++-- engine/common/filesystem.c | 185 +++++++++++++++++++++++++++++++++---- engine/common/host.c | 14 +++ 3 files changed, 193 insertions(+), 24 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index 3eae988a..bb9adff9 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -199,10 +199,13 @@ typedef enum #define MAX_STATIC_ENTITIES 3096 // static entities that moved on the client when level is spawn // filesystem flags -#define FS_STATIC_PATH 1 // FS_ClearSearchPath will be ignore this path -#define FS_NOWRITE_PATH 2 // default behavior - last added gamedir set as writedir. This flag disables it -#define FS_GAMEDIR_PATH 4 // just a marker for gamedir path -#define FS_CUSTOM_PATH 8 // custom directory +#define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path +#define FS_NOWRITE_PATH ( 1U << 1 ) // default behavior - last added gamedir set as writedir. This flag disables it +#define FS_GAMEDIR_PATH ( 1U << 2 ) // just a marker for gamedir path +#define FS_CUSTOM_PATH ( 1U << 3 ) // custom directory +#define FS_GAMERODIR_PATH ( 1U << 4 ) // caseinsensitive + +#define FS_GAMEDIRONLY_SEARCH_FLAGS ( FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH ) #define GI SI.GameInfo #define FS_Gamedir() SI.GameInfo->gamefolder @@ -287,6 +290,8 @@ typedef struct gameinfo_s char game_dll_linux[64]; // custom path for game.dll char game_dll_osx[64]; // custom path for game.dll char client_lib[64]; // custom name of client library + + qboolean added; } gameinfo_t; typedef enum @@ -493,6 +498,7 @@ typedef struct host_parm_s qboolean renderinfo_changed; char rootdir[256]; // member root directory + char rodir[256]; // readonly root char gamefolder[MAX_QPATH]; // it's a default gamefolder byte *imagepool; // imagelib mempool byte *soundpool; // soundlib mempool @@ -520,8 +526,8 @@ void FS_Rescan( void ); void FS_Shutdown( void ); void FS_ClearSearchPath( void ); void FS_AllowDirectPaths( qboolean enable ); -void FS_AddGameDirectory( const char *dir, int flags ); -void FS_AddGameHierarchy( const char *dir, int flags ); +void FS_AddGameDirectory( const char *dir, uint flags ); +void FS_AddGameHierarchy( const char *dir, uint flags ); void FS_LoadGameInfo( const char *rootfolder ); void COM_FileBase( const char *in, char *out ); const char *COM_FileExtension( const char *in ); diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 0a6ba6c3..c0d20ed2 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -330,7 +330,9 @@ static const char *FS_FixFileCase( const char *path ) if( !fs_caseinsensitive ) return path; - Q_snprintf( path2, sizeof( path2 ), "./%s", path ); + if( path[0] != '/' ) + Q_snprintf( path2, sizeof( path2 ), "./%s", path ); + else Q_strncpy( path2, path, PATH_MAX ); fname = Q_strrchr( path2, '/' ); @@ -465,9 +467,13 @@ void FS_Path_f( void ) else if( s->wad ) Con_Printf( "%s (%i files)", s->wad->filename, s->wad->numlumps ); else Con_Printf( "%s", s->filename ); - if( FBitSet( s->flags, FS_GAMEDIR_PATH )) - Con_Printf( " ^2gamedir^7\n" ); - else Con_Printf( "\n" ); + if( s->flags & FS_GAMERODIR_PATH ) Con_Printf( " ^2rodir^7" ); + if( s->flags & FS_GAMEDIR_PATH ) Con_Printf( " ^2gamedir^7" ); + if( s->flags & FS_CUSTOM_PATH ) Con_Printf( " ^2custom^7" ); + if( s->flags & FS_NOWRITE_PATH ) Con_Printf( " ^2nowrite^7" ); + if( s->flags & FS_STATIC_PATH ) Con_Printf( " ^2static^7" ); + + Con_Printf( "\n" ); } } @@ -703,7 +709,7 @@ Sets fs_writedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -void FS_AddGameDirectory( const char *dir, int flags ) +void FS_AddGameDirectory( const char *dir, uint flags ) { stringlist_t list; searchpath_t *search; @@ -756,11 +762,51 @@ void FS_AddGameDirectory( const char *dir, int flags ) FS_AddGameHierarchy ================ */ -void FS_AddGameHierarchy( const char *dir, int flags ) +void FS_AddGameHierarchy( const char *dir, uint flags ) { - // Add the common game directory - if( COM_CheckString( dir )) - FS_AddGameDirectory( va( "%s/", dir ), flags ); + int i; + qboolean isGameDir = flags & FS_GAMEDIR_PATH; + + GI->added = true; + + if( !COM_CheckString( dir )) + return; + + // add the common game directory + + // recursive gamedirs + // for example, czeror->czero->cstrike->valve + for( i = 0; i < SI.numgames; i++ ) + { + if( !Q_strnicmp( SI.games[i]->gamefolder, dir, 64 )) + { + MsgDev( D_NOTE, "FS_AddGameHierarchy: %d %s %s\n", i, SI.games[i]->gamefolder, SI.games[i]->basedir ); + if( !SI.games[i]->added && Q_stricmp( SI.games[i]->gamefolder, SI.games[i]->basedir ) ) + { + SI.games[i]->added = true; + FS_AddGameHierarchy( SI.games[i]->basedir, flags & (~FS_GAMEDIR_PATH) ); + } + break; + } + } + + if( host.rodir[0] ) + { + // append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH + uint newFlags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH)); + if( isGameDir ) + newFlags |= FS_GAMERODIR_PATH; + + FS_AllowDirectPaths( true ); + FS_AddGameDirectory( va( "%s/%s/", host.rodir, dir ), newFlags ); + FS_AllowDirectPaths( false ); + } + + if( isGameDir ) + FS_AddGameDirectory( va( "%s/downloaded/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddGameDirectory( va( "%s/", dir ), flags ); + if( isGameDir ) + FS_AddGameDirectory( va( "%s/custom/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); } /* @@ -855,6 +901,25 @@ void FS_Rescan( void ) FS_ClearSearchPath(); +#ifdef __ANDROID__ + char *str; + if( str = getenv("XASH3D_EXTRAS_PAK1") ) + FS_AddPack_Fullpath( str, NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + if( str = getenv("XASH3D_EXTRAS_PAK2") ) + FS_AddPack_Fullpath( str, NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + //FS_AddPack_Fullpath( "/data/data/in.celest.xash3d.hl.test/files/pak.pak", NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); +#elif TARGET_OS_IPHONE + { + FS_AddPack_Fullpath( va( "%sextras.pak", SDL_GetBasePath() ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddPack_Fullpath( va( "%sextras_%s.pak", SDL_GetBasePath(), GI->gamefolder ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + } +#elif defined(__SAILFISH__) + { + FS_AddPack_Fullpath( va( SHAREPATH"/extras.pak" ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddPack_Fullpath( va( SHAREPATH"/%s/extras.pak", GI->gamefolder ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + } +#endif + if( Q_stricmp( GI->basedir, GI->gamefolder )) FS_AddGameHierarchy( GI->basedir, 0 ); if( Q_stricmp( GI->basedir, GI->falldir ) && Q_stricmp( GI->gamefolder, GI->falldir )) @@ -1265,7 +1330,7 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g FS_ConvertGameInfo ================ */ -void FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path ) +static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path ) { gameinfo_t GameInfo; @@ -1275,7 +1340,11 @@ void FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const c { Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path ); FS_WriteGameInfo( gameinfo_path, &GameInfo ); + + return true; } + + return false; } /* @@ -1333,11 +1402,47 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) string liblist_path, gameinfo_path; string default_gameinfo_path; gameinfo_t tmpGameInfo; + qboolean haveUpdate = false; Q_snprintf( default_gameinfo_path, sizeof( default_gameinfo_path ), "%s/gameinfo.txt", fs_basedir ); Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir ); Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir ); + // here goes some RoDir magic... + if( host.rodir[0] ) + { + string filepath_ro, liblist_ro; + fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime; + + Q_snprintf( filepath_ro, sizeof( filepath_ro ), "%s/%s/gameinfo.txt", host.rodir, gamedir ); + Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", host.rodir, gamedir ); + + roLibListTime = FS_SysFileTime( liblist_ro ); + roGameInfoTime = FS_SysFileTime( filepath_ro ); + rwGameInfoTime = FS_SysFileTime( gameinfo_path ); + + if( roLibListTime > rwGameInfoTime ) + { + haveUpdate = FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_ro ); + } + else if( roGameInfoTime > rwGameInfoTime ) + { + char *afile_ro = FS_LoadDirectFile( filepath_ro, NULL ); + + if( afile_ro ) + { + gameinfo_t gi; + + haveUpdate = true; + + FS_InitGameInfo( &gi, gamedir ); + FS_ParseGenericGameInfo( &gi, afile_ro, true ); + FS_WriteGameInfo( gameinfo_path, &gi ); + Mem_Free( afile_ro ); + } + } + } + // if user change liblist.gam update the gameinfo.txt if( FS_FileTime( liblist_path, false ) > FS_FileTime( gameinfo_path, false )) FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path ); @@ -1443,12 +1548,24 @@ void FS_Init( void ) #ifndef _WIN32 if( Sys_CheckParm( "-casesensitive" ) ) fs_caseinsensitive = false; + + if( !fs_caseinsensitive ) + { + if( host.rodir[0] && !Q_strcmp( host.rodir, host.rootdir ) ) + { + Sys_Error( "RoDir and default rootdir can't point to same directory!" ); + } + } + else #endif + { + if( host.rodir[0] && !Q_stricmp( host.rodir, host.rootdir ) ) + { + Sys_Error( "RoDir and default rootdir can't point to same directory!" ); + } + } // ignore commandlineoption "-game" for other stuff - stringlistinit( &dirs ); - listdirectory( &dirs, "./", false ); - stringlistsort( &dirs ); SI.numgames = 0; Q_strncpy( fs_basedir, SI.basedirName, sizeof( fs_basedir )); // default dir @@ -1468,7 +1585,32 @@ void FS_Init( void ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // default dir } + // add readonly directories first + if( host.rodir[0] ) + { + stringlistinit( &dirs ); + listdirectory( &dirs, host.rodir, false ); + stringlistsort( &dirs ); + + for( i = 0; i < dirs.numstrings; i++ ) + { + if( !FS_SysFolderExists( dirs.strings[i] ) || ( !Q_stricmp( dirs.strings[i], ".." ) && !fs_ext_path )) + continue; + + // magic here is that dirs.strings don't contain full path + // so code below checks and creates folders in current directory(host.rootdir) + if( !FS_SysFolderExists( dirs.strings[i] ) ) + FS_CreatePath( dirs.strings[i] ); + } + + stringlistfreecontents( &dirs ); + } + // validate directories + stringlistinit( &dirs ); + listdirectory( &dirs, "./", false ); + stringlistsort( &dirs ); + for( i = 0; i < dirs.numstrings; i++ ) { if( !Q_stricmp( fs_basedir, dirs.strings[i] )) @@ -1749,7 +1891,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir // search through the path, one element at a time for( search = fs_searchpaths; search; search = search->next ) { - if( gamedironly & !FBitSet( search->flags, FS_GAMEDIR_PATH )) + if( gamedironly & !FBitSet( search->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; // is the element a pak file? @@ -2738,7 +2880,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) // search through the path, one element at a time for( searchpath = fs_searchpaths; searchpath; searchpath = searchpath->next ) { - if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIR_PATH )) + if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; // is the element a pak file? @@ -3221,17 +3363,24 @@ open the wad for reading & writing wfile_t *W_Open( const char *filename, int *error ) { wfile_t *wad = (wfile_t *)Mem_Calloc( fs_mempool, sizeof( wfile_t )); + const char *basename; int i, lumpcount; dlumpinfo_t *srclumps; size_t lat_size; dwadinfo_t header; // NOTE: FS_Open is load wad file from the first pak in the list (while fs_ext_path is false) - if( fs_ext_path ) wad->handle = FS_Open( filename, "rb", false ); - else wad->handle = FS_Open( COM_FileWithoutPath( filename ), "rb", false ); + if( fs_ext_path ) basename = filename; + else basename = COM_FileWithoutPath( filename ); + + wad->handle = FS_Open( basename, "rb", false ); + + // HACKHACK: try to open WAD by full path for RoDir, when searchpaths are not ready + if( host.rodir[0] && fs_ext_path && wad->handle == NULL ) + wad->handle = FS_SysOpen( filename, "rb" ); if( wad->handle == NULL ) - { + { MsgDev( D_ERROR, "W_Open: couldn't open %s\n", filename ); if( error ) *error = WAD_LOAD_COULDNT_OPEN; W_Close( wad ); diff --git a/engine/common/host.c b/engine/common/host.c index 8896f7d5..70d68046 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -742,6 +742,20 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( host.rootdir[Q_strlen( host.rootdir ) - 1] == '/' ) host.rootdir[Q_strlen( host.rootdir ) - 1] = 0; + // get readonly root. The order is: check for arg, then env. + // if still not got it, rodir is disabled. + host.rodir[0] = 0; + if( !Sys_GetParmFromCmdLine( "-rodir", host.rodir )) + { + char *roDir; + + if(( roDir = getenv( "XASH3D_RODIR" ))) + Q_strncpy( host.rodir, roDir, sizeof( host.rodir )); + } + + if( host.rodir[0] && host.rodir[Q_strlen( host.rodir ) - 1] == '/' ) + host.rodir[Q_strlen( host.rodir ) - 1] = 0; + host.enabledll = !Sys_CheckParm( "-nodll" ); #ifdef DLL_LOADER From c8b78b412416eef5c1f1fe908d75abad47b50e08 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 10 Jul 2018 23:05:57 +0300 Subject: [PATCH 029/205] Update mainui --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 89a82727..9b088935 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 89a827276eecfeff1c9d97f24913f8757693dbce +Subproject commit 9b0889354f91b9703a75fcb1233b0afc5c212bc1 From 7601a4cd29f83ad2d4cf9fc3ff622e1eb9a5036a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 12 Jul 2018 23:13:30 +0300 Subject: [PATCH 030/205] Revert 64257ef to not break C++ header compability --- common/net_api.h | 14 +++++++------- common/port.h | 1 + engine/cdll_exp.h | 30 +++++++++--------------------- engine/cdll_int.h | 1 + engine/client/client.h | 11 +++-------- engine/common/common.h | 5 ++--- engine/eiface.h | 39 ++++++++++++++++----------------------- engine/menu_int.h | 2 -- 8 files changed, 39 insertions(+), 64 deletions(-) diff --git a/common/net_api.h b/common/net_api.h index 86d90ba1..00831394 100644 --- a/common/net_api.h +++ b/common/net_api.h @@ -28,6 +28,8 @@ // kill the request hook after receiving the first response #define FNETAPI_MULTIPLE_RESPONSE ( 1<<0 ) +typedef void (*net_api_response_func_t) ( struct net_response_s *response ); + #define NET_SUCCESS ( 0 ) #define NET_ERROR_TIMEOUT ( 1<<0 ) #define NET_ERROR_PROTO_UNSUPPORTED ( 1<<1 ) @@ -58,8 +60,6 @@ typedef struct net_response_s void *response; } net_response_t; -typedef void (*net_api_response_func_t) ( net_response_t *response ); - typedef struct net_status_s { // Connected to remote server? 1 == yes, 0 otherwise @@ -83,15 +83,15 @@ typedef struct net_api_s // APIs void (*InitNetworking)( void ); void (*Status )( struct net_status_s *status ); - void (*SendRequest)( int context, int request, int flags, double timeout, netadr_t *remote_address, net_api_response_func_t response ); + void (*SendRequest)( int context, int request, int flags, double timeout, struct netadr_s *remote_address, net_api_response_func_t response ); void (*CancelRequest)( int context ); void (*CancelAllRequests)( void ); - char *(*AdrToString)( netadr_t *a ); - int ( *CompareAdr)( netadr_t *a, netadr_t *b ); - int ( *StringToAdr)( char *s, netadr_t *a ); + char *(*AdrToString)( struct netadr_s *a ); + int ( *CompareAdr)( struct netadr_s *a, struct netadr_s *b ); + int ( *StringToAdr)( char *s, struct netadr_s *a ); const char *(*ValueForKey)( const char *s, const char *key ); void (*RemoveKey)( char *s, const char *key ); void (*SetValueForKey)( char *s, const char *key, const char *value, int maxsize ); } net_api_t; -#endif//NET_APIH +#endif//NET_APIH \ No newline at end of file diff --git a/common/port.h b/common/port.h index fc3b5b44..79ef55eb 100644 --- a/common/port.h +++ b/common/port.h @@ -76,6 +76,7 @@ GNU General Public License for more details. // Windows-specific #define __cdecl #define __stdcall + #define _inline static inline #define O_BINARY 0 // O_BINARY is Windows extension #define O_TEXT 0 // O_TEXT is Windows extension diff --git a/engine/cdll_exp.h b/engine/cdll_exp.h index a6d200d0..9d72df37 100644 --- a/engine/cdll_exp.h +++ b/engine/cdll_exp.h @@ -15,18 +15,6 @@ GNU General Public License for more details. #ifndef CDLL_EXP_H #define CDLL_EXP_H -typedef struct r_studio_interface_s r_studio_interface_t; -typedef struct engine_studio_api_s engine_studio_api_t; -typedef struct mstudioevent_s mstudioevent_t; -typedef struct local_state_s local_state_t; -typedef struct playermove_s playermove_t; -typedef struct tempent_s tempent_t; -typedef struct physent_s physent_t; -typedef struct pmtrace_s pmtrace_t; -typedef struct usercmd_s usercmd_t; -typedef struct netadr_s netadr_t; - - // NOTE: ordering is important! typedef struct cldll_func_s { @@ -36,15 +24,15 @@ typedef struct cldll_func_s int (*pfnRedraw)( float flTime, int intermission ); int (*pfnUpdateClientData)( client_data_t *cdata, float flTime ); void (*pfnReset)( void ); - void (*pfnPlayerMove)( playermove_t *ppmove, int server ); - void (*pfnPlayerMoveInit)( playermove_t *ppmove ); + void (*pfnPlayerMove)( struct playermove_s *ppmove, int server ); + void (*pfnPlayerMoveInit)( struct playermove_s *ppmove ); char (*pfnPlayerMoveTexture)( char *name ); void (*IN_ActivateMouse)( void ); void (*IN_DeactivateMouse)( void ); void (*IN_MouseEvent)( int mstate ); void (*IN_ClearStates)( void ); void (*IN_Accumulate)( void ); - void (*CL_CreateMove)( float frametime, usercmd_t *cmd, int active ); + void (*CL_CreateMove)( float frametime, struct usercmd_s *cmd, int active ); int (*CL_IsThirdPerson)( void ); void (*CL_CameraOffset)( float *ofs ); // unused void *(*KB_Find)( const char *name ); @@ -54,26 +42,26 @@ typedef struct cldll_func_s void (*pfnCreateEntities)( void ); void (*pfnDrawNormalTriangles)( void ); void (*pfnDrawTransparentTriangles)( void ); - void (*pfnStudioEvent)( const mstudioevent_t *event, const cl_entity_t *entity ); - void (*pfnPostRunCmd)( local_state_t *from, local_state_t *to, usercmd_t *cmd, int runfuncs, double time, unsigned int random_seed ); + void (*pfnStudioEvent)( const struct mstudioevent_s *event, const cl_entity_t *entity ); + void (*pfnPostRunCmd)( struct local_state_s *from, struct local_state_s *to, usercmd_t *cmd, int runfuncs, double time, unsigned int random_seed ); void (*pfnShutdown)( void ); void (*pfnTxferLocalOverrides)( entity_state_t *state, const clientdata_t *client ); void (*pfnProcessPlayerState)( entity_state_t *dst, const entity_state_t *src ); void (*pfnTxferPredictionData)( entity_state_t *ps, const entity_state_t *pps, clientdata_t *pcd, const clientdata_t *ppcd, weapon_data_t *wd, const weapon_data_t *pwd ); void (*pfnDemo_ReadBuffer)( int size, byte *buffer ); - int (*pfnConnectionlessPacket)( const netadr_t *net_from, const char *args, char *buffer, int *size ); + int (*pfnConnectionlessPacket)( const struct netadr_s *net_from, const char *args, char *buffer, int *size ); int (*pfnGetHullBounds)( int hullnumber, float *mins, float *maxs ); void (*pfnFrame)( double time ); int (*pfnKey_Event)( int eventcode, int keynum, const char *pszCurrentBinding ); - void (*pfnTempEntUpdate)( double frametime, double client_time, double cl_gravity, tempent_t **ppTempEntFree, tempent_t **ppTempEntActive, int ( *Callback_AddVisibleEntity )( cl_entity_t *pEntity ), void ( *Callback_TempEntPlaySound )( tempent_t *pTemp, float damp )); + void (*pfnTempEntUpdate)( double frametime, double client_time, double cl_gravity, struct tempent_s **ppTempEntFree, struct tempent_s **ppTempEntActive, int ( *Callback_AddVisibleEntity )( cl_entity_t *pEntity ), void ( *Callback_TempEntPlaySound )( struct tempent_s *pTemp, float damp )); cl_entity_t *(*pfnGetUserEntity)( int index ); void (*pfnVoiceStatus)( int entindex, qboolean bTalking ); void (*pfnDirectorMessage)( int iSize, void *pbuf ); - int (*pfnGetStudioModelInterface)( int version, r_studio_interface_t **ppinterface, engine_studio_api_t *pstudio ); + int (*pfnGetStudioModelInterface)( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio ); void (*pfnChatInputPosition)( int *x, int *y ); // Xash3D extension int (*pfnGetRenderInterface)( int version, render_api_t *renderfuncs, render_interface_t *callback ); - void (*pfnClipMoveToEntity)( physent_t *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, pmtrace_t *tr ); + void (*pfnClipMoveToEntity)( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); // Xash3D FWGS extension int (*pfnTouchEvent)( int type, int fingerID, float x, float y, float dx, float dy ); void (*pfnMoveEvent)( float forwardmove, float sidemove ); diff --git a/engine/cdll_int.h b/engine/cdll_int.h index 3589c9e6..db6d9eb9 100644 --- a/engine/cdll_int.h +++ b/engine/cdll_int.h @@ -212,6 +212,7 @@ typedef struct cl_enginefuncs_s float (*pfnRandomFloat)( float flLow, float flHigh ); int (*pfnRandomLong)( int lLow, int lHigh ); void (*pfnHookEvent)( const char *name, void ( *pfnEvent )( struct event_args_s *args )); + int (*Con_IsVisible) (); const char *(*pfnGetGameDirectory)( void ); struct cvar_s *(*pfnGetCvarPointer)( const char *szName ); diff --git a/engine/client/client.h b/engine/client/client.h index 694c1a7e..442189dd 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -954,13 +954,11 @@ void CL_InitStudioAPI( void ); // // cl_frame.c // -typedef struct channel_s channel_t; -typedef struct rawchan_s rawchan_t; int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ); qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ); void CL_ResetLatchedVars( cl_entity_t *ent, qboolean full_reset ); -qboolean CL_GetEntitySpatialization( channel_t *ch ); -qboolean CL_GetMovieSpatialization( rawchan_t *ch ); +qboolean CL_GetEntitySpatialization( struct channel_s *ch ); +qboolean CL_GetMovieSpatialization( struct rawchan_s *ch ); void CL_ComputePlayerOrigin( cl_entity_t *clent ); void CL_ProcessPacket( frame_t *frame ); void CL_MoveThirdpersonCamera( void ); @@ -981,7 +979,6 @@ void CL_ClearAllRemaps( void ); // // cl_tent.c // -typedef struct particle_s particle_t; int CL_AddEntity( int entityType, cl_entity_t *pEnt ); void CL_WeaponAnim( int iAnim, int body ); void CL_ClearEffects( void ); @@ -991,7 +988,7 @@ void CL_DrawParticlesExternal( const ref_viewpass_t *rvp, qboolean trans_pass, f void CL_FireCustomDecal( int textureIndex, int entityIndex, int modelIndex, float *pos, int flags, float scale ); void CL_DecalShoot( int textureIndex, int entityIndex, int modelIndex, float *pos, int flags ); void CL_PlayerDecal( int playerIndex, int textureIndex, int entityIndex, float *pos ); -void R_FreeDeadParticles( particle_t **ppparticles ); +void R_FreeDeadParticles( struct particle_s **ppparticles ); void CL_AddClientResource( const char *filename, int type ); void CL_AddClientResources( void ); int CL_FxBlend( cl_entity_t *e ); @@ -1114,6 +1111,4 @@ void SCR_RunCinematic( void ); void SCR_StopCinematic( void ); void CL_PlayVideo_f( void ); -extern rgba_t g_color_table[8]; - #endif//CLIENT_H diff --git a/engine/common/common.h b/engine/common/common.h index bb9adff9..f821bb78 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -981,7 +981,6 @@ void Key_EnableTextInput( qboolean enable, qboolean force ); // shared calls typedef struct sv_client_s sv_client_t; typedef struct sizebuf_s sizebuf_t; -typedef struct physent_s physent_t; qboolean CL_IsInGame( void ); qboolean CL_IsInMenu( void ); qboolean CL_IsInConsole( void ); @@ -1036,8 +1035,8 @@ qboolean SV_Initialized( void ); qboolean CL_LoadProgs( const char *name ); int SV_GetSaveComment( const char *savename, char *comment ); qboolean SV_NewGame( const char *mapName, qboolean loadGame ); -void SV_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); -void CL_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); +void SV_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); +void CL_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); void CL_Particle( const vec3_t origin, int color, float life, int zpos, int zvel ); // debug thing void SV_SysError( const char *error_string ); void SV_ShutdownGame( void ); diff --git a/engine/eiface.h b/engine/eiface.h index 2e456814..bd197786 100644 --- a/engine/eiface.h +++ b/engine/eiface.h @@ -234,14 +234,14 @@ typedef struct enginefuncs_s int (*pfnCheckVisibility )( const edict_t *entity, unsigned char *pset ); - void (*pfnDeltaSetField) ( delta_t *pFields, const char *fieldname ); - void (*pfnDeltaUnsetField)( delta_t *pFields, const char *fieldname ); - void (*pfnDeltaAddEncoder)( char *name, void (*conditionalencode)( delta_t *pFields, const unsigned char *from, const unsigned char *to ) ); + void (*pfnDeltaSetField) ( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaUnsetField)( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaAddEncoder)( char *name, void (*conditionalencode)( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) ); int (*pfnGetCurrentPlayer)( void ); int (*pfnCanSkipPlayer)( const edict_t *player ); - int (*pfnDeltaFindField)( delta_t *pFields, const char *fieldname ); - void (*pfnDeltaSetFieldByIndex)( delta_t *pFields, int fieldNumber ); - void (*pfnDeltaUnsetFieldByIndex)( delta_t *pFields, int fieldNumber ); + int (*pfnDeltaFindField)( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaSetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); + void (*pfnDeltaUnsetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); void (*pfnSetGroupMask)( int mask, int op ); int (*pfnCreateInstancedBaseline)( int classname, struct entity_state_s *baseline ); void (*pfnCvar_DirectSet)( struct cvar_s *var, const char *value ); @@ -393,13 +393,6 @@ typedef struct #undef ARRAYSIZE #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) -typedef struct playermove_s playermove_t; -typedef struct clientdata_s clientdata_t; -typedef struct entity_state_s entity_state_t; -typedef struct weapon_data_s weapon_data_t; -typedef struct netadr_t netadr_s; -typedef struct usercmd_s usercmd_t; - typedef struct { // Initialize/shutdown the game (one-time call after loading of game .dll ) @@ -450,22 +443,22 @@ typedef struct // Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. void (*pfnSys_Error)( const char *error_string ); - void (*pfnPM_Move)( playermove_t *ppmove, qboolean server ); - void (*pfnPM_Init)( playermove_t *ppmove ); + void (*pfnPM_Move)( struct playermove_s *ppmove, qboolean server ); + void (*pfnPM_Init)( struct playermove_s *ppmove ); char (*pfnPM_FindTextureType)( char *name ); - void (*pfnSetupVisibility)( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ); - void (*pfnUpdateClientData) ( const edict_t *ent, int sendweapons, clientdata_t *cd ); - int (*pfnAddToFullPack)( entity_state_t *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); - void (*pfnCreateBaseline)( int player, int eindex, entity_state_t *baseline, edict_t *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); + void (*pfnSetupVisibility)( struct edict_s *pViewEntity, struct edict_s *pClient, unsigned char **pvs, unsigned char **pas ); + void (*pfnUpdateClientData) ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); + int (*pfnAddToFullPack)( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); + void (*pfnCreateBaseline)( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); void (*pfnRegisterEncoders)( void ); - int (*pfnGetWeaponData)( edict_t *player, weapon_data_t *info ); + int (*pfnGetWeaponData)( struct edict_s *player, struct weapon_data_s *info ); - void (*pfnCmdStart)( const edict_t *player, const usercmd_t *cmd, unsigned int random_seed ); + void (*pfnCmdStart)( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); void (*pfnCmdEnd)( const edict_t *player ); // Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max // size of the response_buffer, so you must zero it out if you choose not to respond. - int (*pfnConnectionlessPacket )( const netadr_t *net_from, const char *args, char *response_buffer, int *response_buffer_size ); + int (*pfnConnectionlessPacket )( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); // Enumerates player hulls. Returns 0 if the hull number doesn't exist, 1 otherwise int (*pfnGetHullBounds) ( int hullnumber, float *mins, float *maxs ); @@ -475,7 +468,7 @@ typedef struct // One of the pfnForceUnmodified files failed the consistency check for the specified player // Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) - int (*pfnInconsistentFile)( const edict_t *player, const char *filename, char *disconnect_message ); + int (*pfnInconsistentFile)( const struct edict_s *player, const char *filename, char *disconnect_message ); // The game .dll should return 1 if lag compensation should be allowed ( could also just set // the sv_unlag cvar. diff --git a/engine/menu_int.h b/engine/menu_int.h index 6ad0a413..2f7810d6 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -47,8 +47,6 @@ typedef struct ui_globalvars_s char maptitle[64]; // title of active map } ui_globalvars_t; -typedef struct ref_viewpass_s ref_viewpass_t; - typedef struct ui_enginefuncs_s { // image handlers From e123499e2367bc37e9e6a96278e38eb7e9257c6b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 12 Jul 2018 23:22:06 +0300 Subject: [PATCH 031/205] Fix build --- engine/client/cl_frame.c | 2 +- engine/client/client.h | 5 +++++ engine/client/gl_rpart.c | 2 +- engine/common/common.h | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 3a66cd09..2d09ffa1 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -1328,4 +1328,4 @@ void CL_ExtraUpdate( void ) { clgame.dllFuncs.IN_Accumulate(); S_ExtraUpdate(); -} \ No newline at end of file +} diff --git a/engine/client/client.h b/engine/client/client.h index 442189dd..d4d1ca90 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -954,6 +954,8 @@ void CL_InitStudioAPI( void ); // // cl_frame.c // +struct channel_s; +struct rawchan_s; int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ); qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ); void CL_ResetLatchedVars( cl_entity_t *ent, qboolean full_reset ); @@ -979,6 +981,7 @@ void CL_ClearAllRemaps( void ); // // cl_tent.c // +struct particle_s; int CL_AddEntity( int entityType, cl_entity_t *pEnt ); void CL_WeaponAnim( int iAnim, int body ); void CL_ClearEffects( void ); @@ -1111,4 +1114,6 @@ void SCR_RunCinematic( void ); void SCR_StopCinematic( void ); void CL_PlayVideo_f( void ); +extern rgba_t g_color_table[8]; + #endif//CLIENT_H diff --git a/engine/client/gl_rpart.c b/engine/client/gl_rpart.c index b2ba1b5a..b86950a7 100644 --- a/engine/client/gl_rpart.c +++ b/engine/client/gl_rpart.c @@ -1664,4 +1664,4 @@ void CL_ReadPointFile_f( void ) if( count ) Con_Printf( "%i points read\n", count ); else Con_Printf( "map %s has no leaks!\n", clgame.mapname ); -} \ No newline at end of file +} diff --git a/engine/common/common.h b/engine/common/common.h index f821bb78..d2e04c71 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -979,6 +979,7 @@ void Key_EnableTextInput( qboolean enable, qboolean force ); #include "avi/avi.h" // shared calls +struct physent_s; typedef struct sv_client_s sv_client_t; typedef struct sizebuf_s sizebuf_t; qboolean CL_IsInGame( void ); From 1dedf0e20888c1d7dc7e6e90e9679d89e4af409a Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 12:15:05 +0700 Subject: [PATCH 032/205] Fix tab in wscript --- vgui_support/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index 66dcaeae..ed2b8b9c 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -40,7 +40,7 @@ def configure(conf): else: conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib'))] - conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'include'))] + conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'include'))] conf.env.HAVE_VGUI = 1 conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) From 9dadc0de930db3402853e7538b57737e5572e2f8 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 12:28:25 +0700 Subject: [PATCH 033/205] Make vgui build dependence optional as it does not have opensource license --- vgui_support/wscript | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index ed2b8b9c..21f12af4 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -11,12 +11,16 @@ def options(opt): opt.add_option( '--vgui', action = 'store', type='string', dest = 'VGUI_DEV', help = 'path to vgui-dev repo', default='' ) + opt.add_option( + '--no-vgui', action = 'store_true', dest = 'NO_VGUI', + help = 'disable vgui_support', default=False ) # stub return def configure(conf): - if conf.options.DEDICATED: + conf.env.NO_VGUI = conf.options.NO_VGUI + if conf.options.DEDICATED or conf.options.NO_VGUI: return conf.start_msg('Checking for VGUI') @@ -52,7 +56,7 @@ def build(bld): bld.load_envs() bld.env = bld.all_envs[get_subproject_name(bld)] - if bld.env.DEDICATED: + if bld.env.DEDICATED or bld.env.NO_VGUI: return # basic build: dedicated only, no dependencies From 5c738b34028fec7d45f6a90bbb6deebd51b3be8c Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 13:08:48 +0700 Subject: [PATCH 034/205] Apply 4253 update --- common/boneinfo.h | 25 + common/bspfile.h | 2 +- common/com_model.h | 2 +- common/event_api.h | 2 +- common/render_api.h | 13 +- common/wadfile.h | 11 - engine/client/cl_cmds.c | 4 +- engine/client/cl_custom.c | 12 +- engine/client/cl_debug.c | 2 +- engine/client/cl_demo.c | 29 +- engine/client/cl_events.c | 24 +- engine/client/cl_frame.c | 39 +- engine/client/cl_game.c | 51 +- engine/client/cl_gameui.c | 21 +- engine/client/cl_main.c | 22 +- engine/client/cl_parse.c | 41 +- engine/client/cl_pmove.c | 38 +- engine/client/cl_qparse.c | 44 +- engine/client/cl_remap.c | 10 +- engine/client/cl_scrn.c | 16 +- engine/client/cl_tent.c | 31 +- engine/client/cl_video.c | 3 +- engine/client/cl_view.c | 2 +- engine/client/client.h | 1 + engine/client/gl_alias.c | 8 +- engine/client/gl_backend.c | 7 +- engine/client/gl_beams.c | 48 +- engine/client/gl_decals.c | 16 +- engine/client/gl_draw.c | 8 +- engine/client/gl_export.h | 2 + engine/client/gl_image.c | 1042 ++++++++++++---------------- engine/client/gl_local.h | 13 +- engine/client/gl_refrag.c | 12 +- engine/client/gl_rlight.c | 30 +- engine/client/gl_rmain.c | 66 +- engine/client/gl_rmath.c | 8 +- engine/client/gl_rmisc.c | 215 +----- engine/client/gl_rpart.c | 9 +- engine/client/gl_rsurf.c | 48 +- engine/client/gl_sprite.c | 6 +- engine/client/gl_studio.c | 39 +- engine/client/gl_vidnt.c | 75 +- engine/client/gl_warp.c | 8 +- engine/client/s_dsp.c | 18 +- engine/client/s_load.c | 79 ++- engine/client/s_main.c | 69 +- engine/client/s_stream.c | 7 +- engine/client/s_vox.c | 4 +- engine/client/sound.h | 1 + engine/client/vgui/vgui_draw.c | 6 +- engine/client/vgui/vgui_main.h | 1 + engine/client/vgui/vgui_surf.cpp | 24 +- engine/common/avikit.c | 53 +- engine/common/build.c | 6 +- engine/common/cmd.c | 33 +- engine/common/common.c | 6 +- engine/common/common.h | 6 +- engine/common/con_utils.c | 19 +- engine/common/console.c | 14 +- engine/common/cvar.c | 17 +- engine/common/filesystem.c | 103 +-- engine/common/filesystem.h | 8 +- engine/common/host.c | 24 +- engine/common/host_state.c | 20 + engine/common/imagelib/img_dds.c | 13 +- engine/common/imagelib/img_main.c | 41 +- engine/common/imagelib/img_quant.c | 9 +- engine/common/imagelib/img_utils.c | 41 +- engine/common/imagelib/img_wad.c | 17 +- engine/common/infostring.c | 11 +- engine/common/keys.c | 184 ++--- engine/common/library.c | 19 +- engine/common/mathlib.c | 20 + engine/common/mathlib.h | 5 +- engine/common/matrixlib.c | 41 ++ engine/common/mod_bmodel.c | 183 ++--- engine/common/mod_dbghulls.c | 2 +- engine/common/mod_local.h | 2 - engine/common/mod_studio.c | 8 +- engine/common/net_chan.c | 5 +- engine/common/net_encode.c | 34 +- engine/common/netchan.h | 1 + engine/common/protocol.h | 2 +- engine/common/soundlib/snd_main.c | 19 +- engine/common/soundlib/snd_mp3.c | 2 +- engine/common/soundlib/snd_utils.c | 5 +- engine/common/sys_con.c | 15 +- engine/common/sys_win.c | 14 +- engine/common/titles.c | 16 +- engine/common/world.c | 1 - engine/common/world.h | 4 - engine/physint.h | 2 +- engine/server/server.h | 1 + engine/server/sv_client.c | 44 +- engine/server/sv_cmds.c | 3 +- engine/server/sv_custom.c | 6 +- engine/server/sv_frame.c | 20 +- engine/server/sv_game.c | 27 +- engine/server/sv_log.c | 2 +- engine/server/sv_main.c | 19 +- engine/server/sv_move.c | 8 +- engine/server/sv_phys.c | 13 +- engine/server/sv_pmove.c | 8 +- engine/server/sv_save.c | 14 +- engine/server/sv_world.c | 35 +- 105 files changed, 1562 insertions(+), 1977 deletions(-) create mode 100644 common/boneinfo.h diff --git a/common/boneinfo.h b/common/boneinfo.h new file mode 100644 index 00000000..bd58845e --- /dev/null +++ b/common/boneinfo.h @@ -0,0 +1,25 @@ +/* +boneinfo.h - structure that send delta-compressed bones across network +Copyright (C) 2018 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef BONEINFO_H +#define BONEINFO_H + +typedef struct +{ + vec3_t angles; + vec3_t origin; +} boneinfo_t; + +#endif//BONEINFO_H \ No newline at end of file diff --git a/common/bspfile.h b/common/bspfile.h index 201e03dd..91d5c1fd 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -74,7 +74,7 @@ BRUSH MODELS #define MAX_MAP_MARKSURFACES 524288 // can be increased without problems #else #define MAX_MAP_MODELS 768 // embedded models -#define MAX_MAP_ENTSTRING 0x80000 // 512 kB should be enough +#define MAX_MAP_ENTSTRING 0x100000 // 1 Mb should be enough #define MAX_MAP_PLANES 65536 // can be increased without problems #define MAX_MAP_NODES 32767 // because negative shorts are leafs #define MAX_MAP_CLIPNODES 32767 // because negative shorts are contents diff --git a/common/com_model.h b/common/com_model.h index 0a85c084..6642722c 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -367,7 +367,7 @@ typedef struct player_info_s int userid; // User id on server char userinfo[MAX_INFO_STRING]; // User info string char name[MAX_SCOREBOARDNAME]; // Name (extracted from userinfo) - int spectator; // Spectator or not, unused + int spectator; // Spectator or not, unused (frags for quake demo playback) int ping; int packet_loss; diff --git a/common/event_api.h b/common/event_api.h index 8b7ae640..699310d0 100644 --- a/common/event_api.h +++ b/common/event_api.h @@ -55,4 +55,4 @@ typedef struct event_api_s void ( *EV_PopTraceBounds)( void ); } event_api_t; -#endif//EVENT_API_H +#endif//EVENT_API_H \ No newline at end of file diff --git a/common/render_api.h b/common/render_api.h index 246d7512..b6b8f462 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -74,12 +74,13 @@ enum typedef enum { + TF_COLORMAP = 0, // just for tabulate source TF_NEAREST = (1<<0), // disable texfilter TF_KEEP_SOURCE = (1<<1), // some images keep source TF_NOFLIP_TGA = (1<<2), // Steam background completely ignore tga attribute 0x20 TF_EXPAND_SOURCE = (1<<3), // Don't keep source as 8-bit expand to RGBA - TF_TEXTURE_2D_ARRAY = (1<<4), // this is 2D texture array (multi-layers) - TF_TEXTURE_RECTANGLE= (1<<5), // this is GL_TEXTURE_RECTANGLE +// reserved + TF_RECTANGLE = (1<<5), // this is GL_TEXTURE_RECTANGLE TF_CUBEMAP = (1<<6), // it's cubemap texture TF_DEPTHMAP = (1<<7), // custom texture filter used TF_QUAKEPAL = (1<<8), // image has an quake1 palette @@ -92,7 +93,7 @@ typedef enum TF_NORMALMAP = (1<<15), // is a normalmap TF_HAS_ALPHA = (1<<16), // image has alpha (used only for GL_CreateTexture) TF_FORCE_COLOR = (1<<17), // force upload monochrome textures as RGB (detail textures) - TF_TEXTURE_1D = (1<<18), // this is GL_TEXTURE_1D +// reserved TF_BORDER = (1<<19), // zero clamp for projected textures TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D TF_ATLAS_PAGE = (1<<21), // bit who indicate lightmap page or deluxemap page @@ -203,9 +204,9 @@ typedef struct render_api_s void (*GL_TextureTarget)( unsigned int target ); // change texture unit mode without bind texture void (*GL_TexCoordArrayMode)( unsigned int texmode ); void* (*GL_GetProcAddress)( const char *name ); + void (*GL_UpdateTexSize)( int texnum, int width, int height, int depth ); // recalc statistics void (*GL_Reserved0)( void ); // for potential interface expansion without broken compatibility void (*GL_Reserved1)( void ); - void (*GL_Reserved2)( void ); // Misc renderer functions void (*GL_DrawParticles)( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime ); @@ -215,8 +216,8 @@ typedef struct render_api_s struct mstudiotex_s *( *StudioGetTexture )( struct cl_entity_s *e ); const struct ref_overview_s *( *GetOverviewParms )( void ); const char *( *GetFileByIndex )( int fileindex ); - void (*R_Reserved1)( void ); // for potential interface expansion without broken compatibility - void (*R_Reserved2)( void ); + void (*R_Reserved0)( void ); // for potential interface expansion without broken compatibility + void (*R_Reserved1)( void ); // static allocations void *(*pfnMemAlloc)( size_t cb, const char *filename, const int fileline ); diff --git a/common/wadfile.h b/common/wadfile.h index b96b3658..646153b9 100644 --- a/common/wadfile.h +++ b/common/wadfile.h @@ -55,17 +55,6 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps] #define TYP_COLORMAP2 69 // old stuff. build palette from LBM file (not used) #define TYP_QFONT 70 // half-life font (qfont_t) -// dlumpinfo_t->img_type -#define IMG_DIFFUSE 0 // same as default pad1 always equal 0 -#define IMG_ALPHAMASK 1 // alpha-channel that stored separate as luminance texture -#define IMG_NORMALMAP 2 // indexed normalmap -#define IMG_GLOSSMAP 3 // luminance or color specularity map -#define IMG_GLOSSPOWER 4 // gloss power map (each value is a specular pow) -#define IMG_HEIGHTMAP 5 // heightmap (for parallax occlusion mapping or source of normalmap) -#define IMG_LUMA 6 // luma or glow texture with self-illuminated parts -#define IMG_DECAL_ALPHA 7 // it's a decal texture (last color in palette is base color, and other colors his graduations) -#define IMG_DECAL_COLOR 8 // decal without alpha-channel uses base, like 127 127 127 as transparent color - /* ======================================================================== diff --git a/engine/client/cl_cmds.c b/engine/client/cl_cmds.c index 4e62420b..c69a6b5e 100644 --- a/engine/client/cl_cmds.c +++ b/engine/client/cl_cmds.c @@ -152,7 +152,7 @@ qboolean CL_ScreenshotGetName( int lastnum, char *filename ) if( lastnum < 0 || lastnum > 9999 ) { - MsgDev( D_ERROR, "unable to write screenshot\n" ); + Con_Printf( S_ERROR "unable to write screenshot\n" ); return false; } @@ -180,7 +180,7 @@ qboolean CL_SnapshotGetName( int lastnum, char *filename ) if( lastnum < 0 || lastnum > 9999 ) { - MsgDev( D_ERROR, "unable to write snapshot\n" ); + Con_Printf( S_ERROR "unable to write snapshot\n" ); FS_AllowDirectPaths( false ); return false; } diff --git a/engine/client/cl_custom.c b/engine/client/cl_custom.c index 6ea718aa..a152fc25 100644 --- a/engine/client/cl_custom.c +++ b/engine/client/cl_custom.c @@ -44,19 +44,19 @@ qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource ) if( !COM_IsSafeFileToDownload( filepath )) { - MsgDev( D_REPORT, "refusing to download %s\n", filepath ); + Con_Reportf( "refusing to download %s\n", filepath ); return true; } if( !cl_allow_download.value ) { - MsgDev( D_REPORT, "Download refused, cl_allow_download is 0\n" ); + Con_Reportf( "Download refused, cl_allow_download is 0\n" ); return true; } if( cls.state == ca_active && !cl_download_ingame.value ) { - MsgDev( D_REPORT, "In-game download refused...\n" ); + Con_Reportf( "In-game download refused...\n" ); return true; } @@ -66,7 +66,7 @@ qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource ) if( cls.demoplayback ) { - MsgDev( D_WARN, "file %s missing during demo playback.\n", filepath ); + Con_Reportf( S_WARN "file %s missing during demo playback.\n", filepath ); return true; } @@ -81,7 +81,7 @@ void CL_AddToResourceList( resource_t *pResource, resource_t *pList ) { if( pResource->pPrev != NULL || pResource->pNext != NULL ) { - MsgDev( D_ERROR, "Resource already linked\n" ); + Con_Reportf( S_ERROR "Resource already linked\n" ); return; } @@ -112,7 +112,7 @@ void CL_MoveToOnHandList( resource_t *pResource ) { if( !pResource ) { - MsgDev( D_REPORT, "Null resource passed to CL_MoveToOnHandList\n" ); + Con_Reportf( "Null resource passed to CL_MoveToOnHandList\n" ); return; } diff --git a/engine/client/cl_debug.c b/engine/client/cl_debug.c index 4f740a3c..4704b800 100644 --- a/engine/client/cl_debug.c +++ b/engine/client/cl_debug.c @@ -82,7 +82,7 @@ const char *svc_strings[svc_lastmsg+1] = "svc_director", "svc_voiceinit", "svc_voicedata", - "svc_unused54", + "svc_deltapacketbones", "svc_unused55", "svc_resourcelocation", "svc_querycvarvalue", diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 505db0c8..c37cba7e 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -126,7 +126,7 @@ void CL_StartupDemoHeader( void ) if( !cls.demoheader ) { - MsgDev( D_ERROR, "couldn't open temporary header file.\n" ); + Con_DPrintf( S_ERROR "couldn't open temporary header file.\n" ); return; } @@ -359,7 +359,7 @@ void CL_WriteDemoHeader( const char *name ) if( !cls.demofile ) { - MsgDev( D_ERROR, "couldn't open %s.\n", name ); + Con_Printf( S_ERROR "couldn't open %s.\n", name ); return; } @@ -773,14 +773,14 @@ qboolean CL_ReadRawNetworkData( byte *buffer, size_t *length ) if( msglen < 0 ) { - MsgDev( D_ERROR, "Demo message length < 0\n" ); + Con_Reportf( S_ERROR "Demo message length < 0\n" ); CL_DemoCompleted(); return false; } if( msglen > MAX_INIT_MSG ) { - MsgDev( D_ERROR, "Demo message %i > %i\n", msglen, MAX_INIT_MSG ); + Con_Reportf( S_ERROR "Demo message %i > %i\n", msglen, MAX_INIT_MSG ); CL_DemoCompleted(); return false; } @@ -789,7 +789,7 @@ qboolean CL_ReadRawNetworkData( byte *buffer, size_t *length ) { if( FS_Read( cls.demofile, buffer, msglen ) != msglen ) { - MsgDev( D_ERROR, "Error reading demo message data\n" ); + Con_Reportf( S_ERROR "Error reading demo message data\n" ); CL_DemoCompleted(); return false; } @@ -862,14 +862,14 @@ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) if( msglen < 0 ) { - MsgDev( D_ERROR, "Demo message length < 0\n" ); + Con_Reportf( S_ERROR "Demo message length < 0\n" ); CL_DemoCompleted(); return false; } if( msglen > MAX_INIT_MSG ) { - MsgDev( D_ERROR, "Demo message %i > %i\n", msglen, MAX_INIT_MSG ); + Con_Reportf( S_ERROR "Demo message %i > %i\n", msglen, MAX_INIT_MSG ); CL_DemoCompleted(); return false; } @@ -878,7 +878,7 @@ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) { if( FS_Read( cls.demofile, buffer, msglen ) != msglen ) { - MsgDev( D_ERROR, "Error reading demo message data\n" ); + Con_Reportf( S_ERROR "Error reading demo message data\n" ); CL_DemoCompleted(); return false; } @@ -910,7 +910,6 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) if( !cls.demofile ) { - MsgDev( D_ERROR, "tried to read a demo message with no demo file\n" ); CL_DemoCompleted(); return false; } @@ -1454,7 +1453,7 @@ void CL_PlayDemo_f( void ) } else if( !FS_FileExists( filename2, true )) { - MsgDev( D_ERROR, "couldn't open %s\n", filename2 ); + Con_Printf( S_ERROR "couldn't open %s\n", filename2 ); CL_DemoAborted(); return; } @@ -1468,7 +1467,7 @@ void CL_PlayDemo_f( void ) if( demo.header.id != IDEMOHEADER ) { - MsgDev( D_ERROR, "%s is not a demo file\n", demoname ); + Con_Printf( S_ERROR "%s is not a demo file\n", demoname ); CL_DemoAborted(); return; } @@ -1476,10 +1475,10 @@ void CL_PlayDemo_f( void ) if( demo.header.net_protocol != PROTOCOL_VERSION || demo.header.dem_protocol != DEMO_PROTOCOL ) { if( demo.header.dem_protocol != DEMO_PROTOCOL ) - MsgDev( D_ERROR, "playdemo: demo protocol outdated (%i should be %i)\n", demo.header.dem_protocol, DEMO_PROTOCOL ); + Con_Printf( S_ERROR "playdemo: demo protocol outdated (%i should be %i)\n", demo.header.dem_protocol, DEMO_PROTOCOL ); if( demo.header.net_protocol != PROTOCOL_VERSION ) - MsgDev( D_ERROR, "playdemo: net protocol outdated (%i should be %i)\n", demo.header.net_protocol, PROTOCOL_VERSION ); + Con_Printf( S_ERROR "playdemo: net protocol outdated (%i should be %i)\n", demo.header.net_protocol, PROTOCOL_VERSION ); CL_DemoAborted(); return; } @@ -1490,7 +1489,7 @@ void CL_PlayDemo_f( void ) if( demo.directory.numentries < 1 || demo.directory.numentries > 1024 ) { - MsgDev( D_ERROR, "demo had bogus # of directory entries: %i\n", demo.directory.numentries ); + Con_Printf( S_ERROR "demo had bogus # of directory entries: %i\n", demo.directory.numentries ); CL_DemoAborted(); return; } @@ -1559,7 +1558,7 @@ void CL_StartDemos_f( void ) c = Cmd_Argc() - 1; if( c > MAX_DEMOS ) { - MsgDev( D_WARN, "Host_StartDemos: max %i demos in demoloop\n", MAX_DEMOS ); + Con_DPrintf( S_WARN "Host_StartDemos: max %i demos in demoloop\n", MAX_DEMOS ); c = MAX_DEMOS; } diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index c7404dc9..2d9bb4f3 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -157,10 +157,7 @@ void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ) cl_user_event_t *ev; if( lastnum == MAX_EVENTS ) - { - MsgDev( D_ERROR, "CL_RegisterEvent: MAX_EVENTS hit!\n" ); return; - } // clear existing or allocate new one if( !clgame.events[lastnum] ) @@ -197,7 +194,7 @@ qboolean CL_FireEvent( event_info_t *ei, int slot ) if( !ev ) { idx = bound( 1, ei->index, ( MAX_EVENTS - 1 )); - MsgDev( D_ERROR, "CL_FireEvent: %s not precached\n", cl.event_precache[idx] ); + Con_Reportf( S_ERROR "CL_FireEvent: %s not precached\n", cl.event_precache[idx] ); break; } @@ -211,7 +208,7 @@ qboolean CL_FireEvent( event_info_t *ei, int slot ) } name = cl.event_precache[ei->index]; - MsgDev( D_ERROR, "CL_FireEvent: %s not hooked\n", name ); + Con_Reportf( S_ERROR "CL_FireEvent: %s not hooked\n", name ); break; } } @@ -439,10 +436,6 @@ void CL_ParseEvent( sizebuf_t *msg ) if( args.entindex > 0 && args.entindex <= cl.maxclients ) args.angles[PITCH] /= -3.0f; } - else - { - MsgDev( D_WARN, "CL_ParseEvent: Received non-packet entity index 0 for event\n" ); - } } // Place event on queue @@ -462,28 +455,25 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa { event_args_t args; - if( flags & FEV_SERVER ) - { - MsgDev( D_WARN, "CL_PlaybackEvent: event with FEV_SERVER flag!\n" ); + if( FBitSet( flags, FEV_SERVER )) return; - } // first check event for out of bounds if( eventindex < 1 || eventindex > MAX_EVENTS ) { - MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); + Con_DPrintf( S_ERROR "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); return; } // check event for precached if( !CL_EventIndex( cl.event_precache[eventindex] )) { - MsgDev( D_ERROR, "CL_PlaybackEvent: event %i was not precached\n", eventindex ); + Con_DPrintf( S_ERROR "CL_PlaybackEvent: event %i was not precached\n", eventindex ); return; } - flags |= FEV_CLIENT; // it's a client event - flags &= ~(FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL); + SetBits( flags, FEV_CLIENT ); // it's a client event + ClearBits( flags, FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL ); if( delay < 0.0f ) delay = 0.0f; // fixup negative delays memset( &args, 0, sizeof( args )); diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 216b70c5..661059fc 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -270,8 +270,8 @@ void CL_ProcessEntityUpdate( cl_entity_t *ent ) ent->model = CL_ModelHandle( ent->curstate.modelindex ); ent->index = ent->curstate.number; - // g-cont. make sure what it's no broke XashXT physics - COM_NormalizeAngles( ent->curstate.angles ); + if( FBitSet( ent->curstate.entityType, ENTITY_NORMAL )) + COM_NormalizeAngles( ent->curstate.angles ); parametric = CL_ParametricMove( ent ); @@ -407,19 +407,15 @@ int CL_InterpolateModel( cl_entity_t *e ) VectorCopy( e->curstate.origin, e->origin ); VectorCopy( e->curstate.angles, e->angles ); - if( cls.timedemo || !e->model ) + if( cls.timedemo || !e->model || cl.maxclients <= 1 ) return 1; - if( fabs( cl_serverframetime() - cl_clientframetime()) < 0.0001f ) - return 1; // interpolation disabled - if( e->model->type == mod_brush && !cl_bmodelinterp->value ) return 1; if( cl.local.moving && cl.local.onground == e->index ) return 1; -// t = cl.time - cl_serverframetime(); t = cl.time - cl_interp->value; CL_FindInterpolationUpdates( e, t, &ph0, &ph1 ); @@ -635,7 +631,7 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t if(( newnum < 0 ) || ( newnum >= clgame.maxEntities )) { - MsgDev( D_ERROR, "CL_DeltaEntity: invalid newnum: %d\n", newnum ); + Con_DPrintf( S_ERROR "CL_DeltaEntity: invalid newnum: %d\n", newnum ); if( has_update ) MSG_ReadDeltaEntity( msg, old, state, newnum, delta_type, cl.mtime[0] ); return; @@ -734,7 +730,6 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - NUM_PACKET_ENTITIES )) { - MsgDev( D_NOTE, "CL_ParsePacketEntities: delta frame is too old (flush)\n"); Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return playerbytes; @@ -847,7 +842,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) } if( newframe->num_entities != count && newframe->num_entities != 0 ) - MsgDev( D_WARN, "CL_Parse%sPacketEntities: (%i should be %i)\n", delta ? "Delta" : "", newframe->num_entities, count ); + Con_Reportf( S_WARN "CL_Parse%sPacketEntities: (%i should be %i)\n", delta ? "Delta" : "", newframe->num_entities, count ); if( !newframe->valid ) return playerbytes; // frame is not valid but message was parsed @@ -943,7 +938,7 @@ void CL_LinkCustomEntity( cl_entity_t *ent, entity_state_t *state ) ent->curstate.movetype = state->modelindex; // !!! if( ent->model->type != mod_sprite ) - MsgDev( D_WARN, "bad model on beam ( %s )\n", ent->model->name ); + Con_Reportf( S_WARN "bad model on beam ( %s )\n", ent->model->name ); ent->latched.prevsequence = ent->curstate.sequence; VectorCopy( ent->origin, ent->latched.prevorigin ); @@ -1042,6 +1037,7 @@ void CL_LinkPacketEntities( frame_t *frame ) cl_entity_t *ent; entity_state_t *state; qboolean parametric; + qboolean interpolate; int i; for( i = 0; i < frame->num_entities; i++ ) @@ -1060,15 +1056,14 @@ void CL_LinkPacketEntities( frame_t *frame ) if( !ent ) { - MsgDev( D_ERROR, "CL_LinkPacketEntity: bad entity %i\n", state->number ); + Con_Reportf( S_ERROR "CL_LinkPacketEntity: bad entity %i\n", state->number ); continue; } - ent->curstate = *state; - - // XASH SPECIFIC - if( ent->curstate.rendermode == kRenderNormal && ent->curstate.renderfx == kRenderFxNone ) - ent->curstate.renderamt = 255.0f; + // animtime must keep an actual + ent->curstate.animtime = state->animtime; + ent->curstate.frame = state->frame; + interpolate = false; if( !ent->model ) continue; @@ -1096,7 +1091,9 @@ void CL_LinkPacketEntities( frame_t *frame ) #ifdef STUDIO_INTERPOLATION_FIX if( ent->lastmove >= cl.time ) VectorCopy( ent->curstate.origin, ent->latched.prevorigin ); - ent->curstate.movetype = MOVETYPE_STEP; + if( FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) + interpolate = true; + else ent->curstate.movetype = MOVETYPE_STEP; #else if( ent->lastmove >= cl.time ) { @@ -1149,7 +1146,7 @@ void CL_LinkPacketEntities( frame_t *frame ) if( ent->model->type == mod_studio ) { - if( ent->curstate.movetype == MOVETYPE_STEP && FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) + if( interpolate && FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) R_StudioLerpMovement( ent, cl.time, ent->origin, ent->angles ); } } @@ -1167,6 +1164,10 @@ void CL_LinkPacketEntities( frame_t *frame ) ent->curstate.rendercolor.r = ent->curstate.rendercolor.g = ent->curstate.rendercolor.b = 255; } + // XASH SPECIFIC + if( ent->curstate.rendermode == kRenderNormal && ent->curstate.renderfx == kRenderFxNone ) + ent->curstate.renderamt = 255.0f; + if( ent->curstate.aiment != 0 && ent->curstate.movetype != MOVETYPE_COMPOUND ) ent->curstate.movetype = MOVETYPE_FOLLOW; diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index d9bbab68..cc295cba 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -237,7 +237,7 @@ void CL_InitCDAudio( const char *filename ) if( ++c > MAX_CDTRACKS - 1 ) { - MsgDev( D_WARN, "CD_Init: too many tracks %i in %s\n", MAX_CDTRACKS, filename ); + Con_Reportf( S_WARN "CD_Init: too many tracks %i in %s\n", MAX_CDTRACKS, filename ); break; } } @@ -826,14 +826,14 @@ const char *CL_SoundFromIndex( int index ) if( !hSound ) { - MsgDev( D_ERROR, "CL_SoundFromIndex: invalid sound index %i\n", index ); + Con_DPrintf( S_ERROR "CL_SoundFromIndex: invalid sound index %i\n", index ); return NULL; } sfx = S_GetSfxByHandle( hSound ); if( !sfx ) { - MsgDev( D_ERROR, "CL_SoundFromIndex: bad sfx for index %i\n", index ); + Con_DPrintf( S_ERROR "CL_SoundFromIndex: bad sfx for index %i\n", index ); return NULL; } @@ -1059,7 +1059,7 @@ void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize ) for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ ) { // NOTE: no check for DispatchFunc, check only name - if( !Q_strcmp( clgame.msg[i].name, pszName )) + if( !Q_stricmp( clgame.msg[i].name, pszName )) { clgame.msg[i].number = svc_num; clgame.msg[i].size = iSize; @@ -1211,7 +1211,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, } else { - Con_Printf( S_ERROR "%s couldn't load\n", szSpriteName ); + Con_Reportf( S_ERROR "%s couldn't load\n", szSpriteName ); Mod_UnloadSpriteModel( m_pSprite ); return false; } @@ -1253,7 +1253,7 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla if( !COM_CheckString( filename )) { - MsgDev( D_ERROR, "CL_LoadSpriteModel: bad name!\n" ); + Con_Reportf( S_ERROR "CL_LoadSpriteModel: bad name!\n" ); return NULL; } @@ -1685,7 +1685,7 @@ static int pfnHookUserMsg( const char *pszName, pfnUserMsgHook pfn ) for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ ) { // see if already hooked - if( !Q_strcmp( clgame.msg[i].name, pszName )) + if( !Q_stricmp( clgame.msg[i].name, pszName )) return 1; } @@ -1712,7 +1712,7 @@ static int pfnServerCmd( const char *szCmdString ) { string buf; - if( !szCmdString || !szCmdString[0] ) + if( !COM_CheckString( szCmdString )) return 0; // just like the client typed "cmd xxxxx" at the console @@ -1730,11 +1730,20 @@ pfnClientCmd */ static int pfnClientCmd( const char *szCmdString ) { - if( !szCmdString || !szCmdString[0] ) + if( !COM_CheckString( szCmdString )) return 0; - Cbuf_AddText( szCmdString ); - Cbuf_AddText( "\n" ); + if( cls.initialized ) + { + Cbuf_AddText( szCmdString ); + Cbuf_AddText( "\n" ); + } + else + { + // will exec later + Q_strncat( host.deferred_cmd, va( "%s\n", szCmdString ), sizeof( host.deferred_cmd )); + } + return 1; } @@ -1793,12 +1802,8 @@ static void pfnPlaySoundByIndex( int iSound, float volume ) // make sure what we in-bounds iSound = bound( 0, iSound, MAX_SOUNDS ); hSound = cl.sound_index[iSound]; + if( !hSound ) return; - if( !hSound ) - { - MsgDev( D_ERROR, "CL_PlaySoundByIndex: invalid sound handle %i\n", iSound ); - return; - } S_StartSound( NULL, cl.viewentity, CHAN_ITEM, hSound, volume, ATTN_NORM, PITCH_NORM, SND_STOP_LOOPING ); } @@ -2241,7 +2246,7 @@ static void pfnHookEvent( const char *filename, pfnEventHook pfn ) if( !Q_stricmp( name, ev->name ) && ev->func != NULL ) { - MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n", name ); + Con_Reportf( S_WARN "CL_HookEvent: %s already hooked!\n", name ); return; } } @@ -2722,7 +2727,7 @@ pfnServerCmdUnreliable */ int pfnServerCmdUnreliable( char *szCmdString ) { - if( !szCmdString || !szCmdString[0] ) + if( !COM_CheckString( szCmdString )) return 0; MSG_BeginClientCmd( &cls.datagram, clc_stringcmd ); @@ -3327,7 +3332,7 @@ void NetAPI_SendRequest( int context, int request, int flags, double timeout, ne if( !response ) { - MsgDev( D_ERROR, "Net_SendRequest: no callbcak specified for request with context %i!\n", context ); + Con_DPrintf( S_ERROR "Net_SendRequest: no callbcak specified for request with context %i!\n", context ); return; } @@ -3945,7 +3950,7 @@ qboolean CL_LoadProgs( const char *name ) // trying to get single export if(( GetClientAPI = (void *)COM_GetProcAddress( clgame.hInstance, "GetClientAPI" )) != NULL ) { - MsgDev( D_NOTE, "CL_LoadProgs: found single callback export\n" ); + Con_Reportf( "CL_LoadProgs: found single callback export\n" ); // trying to fill interface now GetClientAPI( &clgame.dllFuncs ); @@ -3970,7 +3975,7 @@ qboolean CL_LoadProgs( const char *name ) // functions are cleared before all the extensions are evaluated if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL ) { - MsgDev( D_NOTE, "CL_LoadProgs: failed to get address of %s proc\n", func->name ); + Con_Reportf( "CL_LoadProgs: failed to get address of %s proc\n", func->name ); if( critical_exports ) { @@ -3997,13 +4002,13 @@ qboolean CL_LoadProgs( const char *name ) // functions are cleared before all the extensions are evaluated // NOTE: new exports can be missed without stop the engine if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL ) - MsgDev( D_NOTE, "CL_LoadProgs: failed to get address of %s proc\n", func->name ); + Con_Reportf( "CL_LoadProgs: failed to get address of %s proc\n", func->name ); } if( !clgame.dllFuncs.pfnInitialize( &gEngfuncs, CLDLL_INTERFACE_VERSION )) { COM_FreeLibrary( clgame.hInstance ); - MsgDev( D_NOTE, "CL_LoadProgs: can't init client API\n" ); + Con_Reportf( "CL_LoadProgs: can't init client API\n" ); clgame.hInstance = NULL; return false; } diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index cf9690e8..dce138b9 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -29,6 +29,19 @@ void UI_UpdateMenu( float realtime ) { if( !gameui.hInstance ) return; + // if some deferred cmds is waiting + if( UI_IsVisible() && COM_CheckString( host.deferred_cmd )) + { + Cbuf_AddText( host.deferred_cmd ); + host.deferred_cmd[0] = '\0'; + Cbuf_Execute(); + return; + } + + // don't show menu while level is loaded + if( GameState->nextstate != STATE_RUNFRAME && !GameState->loadGame ) + return; + // menu time (not paused, not clamped) gameui.globals->time = host.realtime; gameui.globals->frametime = host.realframetime; @@ -146,7 +159,7 @@ static void UI_DrawLogo( const char *filename, float x, float y, float width, fl if( FS_FileExists( path, false ) && !fullpath ) { - MsgDev( D_ERROR, "Couldn't load %s from packfile. Please extract it\n", path ); + Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", path ); gameui.drawLogo = false; return; } @@ -365,7 +378,7 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long im if( !szPicName || !*szPicName ) { - MsgDev( D_ERROR, "CL_LoadImage: bad name!\n" ); + Con_Reportf( S_ERROR "CL_LoadImage: bad name!\n" ); return 0; } @@ -1026,7 +1039,7 @@ qboolean UI_LoadProgs( void ) if(( GetMenuAPI = (MENUAPI)COM_GetProcAddress( gameui.hInstance, "GetMenuAPI" )) == NULL ) { COM_FreeLibrary( gameui.hInstance ); - MsgDev( D_NOTE, "UI_LoadProgs: can't init menu API\n" ); + Con_Reportf( "UI_LoadProgs: can't init menu API\n" ); gameui.hInstance = NULL; return false; } @@ -1039,7 +1052,7 @@ qboolean UI_LoadProgs( void ) if( !GetMenuAPI( &gameui.dllFuncs, &gpEngfuncs, gameui.globals )) { COM_FreeLibrary( gameui.hInstance ); - MsgDev( D_NOTE, "UI_LoadProgs: can't init menu API\n" ); + Con_Reportf( "UI_LoadProgs: can't init menu API\n" ); Mem_FreePool( &gameui.mempool ); gameui.hInstance = NULL; return false; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 307eff59..a156a8b4 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1488,8 +1488,10 @@ CL_InternetServers_f */ void CL_InternetServers_f( void ) { + char fullquery[512] = MS_SCAN_REQUEST; + char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1; + int remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST ); netadr_t adr; - char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\"; Con_Printf( "Scanning for servers on the internet area...\n" ); NET_Config( true ); // allow remote @@ -1497,9 +1499,10 @@ void CL_InternetServers_f( void ) if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) ) MsgDev( D_ERROR, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); - Q_strcpy( &fullquery[22], GI->gamedir ); + Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); + Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version - NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 23, fullquery, adr ); + NET_SendPacket( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery, adr ); // now we clearing the vgui request if( clgame.master_request != NULL ) @@ -1620,10 +1623,13 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) CL_FixupColorStringsForInfoString( s, infostring ); if( !COM_CheckString( Info_ValueForKey( infostring, "gamedir" ))) - return; // unsupported proto + { + Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); + return; // unsupported proto + } // more info about servers - Con_Printf( "Server: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); + Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); UI_AddServerToList( from, infostring ); } @@ -1839,7 +1845,6 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) } // if we waiting more than cl_timeout or packet was trashed - Msg( "got testpacket, size mismatched %d should be %d\n", MSG_GetMaxBytes( msg ), cls.max_fragment_size ); cls.connect_time = MAX_HEARTBEAT; return; // just wait for a next responce } @@ -1853,7 +1858,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) if( crcValue == crcValue2 ) { // packet was sucessfully delivered, adjust the fragment size and get challenge - Msg( "CRC %p is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); + Con_DPrintf( "CRC %p is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); Netchan_OutOfBandPrint( NS_CLIENT, from, "getchallenge\n" ); Cvar_SetValue( "cl_dlmax", cls.max_fragment_size ); cls.connect_time = host.realtime; @@ -1870,7 +1875,6 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) return; } - Msg( "got testpacket, CRC mismatched %p should be %p, trying next fragment size %d\n", crcValue2, crcValue, cls.max_fragment_size >> 1 ); // trying the next size of packet cls.connect_time = MAX_HEARTBEAT; } @@ -2618,7 +2622,7 @@ void CL_InitLocal( void ) cl_showfps = Cvar_Get( "cl_showfps", "1", FCVAR_ARCHIVE, "show client fps" ); cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", FCVAR_ARCHIVE, "disable smooth up stair climbing and interpolate position in multiplayer" ); - cl_smoothtime = Cvar_Get( "cl_smoothtime", "0.1", FCVAR_ARCHIVE, "time to smooth up" ); + cl_smoothtime = Cvar_Get( "cl_smoothtime", "0", FCVAR_ARCHIVE, "time to smooth up" ); cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "10", FCVAR_ARCHIVE, "how many additional history commands are sent" ); cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" ); cl_draw_particles = Cvar_Get( "r_drawparticles", "1", FCVAR_CHEAT, "render particles" ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 9e54a943..a3ac5957 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -101,10 +101,7 @@ void CL_ParseSoundPacket( sizebuf_t *msg ) else handle = cl.sound_index[sound]; // see precached sound if( !cl.audio_prepped ) - { - MsgDev( D_WARN, "CL_StartSoundPacket: ignore sound message: too early\n" ); return; // too early - } // g-cont. sound and ambient sound have only difference with channel if( chan == CHAN_STATIC ) @@ -160,7 +157,7 @@ void CL_ParseRestoreSoundPacket( sizebuf_t *msg ) char sentenceName[32]; if( flags & SND_SEQUENCE ) - Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS ); + Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound + MAX_SOUNDS ); else Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); @@ -174,10 +171,7 @@ void CL_ParseRestoreSoundPacket( sizebuf_t *msg ) MSG_ReadBytes( msg, &forcedEnd, sizeof( forcedEnd )); if( !cl.audio_prepped ) - { - MsgDev( D_WARN, "CL_RestoreSoundPacket: ignore sound message: too early\n" ); return; // too early - } S_RestoreSound( pos, entnum, chan, handle, volume, attn, pitch, flags, samplePos, forcedEnd, wordIndex ); } @@ -229,7 +223,7 @@ void CL_ParseSignon( sizebuf_t *msg ) if( i <= cls.signon ) { - MsgDev( D_ERROR, "received signon %i when at %i\n", i, cls.signon ); + Con_Reportf( S_ERROR "received signon %i when at %i\n", i, cls.signon ); CL_Disconnect(); return; } @@ -324,7 +318,7 @@ void CL_ParseStaticEntity( sizebuf_t *msg ) i = clgame.numStatics; if( i >= MAX_STATIC_ENTITIES ) { - Con_Printf( S_ERROR, "MAX_STATIC_ENTITIES limit exceeded!\n" ); + Con_Printf( S_ERROR "MAX_STATIC_ENTITIES limit exceeded!\n" ); return; } @@ -522,7 +516,6 @@ void CL_BatchResourceRequest( qboolean initialize ) if( !COM_IsSafeFileToDownload( p->szFileName )) { CL_RemoveFromResourceList( p ); - MsgDev( D_WARN, "Invalid file type...skipping download of %s\n", p->szFileName ); Mem_Free( p ); break; } @@ -777,16 +770,10 @@ void CL_ParseResourceRequest( sizebuf_t *msg ) nStartIndex = MSG_ReadLong( msg ); if( cl.servercount != arg ) - { - MsgDev( D_ERROR, "request resources from different level\n" ); return; - } if( nStartIndex < 0 && nStartIndex > cl.num_resources ) - { - MsgDev( D_ERROR, "custom resource list request out of range\n" ); return; - } MSG_BeginClientCmd( &sbuf, clc_resourcelist ); MSG_WriteShort( &sbuf, cl.num_resources ); @@ -867,7 +854,7 @@ void CL_ParseServerData( sizebuf_t *msg ) qboolean background; int i; - MsgDev( D_NOTE, "Serverdata packet received.\n" ); + Con_Reportf( "Serverdata packet received.\n" ); cls.timestart = Sys_DoubleTime(); cls.demowaiting = false; // server is changed @@ -1687,7 +1674,7 @@ void CL_ParseResLocation( sizebuf_t *msg ) if( lastSlash && lastSlash[1] == '\0' ) Q_strncpy( cl.downloadUrl, url, sizeof( cl.downloadUrl )); else Q_snprintf( cl.downloadUrl, sizeof( cl.downloadUrl ), "%s/", url ); - MsgDev( D_REPORT, "Using %s as primary download location\n", cl.downloadUrl ); + Con_Reportf( "Using %s as primary download location\n", cl.downloadUrl ); } } @@ -1722,7 +1709,6 @@ void CL_ParseHLTV( sizebuf_t *msg ) SCR_EndLoadingPlaque(); break; default: - MsgDev( D_ERROR, "CL_ParseHLTV: unknown HLTV command.\n" ); break; } } @@ -1989,9 +1975,10 @@ dispatch messages */ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) { - size_t bufStart, playerbytes; - int cmd, param1, param2; - int old_background; + size_t bufStart, playerbytes; + int cmd, param1, param2; + int old_background; + const char *s; cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame CL_Parse_Debug( true ); // begin parsing @@ -2028,8 +2015,6 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) cmd = MSG_ReadServerCmd( msg ); -// Msg( "%s\n", CL_MsgInfo( cmd )); - // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); @@ -2099,7 +2084,13 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) Con_Printf( "%s", MSG_ReadString( msg )); break; case svc_stufftext: - Cbuf_AddText( MSG_ReadString( msg )); + s = MSG_ReadString( msg ); +#ifdef HACKS_RELATED_HLMODS + // dsiable Cry Of Fear antisave protection + if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS ) + break; // too early +#endif + Cbuf_AddText( s ); break; case svc_setangle: CL_ParseSetAngle( msg ); diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 5d706939..dc76da16 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -48,17 +48,10 @@ CL_PushPMStates */ void CL_PushPMStates( void ) { - if( clgame.pushed ) - { - MsgDev( D_ERROR, "PushPMStates: stack overflow\n"); - } - else - { - clgame.oldphyscount = clgame.pmove->numphysent; - clgame.oldviscount = clgame.pmove->numvisent; - clgame.pushed = true; - } - + if( clgame.pushed ) return; + clgame.oldphyscount = clgame.pmove->numphysent; + clgame.oldviscount = clgame.pmove->numvisent; + clgame.pushed = true; } /* @@ -69,16 +62,10 @@ CL_PopPMStates */ void CL_PopPMStates( void ) { - if( clgame.pushed ) - { - clgame.pmove->numphysent = clgame.oldphyscount; - clgame.pmove->numvisent = clgame.oldviscount; - clgame.pushed = false; - } - else - { - MsgDev( D_ERROR, "PopPMStates: stack underflow\n"); - } + if( !clgame.pushed ) return; + clgame.pmove->numphysent = clgame.oldphyscount; + clgame.pmove->numvisent = clgame.oldviscount; + clgame.pushed = false; } /* @@ -794,10 +781,7 @@ static void pfnStuckTouch( int hitent, pmtrace_t *tr ) } if( clgame.pmove->numtouch >= MAX_PHYSENTS ) - { - MsgDev( D_ERROR, "PM_StuckTouch: MAX_TOUCHENTS limit exceeded\n" ); return; - } VectorCopy( clgame.pmove->velocity, tr->deltavelocity ); tr->ent = hitent; @@ -995,7 +979,7 @@ void CL_InitClientMove( void ) for( i = 0; i < MAX_MAP_HULLS; i++ ) { if( clgame.dllFuncs.pfnGetHullBounds( i, host.player_mins[i], host.player_maxs[i] )) - MsgDev( D_NOTE, "CL: hull%i, player_mins: %g %g %g, player_maxs: %g %g %g\n", i, + Con_Reportf( "CL: hull%i, player_mins: %g %g %g, player_maxs: %g %g %g\n", i, host.player_mins[i][0], host.player_mins[i][1], host.player_mins[i][2], host.player_maxs[i][0], host.player_maxs[i][1], host.player_maxs[i][2] ); } @@ -1344,7 +1328,7 @@ void CL_PredictMovement( qboolean repredicting ) cl.local.onground = frame->playerstate[cl.playernum].onground; else cl.local.onground = -1; - if( !repredicting || !cl_lw->value ) + if( !repredicting || !CVAR_TO_BOOL( cl_lw )) cl.local.viewmodel = to->client.viewmodel; cl.local.repredicting = false; cl.local.moving = false; @@ -1374,7 +1358,7 @@ void CL_PredictMovement( qboolean repredicting ) cl.local.waterlevel = to->client.waterlevel; cl.local.usehull = to->playerstate.usehull; - if( !repredicting || !cl_lw->value ) + if( !repredicting || !CVAR_TO_BOOL( cl_lw )) cl.local.viewmodel = to->client.viewmodel; if( FBitSet( to->client.flags, FL_ONGROUND )) diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c index 45c9959f..912a9b70 100644 --- a/engine/client/cl_qparse.c +++ b/engine/client/cl_qparse.c @@ -95,6 +95,19 @@ static int CL_UpdateQuakeStats( sizebuf_t *msg, int statnum, qboolean has_update return value; } +/* +================== +CL_UpdateQuakeGameMode + +redirect to qwrap->client +================== +*/ +static void CL_UpdateQuakeGameMode( int gamemode ) +{ + MSG_WriteByte( &msg_demo, gamemode ); + CL_DispatchQuakeMessage( "GameMode" ); +} + /* ================== CL_ParseQuakeSound @@ -132,10 +145,7 @@ static void CL_ParseQuakeSound( sizebuf_t *msg ) handle = cl.sound_index[sound]; if( !cl.audio_prepped ) - { - Con_Printf( S_WARN "CL_StartSoundPacket: ignore sound message: too early\n" ); return; // too early - } S_StartSound( pos, entnum, channel, handle, volume, attn, PITCH_NORM, flags ); } @@ -171,7 +181,7 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION_QUAKE ); cl.maxclients = MSG_ReadByte( msg ); - gametype = MSG_ReadByte( msg ); // FIXME: tell the client about gametype + gametype = MSG_ReadByte( msg ); clgame.maxEntities = GI->max_edicts; clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS ); clgame.maxModels = MAX_MODELS; @@ -283,6 +293,9 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) cl.video_prepped = false; cl.audio_prepped = false; + // GAME_COOP or GAME_DEATHMATCH + CL_UpdateQuakeGameMode( gametype ); + // now we can start to precache CL_BatchResourceRequest( true ); @@ -420,6 +433,7 @@ void CL_ParseQuakeEntityData( sizebuf_t *msg, int bits ) ent = CL_EDICT_NUM( newnum ); ent->index = newnum; // enumerate entity index ent->player = CL_IsPlayerIndex( newnum ); + state->animtime = cl.mtime[0]; if( ent->curstate.msg_time != cl.mtime[1] ) forcelink = true; // no previous frame to lerp from @@ -596,7 +610,7 @@ static void CL_ParseQuakeStaticEntity( sizebuf_t *msg ) i = clgame.numStatics; if( i >= MAX_STATIC_ENTITIES ) { - Con_Printf( S_ERROR, "CL_ParseStaticEntity: static entities limit exceeded!\n" ); + Con_Printf( S_ERROR "CL_ParseStaticEntity: static entities limit exceeded!\n" ); return; } @@ -730,7 +744,7 @@ static void CL_ParseQuakeSignon( sizebuf_t *msg ) int i = MSG_ReadByte( msg ); if( i == 3 ) cls.signon = SIGNONS - 1; - Msg( "CL_Signon: %d\n", i ); + Con_Printf( "CL_Signon: %d\n", i ); } /* @@ -819,8 +833,6 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) continue; } -// Msg( "%s\n", CL_MsgInfo( cmd )); - // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); @@ -855,6 +867,7 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) Con_Printf( "%s", MSG_ReadString( msg )); break; case svc_stufftext: + // FIXME: do revision for all Quake and Nehahra console commands str = MSG_ReadString( msg ); Msg( "%s\n", str ); Cbuf_AddText( str ); @@ -881,7 +894,8 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) case svc_updatefrags: param1 = MSG_ReadByte( msg ); param2 = MSG_ReadShort( msg ); - // FIXME: tell the client about scores + // HACKHACK: store frags into spectator + cl.players[param1].spectator = param2; break; case svc_clientdata: CL_ParseQuakeClientData( msg ); @@ -944,15 +958,15 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_cdtrack: param1 = MSG_ReadByte( msg ); - param1 = bound( 0, param1, MAX_CDTRACKS ); // tracknum + param1 = bound( 0, param1, MAX_CDTRACKS - 1 ); // tracknum param2 = MSG_ReadByte( msg ); - param2 = bound( 0, param2, MAX_CDTRACKS ); // loopnum - Msg( "main track %d, loop track %d\n", param1, param2 ); - // FIXME: allow cls.forcetrack from demo - S_StartBackgroundTrack( clgame.cdtracks[param1], clgame.cdtracks[param2], 0, false ); + param2 = bound( 0, param2, MAX_CDTRACKS - 1 ); // loopnum + if(( cls.demoplayback || cls.demorecording ) && ( cls.forcetrack != -1 )) + S_StartBackgroundTrack( clgame.cdtracks[cls.forcetrack], clgame.cdtracks[cls.forcetrack], 0, false ); + else S_StartBackgroundTrack( clgame.cdtracks[param1], clgame.cdtracks[param2], 0, false ); break; case svc_sellscreen: - Cmd_ExecuteString( "help" ); + Cmd_ExecuteString( "help" ); // open quake menu break; case svc_cutscene: CL_ParseFinaleCutscene( msg, 3 ); diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c index 30ab070e..f445ef7a 100644 --- a/engine/client/cl_remap.c +++ b/engine/client/cl_remap.c @@ -99,7 +99,7 @@ Dupliacte texture with remap pixels */ void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomcolor ) { - gltexture_t *glt; + gl_texture_t *glt; texture_t *tx = NULL; char texname[128]; int i, size, index; @@ -141,7 +141,7 @@ Update texture top and bottom colors */ void CL_UpdateStudioTexture( mstudiotexture_t *ptexture, int topcolor, int bottomcolor ) { - gltexture_t *glt; + gl_texture_t *glt; rgbdata_t *pic; texture_t *tx = NULL; char texname[128], name[128], mdlname[128]; @@ -179,11 +179,11 @@ void CL_UpdateStudioTexture( mstudiotexture_t *ptexture, int topcolor, int botto pic = FS_LoadImage( glt->name, raw, size ); if( !pic ) { - MsgDev( D_ERROR, "Couldn't update texture %s\n", glt->name ); + Con_DPrintf( S_ERROR "Couldn't update texture %s\n", glt->name ); return; } - index = GL_LoadTextureInternal( glt->name, pic, 0, true ); + index = GL_UpdateTextureInternal( glt->name, pic, 0 ); FS_FreeImage( pic ); // restore original palette @@ -224,7 +224,7 @@ void CL_UpdateAliasTexture( unsigned short *texture, int skinnum, int topcolor, skin.buffer = (byte *)(tx + 1); skin.palette = skin.buffer + skin.size; pic = FS_CopyImage( &skin ); // because GL_LoadTextureInternal will freed a rgbdata_t at end - *texture = GL_LoadTextureInternal( texname, pic, TF_KEEP_SOURCE, false ); + *texture = GL_LoadTextureInternal( texname, pic, TF_KEEP_SOURCE ); } // and now we can remap with internal routines diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index d3a2dac5..cc600be3 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -276,9 +276,9 @@ void SCR_MakeScreenShot( void ) { // snapshots don't writes message about image if( cls.scrshot_action != scrshot_snapshot ) - MsgDev( D_REPORT, "Write %s\n", cls.shotname ); + Con_Reportf( "Write %s\n", cls.shotname ); } - else MsgDev( D_ERROR, "Unable to write %s\n", cls.shotname ); + else Con_Printf( S_ERROR "Unable to write %s\n", cls.shotname ); cls.envshot_vieworg = NULL; cls.scrshot_action = scrshot_inactive; @@ -411,10 +411,10 @@ void SCR_TileClear( void ) if( clear.y2 <= clear.y1 ) return; // nothing disturbed - top = RI.viewport[1]; - bottom = top + RI.viewport[3] - 1; - left = RI.viewport[0]; - right = left + RI.viewport[2] - 1; + top = clgame.viewport[1]; + bottom = top + clgame.viewport[3] - 1; + left = clgame.viewport[0]; + right = left + clgame.viewport[2] - 1; if( clear.y1 < top ) { @@ -569,7 +569,7 @@ void SCR_LoadCreditsFont( void ) if( !SCR_LoadVariableWidthFont( "gfx.wad/creditsfont.fnt" )) { if( !SCR_LoadFixedWidthFont( "gfx/conchars" )) - MsgDev( D_ERROR, "failed to load HUD font\n" ); + Con_DPrintf( S_ERROR "failed to load HUD font\n" ); } } @@ -707,7 +707,6 @@ void SCR_Init( void ) { if( scr_init ) return; - MsgDev( D_NOTE, "SCR_Init()\n" ); scr_centertime = Cvar_Get( "scr_centertime", "2.5", 0, "centerprint hold time" ); cl_levelshot_name = Cvar_Get( "cl_levelshot_name", "*black", 0, "contains path to current levelshot" ); cl_allow_levelshots = Cvar_Get( "allow_levelshots", "0", FCVAR_ARCHIVE, "allow engine to use indivdual levelshots instead of 'loading' image" ); @@ -750,7 +749,6 @@ void SCR_Shutdown( void ) { if( !scr_init ) return; - MsgDev( D_NOTE, "SCR_Shutdown()\n" ); Cmd_RemoveCommand( "timerefresh" ); Cmd_RemoveCommand( "skyname" ); Cmd_RemoveCommand( "viewpos" ); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index d2ad1459..6202f33f 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -593,7 +593,7 @@ TEMPENTITY *CL_TempEntAlloc( const vec3_t org, model_t *pmodel ) if( !cl_free_tents ) { - MsgDev( D_INFO, "Overflow %d temporary ents!\n", GI->max_tents ); + Con_DPrintf( "Overflow %d temporary ents!\n", GI->max_tents ); return NULL; } @@ -633,7 +633,7 @@ TEMPENTITY *CL_TempEntAllocHigh( const vec3_t org, model_t *pmodel ) { // didn't find anything? The tent list is either full of high-priority tents // or all tents in the list are still due to live for > 10 seconds. - MsgDev( D_INFO, "Couldn't alloc a high priority TENT!\n" ); + Con_DPrintf( "Couldn't alloc a high priority TENT!\n" ); return NULL; } @@ -870,10 +870,7 @@ void R_AttachTentToPlayer( int client, int modelIndex, float zoffset, float life model_t *pModel; if( client <= 0 || client > cl.maxclients ) - { - MsgDev( D_ERROR, "Bad client %i in AttachTentToPlayer()!\n", client ); return; - } pClient = CL_GetEntityByIndex( client ); @@ -926,10 +923,7 @@ void R_KillAttachedTents( int client ) int i; if( client <= 0 || client > cl.maxclients ) - { - MsgDev( D_ERROR, "Bad client %i in KillAttachedTents()!\n", client ); return; - } for( i = 0; i < GI->max_tents; i++ ) { @@ -1282,7 +1276,7 @@ TEMPENTITY *R_DefaultSprite( const vec3_t pos, int spriteIndex, float framerate if(( psprite = CL_ModelHandle( spriteIndex )) == NULL || psprite->type != mod_sprite ) { - MsgDev( D_INFO, "No Sprite %d!\n", spriteIndex ); + Con_Reportf( "No Sprite %d!\n", spriteIndex ); return NULL; } @@ -1339,7 +1333,7 @@ TEMPENTITY *R_TempSprite( vec3_t pos, const vec3_t dir, float scale, int modelIn if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) { - MsgDev( D_ERROR, "No model %d!\n", modelIndex ); + Con_Reportf( S_ERROR "No model %d!\n", modelIndex ); return NULL; } @@ -1446,7 +1440,7 @@ void R_Spray( const vec3_t pos, const vec3_t dir, int modelIndex, int count, int if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) { - MsgDev( D_INFO, "No model %d!\n", modelIndex ); + Con_Reportf( "No model %d!\n", modelIndex ); return; } @@ -1577,7 +1571,7 @@ void R_FunnelSprite( const vec3_t org, int modelIndex, int reverse ) if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) { - MsgDev( D_ERROR, "no model %d!\n", modelIndex ); + Con_Reportf( S_ERROR "no model %d!\n", modelIndex ); return; } @@ -1823,10 +1817,7 @@ void R_PlayerSprites( int client, int modelIndex, int count, int size ) pEnt = CL_GetEntityByIndex( client ); if( !pEnt || !pEnt->player ) - { - MsgDev( D_INFO, "Bad ent %i in R_PlayerSprites()!\n", client ); return; - } vel = 128; @@ -2511,12 +2502,13 @@ void CL_ParseTempEntity( sizebuf_t *msg ) R_UserTracerParticle( pos, pos2, life, color, scale, 0, NULL ); break; default: - MsgDev( D_ERROR, "ParseTempEntity: illegible TE message %i\n", type ); + Con_DPrintf( S_ERROR "ParseTempEntity: illegible TE message %i\n", type ); break; } // throw warning - if( MSG_CheckOverflow( &buf )) MsgDev( D_WARN, "ParseTempEntity: overflow TE message\n" ); + if( MSG_CheckOverflow( &buf )) + Con_DPrintf( S_WARN "ParseTempEntity: overflow TE message\n" ); } @@ -2573,7 +2565,8 @@ void CL_SetLightstyle( int style, const char *s, float f ) break; } } - MsgDev( D_REPORT, "Lightstyle %i (%s), interp %s\n", style, ls->pattern, ls->interp ? "Yes" : "No" ); + + Con_Reportf( "Lightstyle %i (%s), interp %s\n", style, ls->pattern, ls->interp ? "Yes" : "No" ); } /* @@ -3014,7 +3007,7 @@ void CL_PlayerDecal( int playernum, int customIndex, int entityIndex, float *pos if( !pCust->nUserData1 && pCust->pInfo != NULL ) { const char *decalname = va( "player%dlogo%d", playernum, customIndex ); - pCust->nUserData1 = GL_LoadTextureInternal( decalname, pCust->pInfo, TF_DECAL, false ); + pCust->nUserData1 = GL_LoadTextureInternal( decalname, pCust->pInfo, TF_DECAL ); } textureIndex = pCust->nUserData1; } diff --git a/engine/client/cl_video.c b/engine/client/cl_video.c index d2861158..58de74b2 100644 --- a/engine/client/cl_video.c +++ b/engine/client/cl_video.c @@ -209,7 +209,7 @@ qboolean SCR_PlayCinematic( const char *arg ) if( FS_FileExists( arg, false ) && !fullpath ) { - MsgDev( D_ERROR, "Couldn't load %s from packfile. Please extract it\n", path ); + Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", path ); return false; } @@ -235,6 +235,7 @@ qboolean SCR_PlayCinematic( const char *arg ) UI_SetActiveMenu( false ); cls.state = ca_cinematic; + Con_FastClose(); cin_time = 0.0f; cls.signon = 0; diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 28bffe78..78ab8722 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -286,7 +286,7 @@ qboolean V_PreRender( void ) { if(( host.realtime - cls.disable_screen ) > cl_timeout->value ) { - MsgDev( D_ERROR, "V_PreRender: loading plaque timed out\n" ); + Con_Reportf( "V_PreRender: loading plaque timed out\n" ); cls.disable_screen = 0.0f; } return false; diff --git a/engine/client/client.h b/engine/client/client.h index df9f30a4..a5b3ad0f 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -948,6 +948,7 @@ qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ); void CL_ResetLatchedVars( cl_entity_t *ent, qboolean full_reset ); qboolean CL_GetEntitySpatialization( struct channel_s *ch ); qboolean CL_GetMovieSpatialization( struct rawchan_s *ch ); +void CL_ProcessPlayerState( int playerindex, entity_state_t *state ); void CL_ComputePlayerOrigin( cl_entity_t *clent ); void CL_ProcessPacket( frame_t *frame ); void CL_MoveThirdpersonCamera( void ); diff --git a/engine/client/gl_alias.c b/engine/client/gl_alias.c index 124f23ec..e7df65d8 100644 --- a/engine/client/gl_alias.c +++ b/engine/client/gl_alias.c @@ -487,7 +487,7 @@ void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size ) m_pAliasHeader->gl_texturenum[skinnum][0] = m_pAliasHeader->gl_texturenum[skinnum][1] = m_pAliasHeader->gl_texturenum[skinnum][2] = - m_pAliasHeader->gl_texturenum[skinnum][3] = GL_LoadTextureInternal( name, pic, 0, false ); + m_pAliasHeader->gl_texturenum[skinnum][3] = GL_LoadTextureInternal( name, pic, 0 ); FS_FreeImage( pic ); if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][0] )->flags & TF_HAS_LUMA ) @@ -496,7 +496,7 @@ void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size ) m_pAliasHeader->fb_texturenum[skinnum][0] = m_pAliasHeader->fb_texturenum[skinnum][1] = m_pAliasHeader->fb_texturenum[skinnum][2] = - m_pAliasHeader->fb_texturenum[skinnum][3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA, false ); + m_pAliasHeader->fb_texturenum[skinnum][3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA ); FS_FreeImage( pic ); } @@ -521,14 +521,14 @@ void *Mod_LoadGroupSkin( daliasskintype_t *pskintype, int skinnum, int size ) { Q_snprintf( name, sizeof( name ), "%s_%i_%i", loadmodel->name, skinnum, i ); pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); - m_pAliasHeader->gl_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( name, pic, 0, false ); + m_pAliasHeader->gl_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( name, pic, 0 ); FS_FreeImage( pic ); if( R_GetTexture( m_pAliasHeader->gl_texturenum[skinnum][i & 3] )->flags & TF_HAS_LUMA ) { Q_snprintf( lumaname, sizeof( lumaname ), "%s_%i_%i_luma", loadmodel->name, skinnum, i ); pic = Mod_CreateSkinData( NULL, (byte *)(pskintype), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); - m_pAliasHeader->fb_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA, false ); + m_pAliasHeader->fb_texturenum[skinnum][i & 3] = GL_LoadTextureInternal( lumaname, pic, TF_MAKELUMA ); FS_FreeImage( pic ); } diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index 71a94888..003c4683 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -185,7 +185,7 @@ void GL_SelectTexture( GLint tmu ) if( tmu >= GL_MaxTextureUnits( )) { - MsgDev( D_ERROR, "GL_SelectTexture: bad tmu state %i\n", tmu ); + Con_Reportf( S_ERROR "GL_SelectTexture: bad tmu state %i\n", tmu ); return; } @@ -249,6 +249,7 @@ GL_CleanupAllTextureUnits */ void GL_CleanupAllTextureUnits( void ) { + if( !glw_state.initialized ) return; // force to cleanup all the units GL_SelectTexture( GL_MaxTextureUnits() - 1 ); GL_CleanUpTextureUnits( 0 ); @@ -277,7 +278,7 @@ void GL_TextureTarget( uint target ) { if( glState.activeTMU < 0 || glState.activeTMU >= GL_MaxTextureUnits( )) { - MsgDev( D_ERROR, "GL_TextureTarget: bad tmu state %i\n", glState.activeTMU ); + Con_Reportf( S_ERROR "GL_TextureTarget: bad tmu state %i\n", glState.activeTMU ); return; } @@ -644,7 +645,7 @@ was there. This is used to test for texture thrashing. */ void R_ShowTextures( void ) { - gltexture_t *image; + gl_texture_t *image; float x, y, w, h; int total, start, end; int i, j, k, base_w, base_h; diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index f73b53d7..a0b67d76 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -266,7 +266,7 @@ static qboolean R_BeamComputePoint( int beamEnt, vec3_t pt ) if( !ent ) { - MsgDev( D_ERROR, "R_BeamComputePoint: invalid entity %i\n", BEAMENT_ENTITY( beamEnt )); + Con_DPrintf( S_ERROR "R_BeamComputePoint: invalid entity %i\n", BEAMENT_ENTITY( beamEnt )); VectorClear( pt ); return false; } @@ -418,8 +418,6 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f div = 1.0f / (segments - 1); length *= 0.01f; - - // UNDONE: Expose texture length scale factor to control "fuzziness" vStep = length * div; // Texture length texels per space pixel // Scroll speed 3.5 -- initial texture position, scrolls 3.5/sec (1.0 is entire texture) @@ -432,22 +430,18 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f segments = 16; div = 1.0f / ( segments - 1 ); } - scale *= 100.0f; length = segments * 0.1f; } else { - scale *= length; + scale *= length * 2.0; } // Iterator to resample noise waveform (it needs to be generated in powers of 2) - noiseStep = noiseIndex = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); - - if( FBitSet( flags, FBEAM_SINENOISE )) - noiseIndex = 0; - + noiseStep = (int)((float)( NOISE_DIVISIONS - 1 ) * div * 65536.0f ); brightness = 1.0f; + noiseIndex = 0; if( FBitSet( flags, FBEAM_SHADEIN )) brightness = 0; @@ -468,20 +462,6 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f fraction = i * div; - if( FBitSet( flags, FBEAM_SHADEIN ) && FBitSet( flags, FBEAM_SHADEOUT )) - { - if( fraction < 0.5f ) brightness = 2.0f * fraction; - else brightness = 2.0f * ( 1.0f - fraction ); - } - else if( FBitSet( flags, FBEAM_SHADEIN )) - { - brightness = fraction; - } - else if( FBitSet( flags, FBEAM_SHADEOUT )) - { - brightness = 1.0f - fraction; - } - VectorMA( source, fraction, delta, nextSeg.pos ); // distort using noise @@ -549,6 +529,20 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f curSeg = nextSeg; segs_drawn++; + if( FBitSet( flags, FBEAM_SHADEIN ) && FBitSet( flags, FBEAM_SHADEOUT )) + { + if( fraction < 0.5f ) brightness = fraction; + else brightness = ( 1.0f - fraction ); + } + else if( FBitSet( flags, FBEAM_SHADEIN )) + { + brightness = fraction; + } + else if( FBitSet( flags, FBEAM_SHADEOUT )) + { + brightness = 1.0f - fraction; + } + if( segs_drawn == total_segs ) { // draw the last segment @@ -1377,7 +1371,7 @@ void CL_AddCustomBeam( cl_entity_t *pEnvBeam ) { if( tr.draw_list->num_beam_entities >= MAX_VISIBLE_PACKET ) { - MsgDev( D_ERROR, "Too many custom beams %d!\n", tr.draw_list->num_beam_entities ); + Con_Printf( S_ERROR "Too many beams %d!\n", tr.draw_list->num_beam_entities ); return; } @@ -1492,7 +1486,7 @@ void CL_DrawBeams( int fTrans ) BEAM *pPrev = NULL; int i, flags; - if( !cl_draw_beams->value ) + if( !CVAR_TO_BOOL( cl_draw_beams )) return; pglShadeModel( GL_SMOOTH ); @@ -1871,7 +1865,6 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType ) R_BeamEnts( startEnt, endEnt, modelIndex, life, width, noise, a, speed, startFrame, frameRate, r, g, b ); break; case TE_BEAM: - MsgDev( D_ERROR, "TE_BEAM is obsolete\n" ); break; case TE_BEAMSPRITE: start[0] = MSG_ReadCoord( msg ); @@ -1934,7 +1927,6 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType ) R_BeamRing( startEnt, endEnt, modelIndex, life, width, noise, a, speed, startFrame, frameRate, r, g, b ); break; case TE_BEAMHOSE: - MsgDev( D_ERROR, "TE_BEAMHOSE is obsolete\n" ); break; case TE_KILLBEAM: startEnt = MSG_ReadShort( msg ); diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index 0b4aa75a..69ab86ec 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -577,11 +577,7 @@ static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, fl decal_t *pdecal, *pold; int count, vertCount; - if( !surf ) - { - MsgDev( D_ERROR, "psurface NULL in R_DecalCreate!\n" ); - return; - } + if( !surf ) return; // ??? pold = R_DecalIntersect( decalinfo, surf, &count ); if( count < MAX_OVERLAP_DECALS ) pold = NULL; @@ -763,7 +759,7 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES ) { - MsgDev( D_ERROR, "Decal has invalid texture!\n" ); + Con_Printf( S_ERROR "Decal has invalid texture!\n" ); return; } @@ -783,7 +779,7 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos if( model->type != mod_brush ) { - MsgDev( D_ERROR, "Decals must hit mod_brush!\n" ); + Con_Printf( S_ERROR "Decals must hit mod_brush!\n" ); return; } @@ -988,7 +984,6 @@ void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ) } } - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( reverse && e->curstate.rendermode == kRenderTransTexture ) @@ -1224,10 +1219,7 @@ void R_DecalRemoveAll( int textureIndex ) int i; if( textureIndex < 0 || textureIndex >= MAX_TEXTURES ) - { - MsgDev( D_ERROR, "Decal has invalid texture!\n" ); - return; - } + return; // out of bounds for( i = 0; i < gDecalCount; i++ ) { diff --git a/engine/client/gl_draw.c b/engine/client/gl_draw.c index eab1b3b7..f970e409 100644 --- a/engine/client/gl_draw.c +++ b/engine/client/gl_draw.c @@ -24,7 +24,7 @@ R_GetImageParms */ void R_GetTextureParms( int *w, int *h, int texnum ) { - gltexture_t *glt; + gl_texture_t *glt; glt = R_GetTexture( texnum ); if( w ) *w = glt->srcWidth; @@ -94,7 +94,7 @@ refresh window. void R_DrawTileClear( int x, int y, int w, int h ) { float tw, th; - gltexture_t *glt; + gl_texture_t *glt; GL_SetRenderMode( kRenderNormal ); pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); @@ -124,7 +124,7 @@ R_DrawStretchRaw void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, qboolean dirty ) { byte *raw = NULL; - gltexture_t *tex; + gl_texture_t *tex; if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { @@ -196,7 +196,7 @@ R_UploadStretchRaw void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height, const byte *data ) { byte *raw = NULL; - gltexture_t *tex; + gl_texture_t *tex; if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { diff --git a/engine/client/gl_export.h b/engine/client/gl_export.h index 22ff3fdc..f75564c0 100644 --- a/engine/client/gl_export.h +++ b/engine/client/gl_export.h @@ -148,6 +148,7 @@ typedef float GLmatrix[16]; #define GL_2_BYTES 0x1407 #define GL_3_BYTES 0x1408 #define GL_4_BYTES 0x1409 +#define GL_HALF_FLOAT_ARB 0x140B #define GL_VERTEX_ARRAY 0x8074 #define GL_NORMAL_ARRAY 0x8075 @@ -390,6 +391,7 @@ typedef float GLmatrix[16]; #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD #define GL_COMPRESSED_ALPHA_ARB 0x84E9 #define GL_COMPRESSED_LUMINANCE_ARB 0x84EA #define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 699cca8c..7006b0dc 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -16,18 +16,12 @@ GNU General Public License for more details. #include "common.h" #include "client.h" #include "gl_local.h" -#include "studio.h" #define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2) -static gltexture_t r_textures[MAX_TEXTURES]; -static gltexture_t *r_texturesHashTable[TEXTURES_HASH_SIZE]; -static byte data2D[1024]; // intermediate texbuffer -static int r_numTextures; -static rgbdata_t r_image; // generic pixelbuffer used for internal textures - -// internal tables -static vec3_t r_luminanceTable[256]; // RGB to luminance +static gl_texture_t gl_textures[MAX_TEXTURES]; +static gl_texture_t* gl_texturesHashTable[TEXTURES_HASH_SIZE]; +static uint gl_numTextures; #define IsLightMap( tex ) ( FBitSet(( tex )->flags, TF_ATLAS_PAGE )) /* @@ -37,10 +31,10 @@ R_GetTexture acess to array elem ================= */ -gltexture_t *R_GetTexture( GLenum texnum ) +gl_texture_t *R_GetTexture( GLenum texnum ) { ASSERT( texnum >= 0 && texnum < MAX_TEXTURES ); - return &r_textures[texnum]; + return &gl_textures[texnum]; } /* @@ -75,18 +69,20 @@ GL_Bind */ void GL_Bind( GLint tmu, GLenum texnum ) { - gltexture_t *texture; + gl_texture_t *texture; GLuint glTarget; - // missed texture ? - if( texnum <= 0 ) texnum = tr.defaultTexture; - Assert( texnum > 0 && texnum < MAX_TEXTURES ); + Assert( texnum >= 0 && texnum < MAX_TEXTURES ); + + // missed or invalid texture? + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + texnum = tr.defaultTexture; if( tmu != GL_KEEP_UNIT ) GL_SelectTexture( tmu ); else tmu = glState.activeTMU; - texture = &r_textures[texnum]; + texture = &gl_textures[texnum]; glTarget = texture->target; if( glTarget == GL_TEXTURE_2D_ARRAY_EXT ) @@ -112,7 +108,7 @@ void GL_Bind( GLint tmu, GLenum texnum ) GL_ApplyTextureParams ================= */ -void GL_ApplyTextureParams( gltexture_t *tex ) +void GL_ApplyTextureParams( gl_texture_t *tex ) { vec4_t border = { 0.0f, 0.0f, 0.0f, 1.0f }; @@ -247,7 +243,7 @@ GL_UpdateTextureParams */ static void GL_UpdateTextureParams( int iTexture ) { - gltexture_t *tex = &r_textures[iTexture]; + gl_texture_t *tex = &gl_textures[iTexture]; Assert( tex != NULL ); @@ -322,7 +318,7 @@ void R_SetTextureParameters( void ) ClearBits( gl_lightmap_nearest->flags, FCVAR_CHANGED ); // change all the existing mipmapped texture objects - for( i = 0; i < r_numTextures; i++ ) + for( i = 0; i < gl_numTextures; i++ ) GL_UpdateTextureParams( i ); } @@ -365,6 +361,7 @@ static size_t GL_CalcImageSize( pixformat_t format, int width, int height, int d break; case PF_DXT3: case PF_DXT5: + case PF_ATI2: size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth; break; } @@ -392,6 +389,7 @@ static size_t GL_CalcTextureSize( GLenum format, int width, int height, int dept break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth; break; case GL_RGBA8: @@ -458,7 +456,7 @@ static size_t GL_CalcTextureSize( GLenum format, int width, int height, int dept size = width * height * depth * 2; break; case GL_DEPTH_COMPONENT24: - size = width * height * depth * 4; + size = width * height * depth * 3; break; case GL_DEPTH_COMPONENT32F: size = width * height * depth * 4; @@ -471,7 +469,7 @@ static size_t GL_CalcTextureSize( GLenum format, int width, int height, int dept return size; } -static int GL_CalcMipmapCount( gltexture_t *tex, qboolean haveBuffer ) +static int GL_CalcMipmapCount( gl_texture_t *tex, qboolean haveBuffer ) { int width, height; int mipcount; @@ -502,7 +500,7 @@ static int GL_CalcMipmapCount( gltexture_t *tex, qboolean haveBuffer ) GL_SetTextureDimensions ================ */ -static void GL_SetTextureDimensions( gltexture_t *tex, int width, int height, int depth ) +static void GL_SetTextureDimensions( gl_texture_t *tex, int width, int height, int depth ) { int maxTextureSize; int maxDepthSize = 1; @@ -591,7 +589,7 @@ static void GL_SetTextureDimensions( gltexture_t *tex, int width, int height, in GL_SetTextureTarget =============== */ -static void GL_SetTextureTarget( gltexture_t *tex, rgbdata_t *pic ) +static void GL_SetTextureTarget( gl_texture_t *tex, rgbdata_t *pic ) { Assert( pic != NULL ); Assert( tex != NULL ); @@ -612,7 +610,7 @@ static void GL_SetTextureTarget( gltexture_t *tex, rgbdata_t *pic ) tex->target = GL_TEXTURE_2D_ARRAY_EXT; else if( pic->width > 1 && pic->height > 1 && pic->depth > 1 ) tex->target = GL_TEXTURE_3D; - else if( FBitSet( tex->flags, TF_TEXTURE_RECTANGLE ) && pic->width == glState.width && pic->height == glState.height ) + else if( FBitSet( tex->flags, TF_RECTANGLE )) tex->target = GL_TEXTURE_RECTANGLE_EXT; else tex->target = GL_TEXTURE_2D; // default case @@ -636,9 +634,6 @@ static void GL_SetTextureTarget( gltexture_t *tex, rgbdata_t *pic ) // depth cubemaps only allowed when GL_EXT_gpu_shader4 is supported if( tex->target == GL_TEXTURE_CUBE_MAP_ARB && !GL_Support( GL_EXT_GPU_SHADER4 ) && FBitSet( tex->flags, TF_DEPTHMAP )) tex->target = GL_NONE; - - if( tex->target == GL_TEXTURE_CUBE_MAP_ARB ) - tex->flags |= TF_CUBEMAP; // it's cubemap! } /* @@ -646,7 +641,7 @@ static void GL_SetTextureTarget( gltexture_t *tex, rgbdata_t *pic ) GL_SetTextureFormat =============== */ -static void GL_SetTextureFormat( gltexture_t *tex, pixformat_t format, int channelMask ) +static void GL_SetTextureFormat( gl_texture_t *tex, pixformat_t format, int channelMask ) { qboolean haveColor = ( channelMask & IMAGE_HAS_COLOR ); qboolean haveAlpha = ( channelMask & IMAGE_HAS_ALPHA ); @@ -660,6 +655,7 @@ static void GL_SetTextureFormat( gltexture_t *tex, pixformat_t format, int chann case PF_DXT1: tex->format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; // never use DXT1 with 1-bit alpha case PF_DXT3: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case PF_DXT5: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case PF_ATI2: tex->format = GL_COMPRESSED_RED_GREEN_RGTC2_EXT; break; } return; } @@ -671,7 +667,7 @@ static void GL_SetTextureFormat( gltexture_t *tex, pixformat_t format, int chann tex->format = GL_DEPTH_COMPONENT32F; else tex->format = GL_DEPTH_COMPONENT24; } - else if( FBitSet( tex->flags, TF_ARB_FLOAT ) && GL_Support( GL_ARB_TEXTURE_FLOAT_EXT )) + else if( FBitSet( tex->flags, TF_ARB_FLOAT|TF_ARB_16BIT ) && GL_Support( GL_ARB_TEXTURE_FLOAT_EXT )) { if( haveColor && haveAlpha ) { @@ -895,31 +891,6 @@ byte *GL_ApplyFilter( const byte *source, int width, int height ) return out; } -/* -================= -GL_ApplyGamma - -Assume input buffer is RGBA -================= -*/ -byte *GL_ApplyGamma( const byte *source, int pixels, qboolean isNormalMap ) -{ - byte *in = (byte *)source; - byte *out = (byte *)source; - int i; - - if( source && !isNormalMap ) - { - for( i = 0; i < pixels; i++, in += 4 ) - { - in[0] = TextureToGamma( in[0] ); - in[1] = TextureToGamma( in[1] ); - in[2] = TextureToGamma( in[2] ); - } - } - return out; -} - /* ================= GL_BuildMipMap @@ -974,7 +945,6 @@ static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, normal[2] = MAKE_SIGNED( in[row+2] ) + MAKE_SIGNED( next[row+2] ); } - if( !VectorNormalizeLength( normal )) VectorSet( normal, 0.5f, 0.5f, 1.0f ); @@ -1012,48 +982,23 @@ static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, } } -/* -================= -GL_MakeLuminance - -Converts the given image to luminance -================= -*/ -void GL_MakeLuminance( rgbdata_t *in ) -{ - byte luminance; - float r, g, b; - int x, y; - - for( y = 0; y < in->height; y++ ) - { - for( x = 0; x < in->width; x++ ) - { - r = r_luminanceTable[in->buffer[4*(y*in->width+x)+0]][0]; - g = r_luminanceTable[in->buffer[4*(y*in->width+x)+1]][1]; - b = r_luminanceTable[in->buffer[4*(y*in->width+x)+2]][2]; - - luminance = (byte)(r + g + b); - - in->buffer[4*(y*in->width+x)+0] = luminance; - in->buffer[4*(y*in->width+x)+1] = luminance; - in->buffer[4*(y*in->width+x)+2] = luminance; - } - } -} - -static void GL_TextureImageRAW( gltexture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, GLint type, const void *data ) +static void GL_TextureImageRAW( gl_texture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, GLint type, const void *data ) { GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; - qboolean subImage = ( tex->flags & TF_IMG_UPLOADED ); + qboolean subImage = FBitSet( tex->flags, TF_IMG_UPLOADED ); GLenum inFormat = PFDesc[type].glFormat; GLint dataType = GL_UNSIGNED_BYTE; Assert( tex != NULL ); - if( tex->flags & TF_DEPTHMAP ) + if( FBitSet( tex->flags, TF_DEPTHMAP )) inFormat = GL_DEPTH_COMPONENT; + if( FBitSet( tex->flags, TF_ARB_16BIT )) + dataType = GL_HALF_FLOAT_ARB; + else if( FBitSet( tex->flags, TF_ARB_FLOAT )) + dataType = GL_FLOAT; + if( tex->target == GL_TEXTURE_1D ) { if( subImage ) pglTexSubImage1D( tex->target, level, 0, width, inFormat, dataType, data ); @@ -1076,10 +1021,10 @@ static void GL_TextureImageRAW( gltexture_t *tex, GLint side, GLint level, GLint } } -static void GL_TextureImageDXT( gltexture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, size_t size, const void *data ) +static void GL_TextureImageDXT( gl_texture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, size_t size, const void *data ) { GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; - qboolean subImage = ( tex->flags & TF_IMG_UPLOADED ); + qboolean subImage = FBitSet( tex->flags, TF_IMG_UPLOADED ); Assert( tex != NULL ); @@ -1112,15 +1057,15 @@ GL_CheckTexImageError show GL-errors on load images =============== */ -static void GL_CheckTexImageError( gltexture_t *tex ) +static void GL_CheckTexImageError( gl_texture_t *tex ) { int err; Assert( tex != NULL ); // catch possible errors - if(( err = pglGetError()) != GL_NO_ERROR ) - MsgDev( D_ERROR, "GL_UploadTexture: error %x while uploading %s [%s]\n", err, tex->name, GL_TargetToString( tex->target )); + if( CVAR_TO_BOOL( gl_check_errors ) && ( err = pglGetError()) != GL_NO_ERROR ) + Con_Printf( S_OPENGL_ERROR "%s while uploading %s [%s]\n", GL_ErrorString( err ), tex->name, GL_TargetToString( tex->target )); } /* @@ -1130,7 +1075,7 @@ GL_UploadTexture upload texture into video memory =============== */ -static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) +static qboolean GL_UploadTexture( gl_texture_t *tex, rgbdata_t *pic ) { byte *buf, *data; size_t texsize, size; @@ -1148,7 +1093,7 @@ static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) // make sure what target is correct if( tex->target == GL_NONE ) { - MsgDev( D_ERROR, "GL_UploadTexture: %s is not supported by your hardware\n", tex->name ); + Con_DPrintf( S_ERROR "GL_UploadTexture: %s is not supported by your hardware\n", tex->name ); return false; } @@ -1224,10 +1169,7 @@ static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) if(( tex->depth == 1 ) && ( pic->width != tex->width ) || ( pic->height != tex->height )) data = GL_ResampleTexture( buf, pic->width, pic->height, tex->width, tex->height, normalMap ); else data = buf; -#if 0 // g-cont. we can't apply gamma to each texture so we shouldn't do it at all - if( !ImageDXT( pic->type ) && !FBitSet( tex->flags, TF_NOMIPMAP|TF_SKYSIDE )) - data = GL_ApplyGamma( data, tex->width * tex->height * tex->depth, FBitSet( tex->flags, TF_NORMALMAP )); -#endif + if( !ImageDXT( pic->type ) && !FBitSet( tex->flags, TF_NOMIPMAP ) && FBitSet( pic->flags, IMAGE_ONEBIT_ALPHA )) data = GL_ApplyFilter( data, tex->width, tex->height ); @@ -1253,7 +1195,7 @@ static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic ) } } - tex->flags |= TF_IMG_UPLOADED; // done + SetBits( tex->flags, TF_IMG_UPLOADED ); // done tex->numMips /= numSides; return true; @@ -1266,7 +1208,7 @@ GL_ProcessImage do specified actions on pixels =============== */ -static void GL_ProcessImage( gltexture_t *tex, rgbdata_t *pic, imgfilter_t *filter ) +static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic, imgfilter_t *filter ) { uint img_flags = 0; @@ -1310,14 +1252,166 @@ static void GL_ProcessImage( gltexture_t *tex, rgbdata_t *pic, imgfilter_t *filt // processing image before uploading (force to rgba, make luma etc) if( pic->buffer ) Image_Process( &pic, 0, 0, img_flags, filter ); - if( tex->flags & TF_LUMINANCE ) + if( FBitSet( tex->flags, TF_LUMINANCE )) + ClearBits( pic->flags, IMAGE_HAS_COLOR ); + } +} + +/* +================ +GL_CheckTexName +================ +*/ +qboolean GL_CheckTexName( const char *name ) +{ + if( !COM_CheckString( name ) || !glw_state.initialized ) + return false; + + // because multi-layered textures can exceed name string + if( Q_strlen( name ) >= sizeof( gl_textures->name )) + { + Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, Q_strlen( name )); + return false; + } + + return true; +} + +/* +================ +GL_TextureForName +================ +*/ +static gl_texture_t *GL_TextureForName( const char *name ) +{ + gl_texture_t *tex; + uint hash; + + // find the texture in array + hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); + + for( tex = gl_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) + { + if( !Q_stricmp( tex->name, name )) + return tex; + } + + return NULL; +} + +/* +================ +GL_AllocTexture +================ +*/ +static gl_texture_t *GL_AllocTexture( const char *name, texFlags_t flags ) +{ + gl_texture_t *tex; + uint i; + + // find a free texture_t slot + for( i = 0, tex = gl_textures; i < gl_numTextures; i++, tex++ ) + if( !tex->name[0] ) break; + + if( i == gl_numTextures ) + { + if( gl_numTextures == MAX_TEXTURES ) + Host_Error( "GL_AllocTexture: MAX_TEXTURES limit exceeds\n" ); + gl_numTextures++; + } + + tex = &gl_textures[i]; + + // copy initial params + Q_strncpy( tex->name, name, sizeof( tex->name )); + if( FBitSet( flags, TF_SKYSIDE )) + tex->texnum = tr.skyboxbasenum++; + else tex->texnum = i; // texnum is used for fast acess into gl_textures array too + tex->flags = flags; + + // add to hash table + tex->hashValue = COM_HashKey( name, TEXTURES_HASH_SIZE ); + tex->nextHash = gl_texturesHashTable[tex->hashValue]; + gl_texturesHashTable[tex->hashValue] = tex; + + return tex; +} + +/* +================ +GL_DeleteTexture +================ +*/ +static void GL_DeleteTexture( gl_texture_t *tex ) +{ + gl_texture_t **prev; + gl_texture_t *cur; + + ASSERT( tex != NULL ); + + // already freed? + if( !tex->texnum ) return; + + // debug + if( !tex->name[0] ) + { + Con_Printf( S_ERROR "GL_DeleteTexture: trying to free unnamed texture with texnum %i\n", tex->texnum ); + return; + } + + // remove from hash table + prev = &gl_texturesHashTable[tex->hashValue]; + + while( 1 ) + { + cur = *prev; + if( !cur ) break; + + if( cur == tex ) { - if( !( tex->flags & TF_DEPTHMAP )) - { - GL_MakeLuminance( pic ); - tex->flags &= ~TF_LUMINANCE; - } - pic->flags &= ~IMAGE_HAS_COLOR; + *prev = cur->nextHash; + break; + } + prev = &cur->nextHash; + } + + // release source + if( tex->original ) + FS_FreeImage( tex->original ); + + pglDeleteTextures( 1, &tex->texnum ); + memset( tex, 0, sizeof( *tex )); +} + +/* +================ +GL_UpdateTexSize + +recalc image room +================ +*/ +void GL_UpdateTexSize( int texnum, int width, int height, int depth ) +{ + int i, j, texsize; + int numSides; + gl_texture_t *tex; + + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + return; + + tex = &gl_textures[texnum]; + numSides = FBitSet( tex->flags, TF_CUBEMAP ) ? 6 : 1; + GL_SetTextureDimensions( tex, width, height, depth ); + tex->size = 0; // recompute now + + for( i = 0; i < numSides; i++ ) + { + for( j = 0; j < Q_max( 1, tex->numMips ); j++ ) + { + width = Q_max( 1, ( tex->width >> j )); + height = Q_max( 1, ( tex->height >> j )); + texsize = GL_CalcTextureSize( tex->format, width, height, tex->depth ); + tex->size += texsize; } } } @@ -1329,34 +1423,22 @@ GL_LoadTexture */ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ) { - gltexture_t *tex; + gl_texture_t *tex; rgbdata_t *pic; - uint i, hash; uint picFlags = 0; - if( !COM_CheckString( name ) || !glw_state.initialized ) + if( !GL_CheckTexName( name )) return 0; - if( Q_strlen( name ) >= sizeof( r_textures->name )) - { - Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, Q_strlen( name )); - return 0; - } - // see if already loaded - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); - for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) - { - if( !Q_stricmp( tex->name, name )) - return (tex - r_textures); - } - - if( flags & TF_NOFLIP_TGA ) - picFlags |= IL_DONTFLIP_TGA; + if( FBitSet( flags, TF_NOFLIP_TGA )) + SetBits( picFlags, IL_DONTFLIP_TGA ); if( FBitSet( flags, TF_KEEP_SOURCE ) && !FBitSet( flags, TF_EXPAND_SOURCE )) - picFlags |= IL_KEEP_8BIT; + SetBits( picFlags, IL_KEEP_8BIT ); // set some image flags Image_SetForceFlags( picFlags ); @@ -1364,34 +1446,13 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i pic = FS_LoadImage( name, buf, size ); if( !pic ) return 0; // couldn't loading image - // find a free texture slot - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - - // find a free texture_t slot - for( i = 0, tex = r_textures; i < r_numTextures; i++, tex++ ) - if( !tex->name[0] ) break; - - if( i == r_numTextures ) - { - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - r_numTextures++; - } - - tex = &r_textures[i]; - Q_strncpy( tex->name, name, sizeof( tex->name )); - tex->flags = flags; - - if( flags & TF_SKYSIDE ) - tex->texnum = tr.skyboxbasenum++; - else tex->texnum = i; // texnum is used for fast acess into r_textures array too - + // allocate the new one + tex = GL_AllocTexture( name, flags ); GL_ProcessImage( tex, pic, filter ); if( !GL_UploadTexture( tex, pic )) { - memset( tex, 0, sizeof( gltexture_t )); + memset( tex, 0, sizeof( gl_texture_t )); FS_FreeImage( pic ); // release source texture return 0; } @@ -1399,13 +1460,8 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i GL_ApplyTextureParams( tex ); // update texture filter, wrap etc FS_FreeImage( pic ); // release source texture - // add to hash table - tex->hashValue = COM_HashKey( tex->name, TEXTURES_HASH_SIZE ); - tex->nextHash = r_texturesHashTable[tex->hashValue]; - r_texturesHashTable[tex->hashValue] = tex; - // NOTE: always return texnum as index in array or engine will stop work !!! - return i; + return tex - gl_textures; } /* @@ -1415,13 +1471,13 @@ GL_LoadTextureArray */ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) { - gltexture_t *tex; rgbdata_t *pic, *src; char basename[256]; uint numLayers = 0; uint picFlags = 0; char name[256]; - uint i, j, hash; + gl_texture_t *tex; + uint i, j; if( !names || !names[0] || !glw_state.initialized ) return 0; @@ -1443,20 +1499,12 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) Q_strncat( name, va( "[%i]", numLayers ), sizeof( name )); - if( Q_strlen( name ) >= sizeof( r_textures->name )) - { - Con_Printf( S_ERROR "LoadTextureArray: too long name %s (%d)\n", name, Q_strlen( name )); + if( !GL_CheckTexName( name )) return 0; - } // see if already loaded - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); - - for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) - { - if( !Q_stricmp( tex->name, name )) - return (tex - r_textures); - } + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); // load all the images and pack it into single image for( i = 0, pic = NULL; i < numLayers; i++ ) @@ -1471,20 +1519,20 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) // mixed mode: DXT + RGB if( pic->type != src->type ) { - MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch image format for %s and %s\n", names[0], names[i] ); + Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch image format for %s and %s\n", names[0], names[i] ); break; } // different mipcount if( pic->numMips != src->numMips ) { - MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch mip count for %s and %s\n", names[0], names[i] ); + Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch mip count for %s and %s\n", names[0], names[i] ); break; } if( pic->encode != src->encode ) { - MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch custom encoding for %s and %s\n", names[0], names[i] ); + Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch custom encoding for %s and %s\n", names[0], names[i] ); break; } @@ -1494,7 +1542,7 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) if( pic->size != src->size ) { - MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch image size for %s and %s\n", names[0], names[i] ); + Con_Printf( S_ERROR "GL_LoadTextureArray: mismatch image size for %s and %s\n", names[0], names[i] ); break; } } @@ -1513,8 +1561,8 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) for( j = 0; j < max( 1, pic->numMips ); j++ ) { - int width = max( 1, ( pic->width >> j )); - int height = max( 1, ( pic->height >> j )); + int width = Q_max( 1, ( pic->width >> j )); + int height = Q_max( 1, ( pic->height >> j )); mipsize = GL_CalcImageSize( pic->type, width, height, 1 ); memcpy( pic->buffer + dstsize + mipsize * i, src->buffer + srcsize, mipsize ); dstsize += mipsize * numLayers; @@ -1530,39 +1578,22 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) // there were errors if( !pic || ( pic->depth != numLayers )) { - MsgDev( D_ERROR, "GL_LoadTextureArray: not all layers were loaded. Texture array is not created\n" ); + Con_Printf( S_ERROR "GL_LoadTextureArray: not all layers were loaded. Texture array is not created\n" ); if( pic ) FS_FreeImage( pic ); return 0; } // it's multilayer image! - pic->flags |= IMAGE_MULTILAYER; + SetBits( pic->flags, IMAGE_MULTILAYER ); pic->size *= numLayers; - // find a free texture slot - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - - // find a free texture_t slot - for( i = 0, tex = r_textures; i < r_numTextures; i++, tex++ ) - if( !tex->name[0] ) break; - - if( i == r_numTextures ) - { - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - r_numTextures++; - } - - tex = &r_textures[i]; - Q_strncpy( tex->name, name, sizeof( tex->name )); - tex->flags = flags; - tex->texnum = i; // texnum is used for fast acess into r_textures array too - + // allocate the new one + tex = GL_AllocTexture( name, flags ); GL_ProcessImage( tex, pic, filter ); + if( !GL_UploadTexture( tex, pic )) { - memset( tex, 0, sizeof( gltexture_t )); + memset( tex, 0, sizeof( gl_texture_t )); FS_FreeImage( pic ); // release source texture return 0; } @@ -1570,98 +1601,50 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) GL_ApplyTextureParams( tex ); // update texture filter, wrap etc FS_FreeImage( pic ); // release source texture - // add to hash table - tex->hashValue = COM_HashKey( tex->name, TEXTURES_HASH_SIZE ); - tex->nextHash = r_texturesHashTable[tex->hashValue]; - r_texturesHashTable[tex->hashValue] = tex; - // NOTE: always return texnum as index in array or engine will stop work !!! - return i; + return tex - gl_textures; } /* ================ -GL_LoadTextureInternal +GL_LoadTextureFromBuffer ================ */ -int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ) +int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ) { - gltexture_t *tex; - uint i, hash; + gl_texture_t *tex; - if( !COM_CheckString( name ) || !glw_state.initialized ) + if( !GL_CheckTexName( name )) return 0; - if( Q_strlen( name ) >= sizeof( r_textures->name )) - { - Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, Q_strlen( name )); - return 0; - } - // see if already loaded - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); + if(( tex = GL_TextureForName( name )) && !update ) + return (tex - gl_textures); - for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) + // couldn't loading image + if( !pic ) return 0; + + if( update ) { - if( !Q_stricmp( tex->name, name )) - { - if( update ) break; - return (tex - r_textures); - } - } - - if( !pic ) return 0; // couldn't loading image - if( update && !tex ) - { - Host_Error( "Couldn't find texture %s for update\n", name ); - } - - // find a free texture slot - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - - if( !update ) - { - // find a free texture_t slot - for( i = 0, tex = r_textures; i < r_numTextures; i++, tex++ ) - if( !tex->name[0] ) break; - - if( i == r_numTextures ) - { - if( r_numTextures == MAX_TEXTURES ) - Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" ); - r_numTextures++; - } - - tex = &r_textures[i]; - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); - Q_strncpy( tex->name, name, sizeof( tex->name )); - tex->texnum = i; // texnum is used for fast acess into r_textures array too - tex->flags = flags; + if( tex == NULL ) + Host_Error( "GL_LoadTextureFromBuffer: couldn't find texture %s for update\n", name ); + SetBits( tex->flags, flags ); } else { - tex->flags |= flags; + // allocate the new one + tex = GL_AllocTexture( name, flags ); } GL_ProcessImage( tex, pic, NULL ); if( !GL_UploadTexture( tex, pic )) { - memset( tex, 0, sizeof( gltexture_t )); + memset( tex, 0, sizeof( gl_texture_t )); return 0; } GL_ApplyTextureParams( tex ); // update texture filter, wrap etc - - if( !update ) - { - // add to hash table - tex->hashValue = COM_HashKey( tex->name, TEXTURES_HASH_SIZE ); - tex->nextHash = r_texturesHashTable[tex->hashValue]; - r_texturesHashTable[tex->hashValue] = tex; - } - - return (tex - r_textures); + return (tex - gl_textures); } /* @@ -1673,43 +1656,40 @@ creates texture from buffer */ int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ) { + int datasize = 1; rgbdata_t r_empty; - int texture; + + if( FBitSet( flags, TF_ARB_16BIT )) + datasize = 2; + else if( FBitSet( flags, TF_ARB_FLOAT )) + datasize = 4; memset( &r_empty, 0, sizeof( r_empty )); r_empty.width = width; r_empty.height = height; r_empty.type = PF_RGBA_32; - r_empty.size = r_empty.width * r_empty.height * 4; - r_empty.flags = IMAGE_HAS_COLOR | (( flags & TF_HAS_ALPHA ) ? IMAGE_HAS_ALPHA : 0 ); + r_empty.size = r_empty.width * r_empty.height * datasize * 4; r_empty.buffer = (byte *)buffer; - if( FBitSet( flags, TF_ALPHACONTRAST )) - ClearBits( r_empty.flags, IMAGE_HAS_COLOR ); + // clear invalid combinations + ClearBits( flags, TF_TEXTURE_3D ); - if( FBitSet( flags, TF_TEXTURE_1D )) + // if image not luminance and not alphacontrast it will have color + if( !FBitSet( flags, TF_LUMINANCE ) && !FBitSet( flags, TF_ALPHACONTRAST )) + SetBits( r_empty.flags, IMAGE_HAS_COLOR ); + + if( FBitSet( flags, TF_HAS_ALPHA )) + SetBits( r_empty.flags, IMAGE_HAS_ALPHA ); + + if( FBitSet( flags, TF_CUBEMAP )) { - r_empty.height = 1; - r_empty.size = r_empty.width * 4; - } - else if( FBitSet( flags, TF_TEXTURE_3D )) - { - if( !GL_Support( GL_TEXTURE_3D_EXT )) + if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT )) return 0; - - r_empty.depth = r_empty.width; // assume 3D texture as cube - r_empty.size = r_empty.width * r_empty.height * r_empty.depth * 4; - } - else if( FBitSet( flags, TF_CUBEMAP )) - { SetBits( r_empty.flags, IMAGE_CUBEMAP ); - ClearBits( flags, TF_CUBEMAP ); // will be set later r_empty.size *= 6; } - texture = GL_LoadTextureInternal( name, &r_empty, flags, false ); - - return texture; + return GL_LoadTextureInternal( name, &r_empty, flags ); } /* @@ -1722,17 +1702,25 @@ creates texture array from buffer int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ) { rgbdata_t r_empty; - int texture; memset( &r_empty, 0, sizeof( r_empty )); - r_empty.width = width; - r_empty.height = height; - r_empty.depth = depth; + r_empty.width = Q_max( width, 1 ); + r_empty.height = Q_max( height, 1 ); + r_empty.depth = Q_max( depth, 1 ); r_empty.type = PF_RGBA_32; r_empty.size = r_empty.width * r_empty.height * r_empty.depth * 4; - r_empty.flags = IMAGE_HAS_COLOR | (( flags & TF_HAS_ALPHA ) ? IMAGE_HAS_ALPHA : 0 ); r_empty.buffer = (byte *)buffer; + // clear invalid combinations + ClearBits( flags, TF_CUBEMAP|TF_SKYSIDE|TF_HAS_LUMA|TF_MAKELUMA|TF_ALPHACONTRAST ); + + // if image not luminance it will have color + if( !FBitSet( flags, TF_LUMINANCE )) + SetBits( r_empty.flags, IMAGE_HAS_COLOR ); + + if( FBitSet( flags, TF_HAS_ALPHA )) + SetBits( r_empty.flags, IMAGE_HAS_ALPHA ); + if( FBitSet( flags, TF_TEXTURE_3D )) { if( !GL_Support( GL_TEXTURE_3D_EXT )) @@ -1745,9 +1733,55 @@ int GL_CreateTextureArray( const char *name, int width, int height, int depth, c SetBits( r_empty.flags, IMAGE_MULTILAYER ); } - texture = GL_LoadTextureInternal( name, &r_empty, flags, false ); + return GL_LoadTextureInternal( name, &r_empty, flags ); +} - return texture; +/* +================ +GL_FindTexture +================ +*/ +int GL_FindTexture( const char *name ) +{ + gl_texture_t *tex; + + if( !GL_CheckTexName( name )) + return 0; + + // see if already loaded + if(( tex = GL_TextureForName( name ))) + return (tex - gl_textures); + + return 0; +} + +/* +================ +GL_FreeImage + +Frees image by name +================ +*/ +void GL_FreeImage( const char *name ) +{ + int texnum; + + if(( texnum = GL_FindTexture( name )) != 0 ) + GL_FreeTexture( texnum ); +} + +/* +================ +GL_FreeTexture +================ +*/ +void GL_FreeTexture( GLenum texnum ) +{ + // number 0 it's already freed + if( texnum <= 0 || !glw_state.initialized ) + return; + + GL_DeleteTexture( &gl_textures[texnum] ); } /* @@ -1757,13 +1791,13 @@ GL_ProcessTexture */ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) { - gltexture_t *image; + gl_texture_t *image; rgbdata_t *pic; int flags = 0; - if( texnum <= 0 ) return; // missed image - Assert( texnum > 0 && texnum < MAX_TEXTURES ); - image = &r_textures[texnum]; + if( texnum <= 0 || texnum >= MAX_TEXTURES ) + return; // missed image + image = &gl_textures[texnum]; // select mode if( gamma != -1.0f ) @@ -1776,19 +1810,19 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) } else { - MsgDev( D_ERROR, "GL_ProcessTexture: bad operation for %s\n", image->name ); + Con_Printf( S_ERROR "GL_ProcessTexture: bad operation for %s\n", image->name ); return; } if( !image->original ) { - MsgDev( D_ERROR, "GL_ProcessTexture: no input data for %s\n", image->name ); + Con_Printf( S_ERROR "GL_ProcessTexture: no input data for %s\n", image->name ); return; } if( ImageDXT( image->original->type )) { - MsgDev( D_ERROR, "GL_ProcessTexture: can't process compressed texture %s\n", image->name ); + Con_Printf( S_ERROR "GL_ProcessTexture: can't process compressed texture %s\n", image->name ); return; } @@ -1802,129 +1836,6 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) FS_FreeImage( pic ); } -/* -================ -GL_LoadTexture -================ -*/ -int GL_FindTexture( const char *name ) -{ - gltexture_t *tex; - uint hash; - - if( !COM_CheckString( name ) || !glw_state.initialized ) - return 0; - - if( Q_strlen( name ) >= sizeof( r_textures->name )) - { - Con_Printf( S_ERROR "FindTexture: too long name %s (%d)\n", name, Q_strlen( name )); - return 0; - } - - // see if already loaded - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); - - for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) - { - if( !Q_stricmp( tex->name, name )) - return (tex - r_textures); - } - - return 0; -} - -/* -================ -GL_FreeImage - -Frees image by name -================ -*/ -void GL_FreeImage( const char *name ) -{ - gltexture_t *tex; - uint hash; - - if( !COM_CheckString( name ) || !glw_state.initialized ) - return; - - if( Q_strlen( name ) >= sizeof( r_textures->name )) - { - Con_Printf( S_ERROR "FreeTexture: too long name %s (%d)\n", name, Q_strlen( name )); - return; - } - - // see if already loaded - hash = COM_HashKey( name, TEXTURES_HASH_SIZE ); - - for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) - { - if( !Q_stricmp( tex->name, name )) - { - R_FreeImage( tex ); - return; - } - } -} - -/* -================ -GL_FreeTexture -================ -*/ -void GL_FreeTexture( GLenum texnum ) -{ - // number 0 it's already freed - if( texnum <= 0 || !glw_state.initialized ) - return; - - Assert( texnum > 0 && texnum < MAX_TEXTURES ); - R_FreeImage( &r_textures[texnum] ); -} - -/* -================ -R_FreeImage -================ -*/ -void R_FreeImage( gltexture_t *image ) -{ - gltexture_t *cur; - gltexture_t **prev; - - Assert( image != NULL ); - - if( !image->name[0] ) - { - if( image->texnum != 0 ) - MsgDev( D_ERROR, "trying to free unnamed texture with texnum %i\n", image->texnum ); - return; - } - - // remove from hash table - prev = &r_texturesHashTable[image->hashValue]; - - while( 1 ) - { - cur = *prev; - if( !cur ) break; - - if( cur == image ) - { - *prev = cur->nextHash; - break; - } - prev = &cur->nextHash; - } - - // release source - if( image->original ) - FS_FreeImage( image->original ); - - pglDeleteTextures( 1, &image->texnum ); - memset( image, 0, sizeof( *image )); -} - /* ============================================================================== @@ -1934,53 +1845,83 @@ INTERNAL TEXTURES */ /* ================== -R_InitDefaultTexture +GL_FakeImage ================== */ -static rgbdata_t *R_InitDefaultTexture( texFlags_t *flags ) +static rgbdata_t *GL_FakeImage( int width, int height, int depth, int flags ) { - int x, y; + static byte data2D[1024]; // 16x16x4 + static rgbdata_t r_image; // also use this for bad textures, but without alpha - r_image.width = r_image.height = 16; - r_image.buffer = data2D; - r_image.flags = IMAGE_HAS_COLOR; + r_image.width = Q_max( 1, width ); + r_image.height = Q_max( 1, height ); + r_image.depth = Q_max( 1, depth ); + r_image.flags = flags; r_image.type = PF_RGBA_32; - r_image.size = r_image.width * r_image.height * 4; + r_image.size = r_image.width * r_image.height * r_image.depth * 4; + r_image.buffer = (r_image.size > sizeof( data2D )) ? NULL : data2D; + r_image.palette = NULL; + r_image.numMips = 1; + r_image.encode = 0; - *flags = 0; + if( FBitSet( r_image.flags, IMAGE_CUBEMAP )) + r_image.size *= 6; + memset( data2D, 0xFF, sizeof( data2D )); - // emo-texture from quake1 - for( y = 0; y < 16; y++ ) - { - for( x = 0; x < 16; x++ ) - { - if(( y < 8 ) ^ ( x < 8 )) - ((uint *)&data2D)[y*16+x] = 0xFFFF00FF; - else ((uint *)&data2D)[y*16+x] = 0xFF000000; - } - } return &r_image; } /* ================== -R_InitParticleTexture +R_InitDlightTexture ================== */ -static rgbdata_t *R_InitParticleTexture( texFlags_t *flags ) +void R_InitDlightTexture( void ) { - int x, y; - int dx2, dy, d; + rgbdata_t r_image; - // particle texture - r_image.width = r_image.height = 16; - r_image.buffer = data2D; - r_image.flags = (IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA); + if( tr.dlightTexture != 0 ) + return; // already initialized + + memset( &r_image, 0, sizeof( r_image )); + r_image.width = BLOCK_SIZE; + r_image.height = BLOCK_SIZE; + r_image.flags = IMAGE_HAS_COLOR; r_image.type = PF_RGBA_32; r_image.size = r_image.width * r_image.height * 4; - *flags = TF_CLAMP; + tr.dlightTexture = GL_LoadTextureInternal( "*dlight", &r_image, TF_NOMIPMAP|TF_CLAMP|TF_ATLAS_PAGE ); +} + +/* +================== +GL_CreateInternalTextures +================== +*/ +static void GL_CreateInternalTextures( void ) +{ + int dx2, dy, d; + int x, y; + rgbdata_t *pic; + + // emo-texture from quake1 + pic = GL_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR ); + + for( y = 0; y < 16; y++ ) + { + for( x = 0; x < 16; x++ ) + { + if(( y < 8 ) ^ ( x < 8 )) + ((uint *)pic->buffer)[y*16+x] = 0xFFFF00FF; + else ((uint *)pic->buffer)[y*16+x] = 0xFF000000; + } + } + + tr.defaultTexture = GL_LoadTextureInternal( "*default", pic, TF_COLORMAP ); + + // particle texture from quake1 + pic = GL_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA ); for( x = 0; x < 16; x++ ) { @@ -1991,139 +1932,33 @@ static rgbdata_t *R_InitParticleTexture( texFlags_t *flags ) { dy = y - 8; d = 255 - 35 * sqrt( dx2 + dy * dy ); - data2D[( y*16 + x ) * 4 + 3] = bound( 0, d, 255 ); + pic->buffer[( y * 16 + x ) * 4 + 3] = bound( 0, d, 255 ); } } - return &r_image; -} -/* -================== -R_InitCinematicTexture -================== -*/ -static rgbdata_t *R_InitCinematicTexture( texFlags_t *flags ) -{ - r_image.type = PF_RGBA_32; - r_image.flags = IMAGE_HAS_COLOR; - r_image.width = 640; // same as menu head - r_image.height = 100; - r_image.size = r_image.width * r_image.height * 4; - r_image.buffer = NULL; + tr.particleTexture = GL_LoadTextureInternal( "*particle", pic, TF_CLAMP ); - *flags = TF_NOMIPMAP|TF_CLAMP; + // white texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFFFFFFFF; + tr.whiteTexture = GL_LoadTextureInternal( "*white", pic, TF_COLORMAP ); - return &r_image; -} + // gray texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFF7F7F7F; + tr.grayTexture = GL_LoadTextureInternal( "*gray", pic, TF_COLORMAP ); -/* -================== -R_InitSolidColorTexture -================== -*/ -static rgbdata_t *R_InitSolidColorTexture( texFlags_t *flags, int color ) -{ - // solid color texture - r_image.width = r_image.height = 1; - r_image.buffer = data2D; - r_image.flags = IMAGE_HAS_COLOR; - r_image.type = PF_RGB_24; - r_image.size = r_image.width * r_image.height * 3; + // black texture + pic = GL_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR ); + for( x = 0; x < 16; x++ ) + ((uint *)pic->buffer)[x] = 0xFF000000; + tr.blackTexture = GL_LoadTextureInternal( "*black", pic, TF_COLORMAP ); - *flags = 0; - - data2D[0] = data2D[1] = data2D[2] = color; - return &r_image; -} - -/* -================== -R_InitWhiteTexture -================== -*/ -static rgbdata_t *R_InitWhiteTexture( texFlags_t *flags ) -{ - return R_InitSolidColorTexture( flags, 255 ); -} - -/* -================== -R_InitGrayTexture -================== -*/ -static rgbdata_t *R_InitGrayTexture( texFlags_t *flags ) -{ - return R_InitSolidColorTexture( flags, 127 ); -} - -/* -================== -R_InitBlackTexture -================== -*/ -static rgbdata_t *R_InitBlackTexture( texFlags_t *flags ) -{ - return R_InitSolidColorTexture( flags, 0 ); -} - -/* -================== -R_InitDlightTexture -================== -*/ -void R_InitDlightTexture( void ) -{ - if( tr.dlightTexture != 0 ) - return; // already initialized - - r_image.width = BLOCK_SIZE; - r_image.height = BLOCK_SIZE; - r_image.flags = IMAGE_HAS_COLOR; - r_image.type = PF_RGBA_32; - r_image.size = r_image.width * r_image.height * 4; - r_image.buffer = NULL; - - tr.dlightTexture = GL_LoadTextureInternal( "*dlight", &r_image, TF_NOMIPMAP|TF_CLAMP|TF_ATLAS_PAGE, false ); -} - -/* -================== -R_InitBuiltinTextures -================== -*/ -static void R_InitBuiltinTextures( void ) -{ - rgbdata_t *pic; - texFlags_t flags; - - const struct - { - char *name; - int *texnum; - rgbdata_t *(*init)( texFlags_t *flags ); - } - - textures[] = - { - { "*default", &tr.defaultTexture, R_InitDefaultTexture }, - { "*particle", &tr.particleTexture, R_InitParticleTexture }, - { "*white", &tr.whiteTexture, R_InitWhiteTexture }, - { "*gray", &tr.grayTexture, R_InitGrayTexture }, - { "*black", &tr.blackTexture, R_InitBlackTexture }, // not used by engine - { "*cintexture", &tr.cinTexture, R_InitCinematicTexture }, // intermediate buffer to renderer cinematic textures - { NULL, NULL, NULL } - }; - size_t i, num_builtin_textures = ARRAYSIZE( textures ) - 1; - - for( i = 0; i < num_builtin_textures; i++ ) - { - memset( &r_image, 0, sizeof( rgbdata_t )); - memset( data2D, 0xFF, sizeof( data2D )); - - pic = textures[i].init( &flags ); - if( pic == NULL ) continue; - *textures[i].texnum = GL_LoadTextureInternal( textures[i].name, pic, flags, false ); - } + // cinematic dummy + pic = GL_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR ); + tr.cinTexture = GL_LoadTextureInternal( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP ); } /* @@ -2133,13 +1968,13 @@ R_TextureList_f */ void R_TextureList_f( void ) { - gltexture_t *image; + gl_texture_t *image; int i, texCount, bytes = 0; Con_Printf( "\n" ); Con_Printf( " -id- -w- -h- -size- -fmt- -type- -data- -encode- -wrap- -depth- -name--------\n" ); - for( i = texCount = 0, image = r_textures; i < r_numTextures; i++, image++ ) + for( i = texCount = 0, image = gl_textures; i < gl_numTextures; i++, image++ ) { if( !image->texnum ) continue; @@ -2182,6 +2017,9 @@ void R_TextureList_f( void ) case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: Con_Printf( "DXT5 " ); break; + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + Con_Printf( "ATI2 " ); + break; case GL_RGBA: Con_Printf( "RGBA " ); break; @@ -2332,32 +2170,20 @@ R_InitImages */ void R_InitImages( void ) { - float f; - uint i; - - memset( r_textures, 0, sizeof( r_textures )); - memset( r_texturesHashTable, 0, sizeof( r_texturesHashTable )); - r_numTextures = 0; + memset( gl_textures, 0, sizeof( gl_textures )); + memset( gl_texturesHashTable, 0, sizeof( gl_texturesHashTable )); + gl_numTextures = 0; // create unused 0-entry - Q_strncpy( r_textures->name, "*unused*", sizeof( r_textures->name )); - r_textures->hashValue = COM_HashKey( r_textures->name, TEXTURES_HASH_SIZE ); - r_textures->nextHash = r_texturesHashTable[r_textures->hashValue]; - r_texturesHashTable[r_textures->hashValue] = r_textures; - r_numTextures = 1; + Q_strncpy( gl_textures->name, "*unused*", sizeof( gl_textures->name )); + gl_textures->hashValue = COM_HashKey( gl_textures->name, TEXTURES_HASH_SIZE ); + gl_textures->nextHash = gl_texturesHashTable[gl_textures->hashValue]; + gl_texturesHashTable[gl_textures->hashValue] = gl_textures; + gl_numTextures = 1; - // build luminance table - for( i = 0; i < 256; i++ ) - { - f = (float)i; - r_luminanceTable[i][0] = f * 0.299f; - r_luminanceTable[i][1] = f * 0.587f; - r_luminanceTable[i][2] = f * 0.114f; - } - - // set texture parameters + // validate cvars R_SetTextureParameters(); - R_InitBuiltinTextures(); + GL_CreateInternalTextures(); R_ParseTexFilters( "scripts/texfilter.txt" ); Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); @@ -2370,19 +2196,17 @@ R_ShutdownImages */ void R_ShutdownImages( void ) { - gltexture_t *image; + gl_texture_t *tex; int i; - if( !glw_state.initialized ) return; - Cmd_RemoveCommand( "texturelist" ); GL_CleanupAllTextureUnits(); - for( i = 0, image = r_textures; i < r_numTextures; i++, image++ ) - R_FreeImage( image ); + for( i = 0, tex = gl_textures; i < gl_numTextures; i++, tex++ ) + GL_DeleteTexture( tex ); memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures )); - memset( r_texturesHashTable, 0, sizeof( r_texturesHashTable )); - memset( r_textures, 0, sizeof( r_textures )); - r_numTextures = 0; + memset( gl_texturesHashTable, 0, sizeof( gl_texturesHashTable )); + memset( gl_textures, 0, sizeof( gl_textures )); + gl_numTextures = 0; } \ No newline at end of file diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index a92b740a..3ec2c89d 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -93,7 +93,7 @@ typedef struct gltexture_s int servercount; uint hashValue; struct gltexture_s *nextHash; -} gltexture_t; +} gl_texture_t; typedef struct { @@ -306,16 +306,18 @@ void R_DrawModelHull( void ); // gl_image.c // void R_SetTextureParameters( void ); -gltexture_t *R_GetTexture( GLenum texnum ); +gl_texture_t *R_GetTexture( GLenum texnum ); +#define GL_LoadTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, false ) +#define GL_UpdateTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, true ) int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ); int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ); -int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); +int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap ); int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ); int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ); void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ); -void GL_ApplyTextureParams( gltexture_t *tex ); -void R_FreeImage( gltexture_t *image ); +void GL_UpdateTexSize( int texnum, int width, int height, int depth ); +void GL_ApplyTextureParams( gl_texture_t *tex ); int GL_FindTexture( const char *name ); void GL_FreeTexture( GLenum texnum ); void GL_FreeImage( const char *name ); @@ -452,6 +454,7 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse ); void GL_CheckForErrors_( const char *filename, const int fileline ); const char *VID_GetModeString( int vid_mode ); void *GL_GetProcAddress( const char *name ); +const char *GL_ErrorString( int err ); void GL_UpdateSwapInterval( void ); qboolean GL_DeleteContext( void ); qboolean GL_Support( int r_ext ); diff --git a/engine/client/gl_refrag.c b/engine/client/gl_refrag.c index 86e4d835..da73f149 100644 --- a/engine/client/gl_refrag.c +++ b/engine/client/gl_refrag.c @@ -140,6 +140,8 @@ R_AddEfrags */ void R_AddEfrags( cl_entity_t *ent ) { + matrix3x4 transform; + vec3_t outmins, outmaxs; int i; if( !ent->model ) @@ -149,10 +151,14 @@ void R_AddEfrags( cl_entity_t *ent ) lastlink = &ent->efrag; r_pefragtopnode = NULL; + // handle entity rotation for right bbox expanding + Matrix3x4_CreateFromEntity( transform, ent->angles, vec3_origin, 1.0f ); + Matrix3x4_TransformAABB( transform, ent->model->mins, ent->model->maxs, outmins, outmaxs ); + for( i = 0; i < 3; i++ ) { - r_emins[i] = ent->origin[i] + ent->model->mins[i]; - r_emaxs[i] = ent->origin[i] + ent->model->maxs[i]; + r_emins[i] = ent->origin[i] + outmins[i]; + r_emaxs[i] = ent->origin[i] + outmaxs[i]; } R_SplitEntityOnNode( cl.worldmodel->nodes ); @@ -189,6 +195,7 @@ void R_StoreEfrags( efrag_t **ppefrag, int framecount ) if( CL_AddVisibleEntity( pent, ET_FRAGMENTED )) { // mark that we've recorded this entity for this frame + pent->curstate.messagenum = cl.parsecount; pent->visframe = framecount; } } @@ -196,7 +203,6 @@ void R_StoreEfrags( efrag_t **ppefrag, int framecount ) ppefrag = &pefrag->leafnext; break; default: - Host_Error( "R_StoreEfrags: bad entity type %d\n", clmodel->type ); break; } } diff --git a/engine/client/gl_rlight.c b/engine/client/gl_rlight.c index 223abdf9..a60485de 100644 --- a/engine/client/gl_rlight.c +++ b/engine/client/gl_rlight.c @@ -74,7 +74,7 @@ void CL_RunLightStyles( void ) tr.lightstylevalue[i] = ls->map[0] * 22 * scale; continue; } - else if( !ls->interp || !cl_lightstyle_lerping->value ) + else if( !ls->interp || !CVAR_TO_BOOL( cl_lightstyle_lerping )) { tr.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale; continue; @@ -379,7 +379,7 @@ R_LightVec check bspmodels to get light from ================= */ -colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) { float last_fraction; int i, maxEnts = 1; @@ -437,6 +437,9 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t light.g = Q_min(( cv.g >> 7 ), 255 ); light.b = Q_min(( cv.b >> 7 ), 255 ); last_fraction = g_trace_fraction; + + if(( light.r + light.g + light.b ) != 0 ) + break; // we get light now } } } @@ -449,6 +452,27 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t return light; } +/* +================= +R_LightVec + +check bspmodels to get light from +================= +*/ +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +{ + colorVec light = R_LightVecInternal( start, end, lspot, lvec ); + + if( CVAR_TO_BOOL( r_lighting_extended ) && lspot != NULL && lvec != NULL ) + { + // trying to get light from ceiling (but ignore gradient analyze) + if(( light.r + light.g + light.b ) == 0 ) + return R_LightVecInternal( end, start, lspot, lvec ); + } + + return light; +} + /* ================= R_LightPoint @@ -463,4 +487,4 @@ colorVec R_LightPoint( const vec3_t p0 ) VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); return R_LightVec( p0, p1, NULL, NULL ); -} +} \ No newline at end of file diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index fa9b4380..c4b64023 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -579,9 +579,9 @@ using to find source waterleaf with watertexture to grab fog values from it ============= */ -static gltexture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) +static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) { - gltexture_t *tex = NULL; + gl_texture_t *tex = NULL; // assure the initial node is not null // we could check it here, but we would rather check it @@ -654,7 +654,7 @@ from underwater leaf (idea: XaeroX) static void R_CheckFog( void ) { cl_entity_t *ent; - gltexture_t *tex; + gl_texture_t *tex; int i, cnt, count; // quake global fog @@ -673,15 +673,6 @@ static void R_CheckFog( void ) return; } -#ifdef HACKS_RELATED_HLMODS - // special condition for Spirit 1.9 that used direct calls of glFog-functions - if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) - { - // fill the fog color from GL-state machine - pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); - RI.fogSkybox = true; - } -#endif RI.fogEnabled = false; if( RI.onlyClientDraw || cl.local.waterlevel < 3 || !RI.drawWorld || !RI.viewleaf ) @@ -751,6 +742,26 @@ static void R_CheckFog( void ) } } +/* +============= +R_CheckGLFog + +special condition for Spirit 1.9 +that used direct calls of glFog-functions +============= +*/ +static void R_CheckGLFog( void ) +{ +#ifdef HACKS_RELATED_HLMODS + if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) + { + // fill the fog color from GL-state machine + pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); + RI.fogSkybox = true; + } +#endif +} + /* ============= R_DrawFog @@ -937,7 +948,8 @@ void R_RenderScene( void ) R_MarkLeaves(); R_DrawFog (); - + + R_CheckGLFog(); R_DrawWorld(); R_CheckFog(); @@ -1085,17 +1097,8 @@ void R_RenderFrame( const ref_viewpass_t *rvp ) if( glConfig.max_multisamples > 1 && FBitSet( gl_msaa->flags, FCVAR_CHANGED )) { if( CVAR_TO_BOOL( gl_msaa )) - { pglEnable( GL_MULTISAMPLE_ARB ); - if( gl_msaa->value > 1.0f ) - pglEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); - else pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); - } - else - { - pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); - pglDisable( GL_MULTISAMPLE_ARB ); - } + else pglDisable( GL_MULTISAMPLE_ARB ); ClearBits( gl_msaa->flags, FCVAR_CHANGED ); } @@ -1164,7 +1167,7 @@ void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size ) static int GL_RenderGetParm( int parm, int arg ) { - gltexture_t *glt; + gl_texture_t *glt; switch( parm ) { @@ -1265,7 +1268,7 @@ static int GL_RenderGetParm( int parm, int arg ) static void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScale ) { - gltexture_t *glt = R_GetTexture( texture ); + gl_texture_t *glt = R_GetTexture( texture ); if( xScale ) *xScale = glt->xscale; if( yScale ) *yScale = glt->yscale; @@ -1273,7 +1276,7 @@ static void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScal static void R_GetExtraParmsForTexture( int texture, byte *red, byte *green, byte *blue, byte *density ) { - gltexture_t *glt = R_GetTexture( texture ); + gl_texture_t *glt = R_GetTexture( texture ); if( red ) *red = glt->fogParams[0]; if( green ) *green = glt->fogParams[1]; @@ -1291,16 +1294,13 @@ static void R_EnvShot( const float *vieworg, const char *name, int skyshot, int { static vec3_t viewPoint; - if( !name ) - { - MsgDev( D_ERROR, "R_%sShot: bad name\n", skyshot ? "Sky" : "Env" ); + if( !COM_CheckString( name )) return; - } if( cls.scrshot_action != scrshot_inactive ) { if( cls.scrshot_action != scrshot_skyshot && cls.scrshot_action != scrshot_envshot ) - MsgDev( D_ERROR, "R_%sShot: subsystem is busy, try later.\n", skyshot ? "Sky" : "Env" ); + Con_Printf( S_ERROR "R_%sShot: subsystem is busy, try for next frame.\n", skyshot ? "Sky" : "Env" ); return; } @@ -1500,7 +1500,7 @@ static render_api_t gRenderAPI = GL_TextureTarget, GL_SetTexCoordArrayMode, GL_GetProcAddress, - NULL, + GL_UpdateTexSize, NULL, NULL, CL_DrawParticlesExternal, @@ -1541,7 +1541,7 @@ qboolean R_InitRenderAPI( void ) { if( clgame.dllFuncs.pfnGetRenderInterface( CL_RENDER_INTERFACE_VERSION, &gRenderAPI, &clgame.drawFuncs )) { - MsgDev( D_REPORT, "CL_LoadProgs: ^2initailized extended RenderAPI ^7ver. %i\n", CL_RENDER_INTERFACE_VERSION ); + Con_Reportf( "CL_LoadProgs: ^2initailized extended RenderAPI ^7ver. %i\n", CL_RENDER_INTERFACE_VERSION ); return true; } diff --git a/engine/client/gl_rmath.c b/engine/client/gl_rmath.c index d21adfa3..30e591b4 100644 --- a/engine/client/gl_rmath.c +++ b/engine/client/gl_rmath.c @@ -27,12 +27,8 @@ float V_CalcFov( float *fov_x, float width, float height ) { float x, half_fov_y; - if( *fov_x < 1.0f || *fov_x > 170.0f ) - { - if( !cls.demoplayback ) - MsgDev( D_ERROR, "V_CalcFov: bad fov %g!\n", *fov_x ); - *fov_x = 90.0f; - } + if( *fov_x < 1.0f || *fov_x > 179.0f ) + *fov_x = 90.0f; // default value x = width / tan( DEG2RAD( *fov_x ) * 0.5f ); half_fov_y = atan( height / x ); diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index 96ef53c6..ee8cd302 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -19,15 +19,6 @@ GNU General Public License for more details. #include "mod_local.h" #include "shake.h" -typedef struct -{ - const char *texname; - const char *detail; - const char material; - int lMin; - int lMax; -} dmaterial_t; - typedef struct { char texname[64]; // shortname @@ -37,188 +28,6 @@ typedef struct dfilter_t *tex_filters[MAX_TEXTURES]; int num_texfilters; -// default rules for apply detail textures. -// maybe move this to external script? -static const dmaterial_t detail_table[] = -{ -{ "crt", "dt_conc", 'C', 0, 0 }, // concrete -{ "rock", "dt_rock1", 'C', 0, 0 }, -{ "conc", "dt_conc", 'C', 0, 0 }, -{ "brick", "dt_brick", 'C', 0, 0 }, -{ "wall", "dt_brick", 'C', 0, 0 }, -{ "city", "dt_conc", 'C', 0, 0 }, -{ "crete", "dt_conc", 'C', 0, 0 }, -{ "generic", "dt_brick", 'C', 0, 0 }, -{ "floor", "dt_conc", 'C', 0, 0 }, -{ "metal", "dt_metal%i", 'M', 1, 2 }, // metal -{ "mtl", "dt_metal%i", 'M', 1, 2 }, -{ "pipe", "dt_metal%i", 'M', 1, 2 }, -{ "elev", "dt_metal%i", 'M', 1, 2 }, -{ "sign", "dt_metal%i", 'M', 1, 2 }, -{ "barrel", "dt_metal%i", 'M', 1, 2 }, -{ "bath", "dt_ssteel1", 'M', 1, 2 }, -{ "tech", "dt_ssteel1", 'M', 1, 2 }, -{ "refbridge", "dt_metal%i", 'M', 1, 2 }, -{ "panel", "dt_ssteel1", 'M', 0, 0 }, -{ "brass", "dt_ssteel1", 'M', 0, 0 }, -{ "rune", "dt_metal%i", 'M', 1, 2 }, -{ "car", "dt_metal%i", 'M', 1, 2 }, -{ "circuit", "dt_metal%i", 'M', 1, 2 }, -{ "steel", "dt_ssteel1", 'M', 0, 0 }, -{ "dirt", "dt_ground%i", 'D', 1, 5 }, // dirt -{ "drt", "dt_ground%i", 'D', 1, 5 }, -{ "out", "dt_ground%i", 'D', 1, 5 }, -{ "grass", "dt_grass1", 'D', 0, 0 }, -{ "mud", "dt_carpet1", 'D', 0, 0 }, -{ "vent", "dt_ssteel1", 'V', 1, 4 }, // vent -{ "duct", "dt_ssteel1", 'V', 1, 4 }, -{ "tile", "dt_smooth%i", 'T', 1, 2 }, -{ "labflr", "dt_smooth%i", 'T', 1, 2 }, -{ "bath", "dt_smooth%i", 'T', 1, 2 }, -{ "grate", "dt_stone%i", 'G', 1, 4 }, // vent -{ "stone", "dt_stone%i", 'G', 1, 4 }, -{ "grt", "dt_stone%i", 'G', 1, 4 }, -{ "wiz", "dt_wood%i", 'W', 1, 3 }, -{ "wood", "dt_wood%i", 'W', 1, 3 }, -{ "wizwood", "dt_wood%i", 'W', 1, 3 }, -{ "wd", "dt_wood%i", 'W', 1, 3 }, -{ "table", "dt_wood%i", 'W', 1, 3 }, -{ "board", "dt_wood%i", 'W', 1, 3 }, -{ "chair", "dt_wood%i", 'W', 1, 3 }, -{ "brd", "dt_wood%i", 'W', 1, 3 }, -{ "carp", "dt_carpet1", 'W', 1, 3 }, -{ "book", "dt_wood%i", 'W', 1, 3 }, -{ "box", "dt_wood%i", 'W', 1, 3 }, -{ "cab", "dt_wood%i", 'W', 1, 3 }, -{ "couch", "dt_wood%i", 'W', 1, 3 }, -{ "crate", "dt_wood%i", 'W', 1, 3 }, -{ "poster", "dt_plaster%i", 'W', 1, 2 }, -{ "sheet", "dt_plaster%i", 'W', 1, 2 }, -{ "stucco", "dt_plaster%i", 'W', 1, 2 }, -{ "comp", "dt_smooth1", 'P', 0, 0 }, -{ "cmp", "dt_smooth1", 'P', 0, 0 }, -{ "elec", "dt_smooth1", 'P', 0, 0 }, -{ "vend", "dt_smooth1", 'P', 0, 0 }, -{ "monitor", "dt_smooth1", 'P', 0, 0 }, -{ "phone", "dt_smooth1", 'P', 0, 0 }, -{ "glass", "dt_ssteel1", 'Y', 0, 0 }, -{ "window", "dt_ssteel1", 'Y', 0, 0 }, -{ "flesh", "dt_rough1", 'F', 0, 0 }, -{ "meat", "dt_rough1", 'F', 0, 0 }, -{ "fls", "dt_rough1", 'F', 0, 0 }, -{ "ground", "dt_ground%i", 'D', 1, 5 }, -{ "gnd", "dt_ground%i", 'D', 1, 5 }, -{ "snow", "dt_snow%i", 'O', 1, 2 }, // snow -{ "wswamp", "dt_smooth1", 'W', 0, 0 }, -{ NULL, NULL, 0, 0, 0 } -}; - -static const char *R_DetailTextureForName( const char *name ) -{ - const dmaterial_t *table; - - if( !name || !*name ) return NULL; - if( !Q_strnicmp( name, "sky", 3 )) - return NULL; // never details for sky - - // never apply details for liquids - if( !Q_strnicmp( name + 1, "!lava", 5 )) - return NULL; - if( !Q_strnicmp( name + 1, "!slime", 6 )) - return NULL; - if( !Q_strnicmp( name, "!cur_90", 7 )) - return NULL; - if( !Q_strnicmp( name, "!cur_0", 6 )) - return NULL; - if( !Q_strnicmp( name, "!cur_270", 8 )) - return NULL; - if( !Q_strnicmp( name, "!cur_180", 8 )) - return NULL; - if( !Q_strnicmp( name, "!cur_up", 7 )) - return NULL; - if( !Q_strnicmp( name, "!cur_dwn", 8 )) - return NULL; - if( name[0] == '!' ) - return NULL; - - // never apply details to the special textures - if( !Q_strnicmp( name, "origin", 6 )) - return NULL; - if( !Q_strnicmp( name, "clip", 4 )) - return NULL; - if( !Q_strnicmp( name, "hint", 4 )) - return NULL; - if( !Q_strnicmp( name, "skip", 4 )) - return NULL; - if( !Q_strnicmp( name, "translucent", 11 )) - return NULL; - if( !Q_strnicmp( name, "3dsky", 5 )) // xash-mod support :-) - return NULL; - if( !Q_strnicmp( name, "scroll", 6 )) - return NULL; - if( name[0] == '@' ) - return NULL; - - // last check ... - if( !Q_strnicmp( name, "null", 4 )) - return NULL; - - for( table = detail_table; table && table->texname; table++ ) - { - if( Q_stristr( name, table->texname )) - { - if(( table->lMin + table->lMax ) > 0 ) - return va( table->detail, COM_RandomLong( table->lMin, table->lMax )); - return table->detail; - } - } - - return NULL; -} - -void R_CreateDetailTexturesList( const char *filename ) -{ - file_t *detail_txt = NULL; - float xScale, yScale; - const char *detail_name; - texture_t *tex; - rgbdata_t *pic; - int i; - - for( i = 0; i < cl.worldmodel->numtextures; i++ ) - { - tex = cl.worldmodel->textures[i]; - detail_name = R_DetailTextureForName( tex->name ); - if( !detail_name ) continue; - - // detailtexture detected - if( detail_name ) - { - if( !detail_txt ) detail_txt = FS_Open( filename, "w", false ); - if( !detail_txt ) - { - MsgDev( D_ERROR, "Can't write %s\n", filename ); - break; - } - - pic = FS_LoadImage( va( "gfx/detail/%s", detail_name ), NULL, 0 ); - - if( pic ) - { - xScale = (pic->width / (float)tex->width) * gl_detailscale->value; - yScale = (pic->height / (float)tex->height) * gl_detailscale->value; - FS_FreeImage( pic ); - } - else xScale = yScale = 10.0f; - - // store detailtexture description - FS_Printf( detail_txt, "%s detail/%s %.2f %.2f\n", tex->name, detail_name, xScale, yScale ); - } - } - - if( detail_txt ) FS_Close( detail_txt ); -} - void R_ParseDetailTextures( const char *filename ) { char *afile, *pfile; @@ -229,12 +38,6 @@ void R_ParseDetailTextures( const char *filename ) texture_t *tex; int i; - if( r_detailtextures->value >= 2 && !FS_FileExists( filename, false )) - { - // use built-in generator for detail textures - R_CreateDetailTexturesList( filename ); - } - afile = FS_LoadFile( filename, NULL, false ); if( !afile ) return; @@ -297,7 +100,7 @@ void R_ParseDetailTextures( const char *filename ) // texture is loaded if( tex->dt_texturenum ) { - gltexture_t *glt; + gl_texture_t *glt; glt = R_GetTexture( tex->gl_texturenum ); glt->xscale = xScale; @@ -363,7 +166,7 @@ void R_ParseTexFilters( const char *filename ) filter.blendFunc = GL_BLEND; else if( !Q_stricmp( token, "add_signed" ) || !Q_stricmp( token, "GL_ADD_SIGNED" )) filter.blendFunc = GL_ADD_SIGNED; - else MsgDev( D_WARN, "unknown blendFunc '%s' specified for texture '%s'\n", texname, token ); + else filter.blendFunc = GL_REPLACE; // defaulting to replace // reading flags pfile = COM_ParseFile( pfile, token ); @@ -371,10 +174,7 @@ void R_ParseTexFilters( const char *filename ) // make sure what factor is not zeroed if( filter.factor == 0.0f ) - { - MsgDev( D_WARN, "texfilter for texture %s has factor 0! Ignored\n", texname ); continue; - } // check if already existed for( i = 0; i < num_texfilters; i++ ) @@ -382,10 +182,7 @@ void R_ParseTexFilters( const char *filename ) tf = tex_filters[i]; if( !Q_stricmp( tf->texname, texname )) - { - MsgDev( D_WARN, "texture %s has specified multiple filters! Ignored\n", texname ); break; - } } if( i != num_texfilters ) @@ -399,7 +196,7 @@ void R_ParseTexFilters( const char *filename ) tf->filter = filter; } - MsgDev( D_INFO, "%i texture filters parsed\n", num_texfilters ); + Con_Reportf( "%i texture filters parsed\n", num_texfilters ); Mem_Free( afile ); } @@ -451,7 +248,7 @@ void R_NewMap( void ) R_ClearDecals(); // clear all level decals // upload detailtextures - if( r_detailtextures->value ) + if( CVAR_TO_BOOL( r_detailtextures )) { string mapname, filepath; @@ -462,7 +259,7 @@ void R_NewMap( void ) R_ParseDetailTextures( filepath ); } - if( v_dark->value ) + if( CVAR_TO_BOOL( v_dark )) { screenfade_t *sf = &clgame.fade; float fadetime = 5.0f; @@ -505,7 +302,7 @@ void R_NewMap( void ) tx = cl.worldmodel->textures[i]; - if( !Q_strncmp( tx->name, "sky", 3 ) && tx->width == 256 && tx->height == 128 ) + if( !Q_strncmp( tx->name, "sky", 3 ) && tx->width == ( tx->height * 2 )) tr.skytexturenum = i; tx->texturechain = NULL; diff --git a/engine/client/gl_rpart.c b/engine/client/gl_rpart.c index cb46866d..a00e9322 100644 --- a/engine/client/gl_rpart.c +++ b/engine/client/gl_rpart.c @@ -225,7 +225,7 @@ particle_t *R_AllocParticle( void (*callback)( particle_t*, float )) if( cl_lasttimewarn < host.realtime ) { // don't spam about overflow - MsgDev( D_ERROR, "Overflow %d particles\n", GI->max_particles ); + Con_DPrintf( S_ERROR "Overflow %d particles\n", GI->max_particles ); cl_lasttimewarn = host.realtime + 1.0f; } return NULL; @@ -276,7 +276,7 @@ particle_t *R_AllocTracer( const vec3_t org, const vec3_t vel, float life ) if( cl_lasttimewarn < host.realtime ) { // don't spam about overflow - MsgDev( D_ERROR, "Overflow %d tracers\n", GI->max_particles ); + Con_DPrintf( S_ERROR "Overflow %d tracers\n", GI->max_particles ); cl_lasttimewarn = host.realtime + 1.0f; } return NULL; @@ -1501,14 +1501,11 @@ void R_UserTracerParticle( float *org, float *vel, float life, int colorIndex, f particle_t *p; if( colorIndex < 0 ) - { - MsgDev( D_ERROR, "UserTracer with color < 0\n" ); return; - } if( colorIndex > ARRAYSIZE( gTracerColors )) { - MsgDev( D_ERROR, "UserTracer with color > %d\n", ARRAYSIZE( gTracerColors )); + Con_Printf( S_ERROR "UserTracer with color > %d\n", ARRAYSIZE( gTracerColors )); return; } diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 9feef4cc..7b69ae21 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -273,9 +273,8 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) medge_t *pedges, *r_pedge; mextrasurf_t *info = fa->info; float sample_size; - int vertpage; texture_t *tex; - gltexture_t *glt; + gl_texture_t *glt; float *vec; float s, t; glpoly_t *poly; @@ -299,7 +298,6 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) // reconstruct the polygon pedges = mod->edges; lnumverts = fa->numedges; - vertpage = 0; // detach if already created, reconstruct again poly = fa->polys; @@ -341,13 +339,13 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) s = DotProduct( vec, info->lmvecs[0] ) + info->lmvecs[0][3]; s -= info->lightmapmins[0]; s += fa->light_s * sample_size; - s += sample_size / 2.0; + s += sample_size * 0.5f; s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width; t = DotProduct( vec, info->lmvecs[1] ) + info->lmvecs[1][3]; t -= info->lightmapmins[1]; t += fa->light_t * sample_size; - t += sample_size / 2.0; + t += sample_size * 0.5f; t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height; poly->verts[i][5] = s; @@ -355,7 +353,7 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa ) } // remove co-linear points - Ed - if( !gl_keeptjunctions->value && !( fa->flags & SURF_UNDERWATER )) + if( !CVAR_TO_BOOL( gl_keeptjunctions ) && !FBitSet( fa->flags, SURF_UNDERWATER )) { for( i = 0; i < lnumverts; i++ ) { @@ -439,17 +437,8 @@ texture_t *R_TextureAnimation( msurface_t *s ) { base = base->anim_next; - if( !base ) - { - MsgDev( D_ERROR, "R_TextureAnimation: broken loop\n" ); + if( !base || ++count > MOD_FRAMES ) return s->texinfo->texture; - } - - if( ++count > MOD_FRAMES ) - { - MsgDev( D_ERROR, "R_TextureAnimation: infinite loop\n" ); - return s->texinfo->texture; - } } return base; @@ -645,7 +634,7 @@ static void LM_UploadBlock( qboolean dynamic ) r_lightmap.size = r_lightmap.width * r_lightmap.height * 4; r_lightmap.flags = IMAGE_HAS_COLOR; r_lightmap.buffer = gl_lms.lightmap_buffer; - tr.lightmapTextures[i] = GL_LoadTextureInternal( lmName, &r_lightmap, TF_FONT|TF_ATLAS_PAGE, false ); + tr.lightmapTextures[i] = GL_LoadTextureInternal( lmName, &r_lightmap, TF_FONT|TF_ATLAS_PAGE ); if( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) Host_Error( "AllocBlock: full\n" ); @@ -703,9 +692,9 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean { for( s = 0; s < smax; s++ ) { - dest[0] = min((bl[0] >> 7), 255 ); - dest[1] = min((bl[1] >> 7), 255 ); - dest[2] = min((bl[2] >> 7), 255 ); + dest[0] = Q_min((bl[0] >> 7), 255 ); + dest[1] = Q_min((bl[1] >> 7), 255 ); + dest[2] = Q_min((bl[2] >> 7), 255 ); dest[3] = 255; bl += 3; @@ -734,7 +723,7 @@ void DrawGLPoly( glpoly_t *p, float xScale, float yScale ) if( p->flags & SURF_CONVEYOR ) { - gltexture_t *texture; + gl_texture_t *texture; float flConveyorSpeed; float flRate, flAngle; @@ -824,7 +813,7 @@ void R_BlendLightmaps( void ) msurface_t *surf, *newsurf = NULL; int i; - if( r_fullbright->value || !cl.worldmodel->lightdata ) + if( CVAR_TO_BOOL( r_fullbright ) || !cl.worldmodel->lightdata ) return; if( RI.currententity ) @@ -845,7 +834,7 @@ void R_BlendLightmaps( void ) GL_SetupFogColorForSurfaces (); - if( !r_lightmap->value ) + if( !CVAR_TO_BOOL( r_lightmap )) pglEnable( GL_BLEND ); else pglDisable( GL_BLEND ); @@ -872,7 +861,7 @@ void R_BlendLightmaps( void ) } // render dynamic lightmaps - if( r_dynamic->value ) + if( CVAR_TO_BOOL( r_dynamic )) { LM_InitBlock(); @@ -1005,7 +994,7 @@ R_RenderDetails */ void R_RenderDetails( void ) { - gltexture_t *glt; + gl_texture_t *glt; mextrasurf_t *es, *p; msurface_t *fa; int i; @@ -1082,7 +1071,7 @@ void R_RenderBrushPoly( msurface_t *fa, int cull_type ) draw_fullbrights = true; } - if( r_detailtextures->value ) + if( CVAR_TO_BOOL( r_detailtextures )) { if( pglIsEnabled( GL_FOG )) { @@ -1125,7 +1114,7 @@ void R_RenderBrushPoly( msurface_t *fa, int cull_type ) DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE)); } - if( fa->flags & SURF_DRAWTILED ) + if( FBitSet( fa->flags, SURF_DRAWTILED )) return; // no lightmaps anyway // check for lightmap modification @@ -1543,7 +1532,7 @@ void R_DrawBrushModel( cl_entity_t *e ) } // sort faces if needs - if( !FBitSet( clmodel->flags, MODEL_LIQUID ) && e->curstate.rendermode == kRenderTransTexture && !gl_nosort->value ) + if( !FBitSet( clmodel->flags, MODEL_LIQUID ) && e->curstate.rendermode == kRenderTransTexture && !CVAR_TO_BOOL( gl_nosort )) qsort( world.draw_surfaces, num_sorted, sizeof( sortedface_t ), R_SurfaceCompare ); // draw sorted translucent surfaces @@ -2180,9 +2169,6 @@ void GL_BuildLightmaps( void ) // now gamma and brightness are valid ClearBits( vid_brightness->flags, FCVAR_CHANGED ); ClearBits( vid_gamma->flags, FCVAR_CHANGED ); - - if( !gl_keeptjunctions->value ) - MsgDev( D_INFO, "Eliminate %i vertexes\n", nColinElim ); } void GL_InitRandomTable( void ) diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index 5532400e..23b05534 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -400,7 +400,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean pspriteframe->left = -( w >> 1 ); pspriteframe->down = ( h >> 1 ) - h; pspriteframe->right = w + -( w >> 1 ); - pspriteframe->gl_texturenum = GL_LoadTextureInternal( texname, &temp, TF_IMAGE, false ); + pspriteframe->gl_texturenum = GL_LoadTextureInternal( texname, &temp, TF_IMAGE ); xl += w; if( xl >= pix->width ) @@ -493,7 +493,7 @@ mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ) else if( frame >= psprite->numframes ) { if( frame > psprite->numframes ) - MsgDev( D_WARN, "R_GetSpriteFrame: no such frame %d (%s)\n", frame, pModel->name ); + Con_Reportf( S_WARN "R_GetSpriteFrame: no such frame %d (%s)\n", frame, pModel->name ); frame = psprite->numframes - 1; } @@ -561,7 +561,7 @@ float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe, } else if( frame >= psprite->numframes ) { - MsgDev( D_WARN, "R_GetSpriteFrameInterpolant: no such frame %d (%s)\n", frame, ent->model->name ); + Con_Reportf( S_WARN "R_GetSpriteFrameInterpolant: no such frame %d (%s)\n", frame, ent->model->name ); frame = psprite->numframes - 1; } diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 58216213..38e4ee70 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -560,7 +560,7 @@ void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t an // Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_studio.time ); VectorLerp( e->latched.prevorigin, f, e->curstate.origin, origin ); - if( !VectorCompare( e->curstate.angles, e->latched.prevangles )) + if( !VectorCompareEpsilon( e->curstate.angles, e->latched.prevangles, ON_EPSILON )) { vec4_t q, q1, q2; @@ -2417,6 +2417,7 @@ static void R_StudioDrawPoints( void ) pglBlendFunc( GL_ONE, GL_ONE ); pglDepthMask( GL_FALSE ); pglEnable( GL_BLEND ); + R_AllowFog( false ); } else pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); } @@ -2438,6 +2439,7 @@ static void R_StudioDrawPoints( void ) { pglDepthMask( GL_TRUE ); pglDisable( GL_BLEND ); + R_AllowFog( true ); } r_stats.c_studio_polys += pmesh->numtris; @@ -2718,11 +2720,11 @@ check for texture flags */ int R_GetEntityRenderMode( cl_entity_t *ent ) { - studiohdr_t *phdr; + int i, opaque, trans; mstudiotexture_t *ptexture; cl_entity_t *oldent; model_t *model; - int i; + studiohdr_t *phdr; oldent = RI.currententity; RI.currententity = ent; @@ -2735,21 +2737,27 @@ int R_GetEntityRenderMode( cl_entity_t *ent ) if(( phdr = Mod_StudioExtradata( model )) == NULL ) { - // forcing to choose right sorting type - if(( model && model->type == mod_brush ) && FBitSet( model->flags, MODEL_TRANSPARENT )) - return kRenderTransAlpha; + if( R_ModelOpaque( ent->curstate.rendermode )) + { + // forcing to choose right sorting type + if(( model && model->type == mod_brush ) && FBitSet( model->flags, MODEL_TRANSPARENT )) + return kRenderTransAlpha; + } return ent->curstate.rendermode; } ptexture = (mstudiotexture_t *)((byte *)phdr + phdr->textureindex); - for( i = 0; i < phdr->numtextures; i++, ptexture++ ) + for( opaque = trans = i = 0; i < phdr->numtextures; i++, ptexture++ ) { - // g-cont. this is not fully proper but better than was - if( FBitSet( ptexture->flags, STUDIO_NF_ADDITIVE )) - return kRenderTransAdd; -// if( FBitSet( ptexture->flags, STUDIO_NF_MASKED )) -// return kRenderTransAlpha; + // ignore chrome & additive it's just a specular-like effect + if( FBitSet( ptexture->flags, STUDIO_NF_ADDITIVE ) && !FBitSet( ptexture->flags, STUDIO_NF_CHROME )) + trans++; + else opaque++; } + + // if model is more additive than opaque + if( trans > opaque ) + return kRenderTransAdd; return ent->curstate.rendermode; } @@ -3698,10 +3706,6 @@ void R_DrawViewModel( void ) pglFrontFace( GL_CW ); } - // FIXME: viewmodel is invisible when alpha to coverage is enabled - if( glConfig.max_multisamples > 1 && gl_msaa->value > 1.0f ) - pglDisable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); - switch( RI.currententity->model->type ) { case mod_alias: @@ -3713,9 +3717,6 @@ void R_DrawViewModel( void ) break; } - if( glConfig.max_multisamples > 1 && gl_msaa->value > 1.0f ) - pglEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB ); - // restore depth range pglDepthRange( gldepthmin, gldepthmax ); diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 55e7693c..42212239 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -26,8 +26,7 @@ GNU General Public License for more details. #define WINDOW_STYLE (WS_OVERLAPPED|WS_BORDER|WS_SYSMENU|WS_CAPTION|WS_VISIBLE) #define WINDOW_EX_STYLE (0) #define WINDOW_NAME "Xash3D Window" // Half-Life -#define FCONTEXT_CORE_PROFILE BIT( 0 ) -#define FCONTEXT_DEBUG_ARB BIT( 1 ) +#define FCONTEXT_DEBUG_ARB BIT( 0 ) convar_t *gl_extensions; convar_t *gl_texture_anisotropy; @@ -535,9 +534,6 @@ static void GL_SetDefaultState( void ) if( Sys_CheckParm( "-gldebug" )) SetBits( context_flags, FCONTEXT_DEBUG_ARB ); - if( Sys_CheckParm( "-glcore" )) - SetBits( context_flags, FCONTEXT_CORE_PROFILE ); - // init draw stack tr.draw_list = &tr.draw_stack[0]; tr.draw_stack_pos = 0; @@ -593,9 +589,7 @@ qboolean GL_CreateContext( void ) pwglCreateContextAttribsARB = GL_GetProcAddress( "wglCreateContextAttribsARB" ); - if( FBitSet( context_flags, FCONTEXT_CORE_PROFILE )) - profile_mask = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - else profile_mask = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + profile_mask = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; if( FBitSet( context_flags, FCONTEXT_DEBUG_ARB )) arb_flags = WGL_CONTEXT_DEBUG_BIT_ARB; @@ -1021,6 +1015,8 @@ void R_SaveVideoMode( int vid_mode ) glState.width = vidmode[mode].width; glState.height = vidmode[mode].height; glState.wideScreen = vidmode[mode].wideScreen; + Cvar_FullSet( "width", va( "%i", glState.width ), FCVAR_READ_ONLY ); + Cvar_FullSet( "height", va( "%i", glState.height ), FCVAR_READ_ONLY ); Cvar_SetValue( "vid_mode", mode ); // merge if it out of bounds MsgDev( D_NOTE, "Set: %s [%dx%d]\n", vidmode[mode].desc, vidmode[mode].width, vidmode[mode].height ); @@ -1585,7 +1581,7 @@ void GL_InitCommands( void ) r_speeds = Cvar_Get( "r_speeds", "0", FCVAR_ARCHIVE, "shows renderer speeds" ); r_fullbright = Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); r_norefresh = Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); - r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", FCVAR_ARCHIVE, "allow to get lighting from world and bmodels" ); + r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", FCVAR_ARCHIVE, "allow to get lighting from bmodels too" ); r_lighting_modulate = Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "lightstyles modulate scale" ); r_lighting_ambient = Cvar_Get( "r_lighting_ambient", "0.3", FCVAR_ARCHIVE, "map ambient lighting scale" ); r_adjust_fov = Cvar_Get( "r_adjust_fov", "1", FCVAR_ARCHIVE, "making FOV adjustment for wide-screens" ); @@ -1619,7 +1615,7 @@ void GL_InitCommands( void ) gl_test = Cvar_Get( "gl_test", "0", 0, "engine developer cvar for quick testing new features" ); gl_wireframe = Cvar_Get( "gl_wireframe", "0", FCVAR_ARCHIVE|FCVAR_SPONLY, "show wireframe overlay" ); gl_round_down = Cvar_Get( "gl_round_down", "2", FCVAR_RENDERINFO, "round texture sizes to nearest POT value" ); - gl_msaa = Cvar_Get( "gl_msaa", "2", FCVAR_ARCHIVE, "enable multi sample anti-aliasing" ); + gl_msaa = Cvar_Get( "gl_msaa", "1", FCVAR_ARCHIVE, "enable multi sample anti-aliasing" ); // these cvar not used by engine but some mods requires this gl_polyoffset = Cvar_Get( "gl_polyoffset", "2.0", FCVAR_ARCHIVE, "polygon offset for decals" ); @@ -1906,6 +1902,35 @@ void R_Shutdown( void ) R_Free_OpenGL(); } + +/* +================= +GL_ErrorString + +convert errorcode to string +================= +*/ +const char *GL_ErrorString( int err ) +{ + switch( err ) + { + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "UNKNOWN ERROR"; + } +} + /* ================= GL_CheckForErrors @@ -1916,38 +1941,12 @@ obsolete void GL_CheckForErrors_( const char *filename, const int fileline ) { int err; - char *str; - if( !gl_check_errors->value ) + if( !CVAR_TO_BOOL( gl_check_errors )) return; if(( err = pglGetError( )) == GL_NO_ERROR ) return; - switch( err ) - { - case GL_STACK_OVERFLOW: - str = "GL_STACK_OVERFLOW"; - break; - case GL_STACK_UNDERFLOW: - str = "GL_STACK_UNDERFLOW"; - break; - case GL_INVALID_ENUM: - str = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - str = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - str = "GL_INVALID_OPERATION"; - break; - case GL_OUT_OF_MEMORY: - str = "GL_OUT_OF_MEMORY"; - break; - default: - str = "UNKNOWN ERROR"; - break; - } - - Con_Printf( S_OPENGL_ERROR "%s (called at %s:%i)\n", str, filename, fileline ); + Con_Printf( S_OPENGL_ERROR "%s (called at %s:%i)\n", GL_ErrorString( err ), filename, fileline ); } \ No newline at end of file diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index 15a6ed77..bb91e4a5 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -21,7 +21,7 @@ GNU General Public License for more details. #define SKYCLOUDS_QUALITY 12 #define MAX_CLIP_VERTS 128 // skybox clip vertices #define TURBSCALE ( 256.0f / ( M_PI2 )) -static const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; +const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; static const int r_skyTexOrder[6] = { 0, 2, 1, 3, 4, 5 }; static const vec3_t skyclip[6] = @@ -672,7 +672,7 @@ void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) // make sure what sky image is valid if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 || r_sky->height == 0 ) { - MsgDev( D_ERROR, "R_InitSky: unable to load sky texture %s\n", tx->name ); + Con_Reportf( S_ERROR "R_InitSky: unable to load sky texture %s\n", tx->name ); if( r_sky ) FS_FreeImage( r_sky ); return; } @@ -711,7 +711,7 @@ void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) r_temp.palette = NULL; // load it in - tr.solidskyTexture = GL_LoadTextureInternal( "solid_sky", &r_temp, TF_NOMIPMAP, false ); + tr.solidskyTexture = GL_LoadTextureInternal( "solid_sky", &r_temp, TF_NOMIPMAP ); for( i = 0; i < r_sky->width >> 1; i++ ) { @@ -734,7 +734,7 @@ void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) r_temp.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA; // load it in - tr.alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", &r_temp, TF_NOMIPMAP, false ); + tr.alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", &r_temp, TF_NOMIPMAP ); // clean up FS_FreeImage( r_sky ); diff --git a/engine/client/s_dsp.c b/engine/client/s_dsp.c index b6144719..07ca7aee 100644 --- a/engine/client/s_dsp.c +++ b/engine/client/s_dsp.c @@ -33,7 +33,7 @@ GNU General Public License for more details. #define MAXDLY (STEREODLY + 1) #define MAXLP 10 -#define MAXPRESETS ARRAYSIZE( rgsxpre ) +#define MAXPRESETS 29 typedef struct sx_preset_s { @@ -79,7 +79,7 @@ typedef struct dly_s int *lpdelayline; } dly_t; -const sx_preset_t rgsxpre[] = +const sx_preset_t rgsxpre[MAXPRESETS] = { // -------reverb-------- -------delay-------- // lp mod size refl rvblp delay feedback dlylp left @@ -193,7 +193,7 @@ void SX_Init( void ) sxmod1cur = sxmod1 = 350 * ( idsp_dma_speed / SOUND_11k ); sxmod2cur = sxmod2 = 450 * ( idsp_dma_speed / SOUND_11k ); - dsp_off = Cvar_Get( "dsp_off", "0", 0, "disable DSP processing" ); + dsp_off = Cvar_Get( "dsp_off", "0", FCVAR_ARCHIVE, "disable DSP processing" ); roomwater_type = Cvar_Get( "waterroom_type", "14", 0, "water room type" ); room_type = Cvar_Get( "room_type", "0", 0, "current room type preset" ); @@ -609,16 +609,15 @@ int RVB_DoReverbForOneDly( dly_t *dly, const int vlr, const portable_samplepair_ if( dly->xfade || delay || samplepair->left || samplepair->right ) { // modulate delay rate - if( !dly->mod ) + if( !dly->xfade && !dly->modcur && dly->mod ) { dly->idelayoutputxf = dly->idelayoutput + ((COM_RandomLong( 0, 255 ) * delay) >> 9 ); - if( dly->idelayoutputxf >= dly->cdelaysamplesmax ) - dly->idelayoutputxf -= dly->cdelaysamplesmax; - - dly->xfade = REVERB_XFADE; + //dly->xfade = 32; } + dly->idelayoutputxf %= dly->cdelaysamplesmax; + if( dly->xfade ) { samplexf = (dly->lpdelayline[dly->idelayoutputxf] * (REVERB_XFADE - dly->xfade)) / REVERB_XFADE; @@ -819,6 +818,9 @@ void CheckNewDspPresets( void ) idsp_room = roomwater_type->value; else idsp_room = room_type->value; + // don't pass invalid presets + idsp_room = bound( 0, idsp_room, MAXPRESETS - 1 ); + if( FBitSet( hisound->flags, FCVAR_CHANGED )) { sxhires = hisound->value; diff --git a/engine/client/s_load.c b/engine/client/s_load.c index 409909c8..92f52056 100644 --- a/engine/client/s_load.c +++ b/engine/client/s_load.c @@ -44,7 +44,7 @@ void S_SoundList_f( void ) for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ ) { - if( !sfx->servercount ) + if( !sfx->name[0] ) continue; sc = sfx->cache; @@ -54,7 +54,9 @@ void S_SoundList_f( void ) if( sc->loopStart >= 0 ) Con_Printf( "L" ); else Con_Printf( " " ); - Con_Printf( " (%2db) %s : sound/%s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name ); + if( sfx->name[0] == '*' ) + Con_Printf( " (%2db) %s : %s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name ); + else Con_Printf( " (%2db) %s : %s%s\n", sc->width * 8, Q_memprint( sc->size ), DEFAULT_SOUNDPATH, sfx->name ); totalSfx++; } } @@ -127,16 +129,21 @@ wavdata_t *S_LoadSound( sfx_t *sfx ) wavdata_t *sc = NULL; if( !sfx ) return NULL; - if( sfx->cache ) return sfx->cache; // see if still in memory - if( Q_stricmp( sfx->name, "*default" )) + // see if still in memory + if( sfx->cache ) + return sfx->cache; + + if( !COM_CheckString( sfx->name )) { - // load it from disk - if( sfx->name[0] == '*' ) - sc = FS_LoadSound( sfx->name + 1, NULL, 0 ); - else sc = FS_LoadSound( sfx->name, NULL, 0 ); + // debug + Con_Printf( "S_LoadSound: sfx %d has NULL name\n", sfx - s_knownSfx ); + return NULL; } + // load it from disk + if( Q_stricmp( sfx->name, "*default" )) + sc = FS_LoadSound( sfx->name, NULL, 0 ); if( !sc ) sc = S_CreateDefaultSound(); if( sc->rate < SOUND_11k ) // some bad sounds @@ -169,11 +176,8 @@ sfx_t *S_FindName( const char *pname, int *pfInCache ) if( !COM_CheckString( pname ) || !dma.initialized ) return NULL; - if( Q_strlen( pname ) >= MAX_STRING ) - { - MsgDev( D_ERROR, "S_FindSound: sound name too long: %s", pname ); + if( Q_strlen( pname ) >= sizeof( sfx->name )) return NULL; - } Q_strncpy( name, pname, sizeof( name )); COM_FixSlashes( name ); @@ -202,10 +206,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache ) if( i == s_numSfx ) { if( s_numSfx == MAX_SFX ) - { - MsgDev( D_ERROR, "S_FindName: MAX_SFX limit exceeded\n" ); return NULL; - } s_numSfx++; } @@ -233,7 +234,8 @@ void S_FreeSound( sfx_t *sfx ) sfx_t *hashSfx; sfx_t **prev; - if( !sfx || !sfx->name[0] ) return; + if( !sfx || !sfx->name[0] ) + return; // de-link it from the hash tree prev = &s_sfxHashList[sfx->hashValue]; @@ -251,7 +253,8 @@ void S_FreeSound( sfx_t *sfx ) prev = &hashSfx->hashNext; } - if( sfx->cache ) FS_FreeSound( sfx->cache ); + if( sfx->cache ) + FS_FreeSound( sfx->cache ); memset( sfx, 0, sizeof( *sfx )); } @@ -266,11 +269,6 @@ void S_BeginRegistration( void ) int i; s_registration_sequence++; - s_registering = true; - - // create unused 0-entry - S_RegisterSound( "*default" ); - snd_ambient = false; // check for automatic ambient sounds @@ -279,11 +277,11 @@ void S_BeginRegistration( void ) if( !GI->ambientsound[i][0] ) continue; // empty slot - if( !ambient_sfx[i] ) - MsgDev( D_NOTE, "Loading ambient[%i]: ^2%s^7\n", i, GI->ambientsound[i] ); ambient_sfx[i] = S_RegisterSound( GI->ambientsound[i] ); if( ambient_sfx[i] ) snd_ambient = true; // allow auto-ambients } + + s_registering = true; } /* @@ -303,7 +301,9 @@ void S_EndRegistration( void ) // free any sounds not from this registration sequence for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ ) { - if( !sfx->name[0] ) continue; + if( !sfx->name[0] || sfx->name[0] == '*' ) + continue; // don't release default sound + if( sfx->servercount != s_registration_sequence ) S_FreeSound( sfx ); // don't need this sound } @@ -311,7 +311,8 @@ void S_EndRegistration( void ) // load everything in for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ ) { - if( !sfx->name[0] ) continue; + if( !sfx->name[0] ) + continue; S_LoadSound( sfx ); } s_registering = false; @@ -351,23 +352,35 @@ sound_t S_RegisterSound( const char *name ) sfx_t *S_GetSfxByHandle( sound_t handle ) { - if( handle == -1 || !dma.initialized ) + if( !dma.initialized ) return NULL; + // create new sfx if( handle == SENTENCE_INDEX ) - { - // create new sfx return S_FindName( s_sentenceImmediateName, NULL ); - } if( handle < 0 || handle >= s_numSfx ) - { - MsgDev( D_ERROR, "S_GetSfxByHandle: handle %i out of range (%i)\n", handle, s_numSfx ); return NULL; - } + return &s_knownSfx[handle]; } +/* +================= +S_InitSounds +================= +*/ +void S_InitSounds( void ) +{ + // create unused 0-entry + Q_strncpy( s_knownSfx->name, "*default", MAX_QPATH ); + s_knownSfx->hashValue = COM_HashKey( s_knownSfx->name, MAX_SFX_HASH ); + s_knownSfx->hashNext = s_sfxHashList[s_knownSfx->hashValue]; + s_sfxHashList[s_knownSfx->hashValue] = s_knownSfx; + s_knownSfx->cache = S_CreateDefaultSound(); + s_numSfx = 1; +} + /* ================= S_FreeSounds diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 49dc6bfd..78fd04a2 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -273,6 +273,28 @@ void SND_ChannelTraceReset( void ) channels[i].bTraced = false; } +/* +================= +SND_FStreamIsPlaying + +Select a channel from the dynamic channel allocation area. For the given entity, +override any other sound playing on the same channel (see code comments below for +exceptions). +================= +*/ +qboolean SND_FStreamIsPlaying( sfx_t *sfx ) +{ + int ch_idx; + + for( ch_idx = NUM_AMBIENTS; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++ ) + { + if( channels[ch_idx].sfx == sfx ) + return true; + } + + return false; +} + /* ================= SND_PickDynamicChannel @@ -294,6 +316,13 @@ channel_t *SND_PickDynamicChannel( int entnum, int channel, sfx_t *sfx, qboolean life_left = 0x7fffffff; if( ignore ) *ignore = false; + if( channel == CHAN_STREAM && SND_FStreamIsPlaying( sfx )) + { + if( ignore ) + *ignore = true; + return NULL; + } + for( ch_idx = NUM_AMBIENTS; ch_idx < MAX_DYNAMIC_CHANNELS; ch_idx++ ) { channel_t *ch = &channels[ch_idx]; @@ -390,7 +419,7 @@ channel_t *SND_PickStaticChannel( const vec3_t pos, sfx_t *sfx ) // no empty slots, alloc a new static sound channel if( total_channels == MAX_CHANNELS ) { - MsgDev( D_ERROR, "S_PickStaticChannel: no free channels\n" ); + Con_DPrintf( S_ERROR "S_PickStaticChannel: no free channels\n" ); return NULL; } @@ -891,14 +920,11 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv // and we didn't find it (it's not playing), go ahead and start it up } - if( pitch == 0 ) - { - MsgDev( D_WARN, "S_StartSound: ( %s ) ignored, called with pitch 0\n", sfx->name ); - return; - } - if( !pos ) pos = RI.vieworg; + if( chan == CHAN_STREAM ) + SetBits( flags, SND_STOP_LOOPING ); + // pick a channel to play on if( chan == CHAN_STATIC ) target_chan = SND_PickStaticChannel( pos, sfx ); else target_chan = SND_PickDynamicChannel( ent, chan, sfx, &bIgnore ); @@ -1019,12 +1045,6 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float vol = bound( 0, fvol * 255, 255 ); if( pitch <= 1 ) pitch = PITCH_NORM; // Invasion issues - if( pitch == 0 ) - { - MsgDev( D_WARN, "S_RestoreSound: ( %s ) ignored, called with pitch 0\n", sfx->name ); - return; - } - // pick a channel to play on if( chan == CHAN_STATIC ) target_chan = SND_PickStaticChannel( pos, sfx ); else target_chan = SND_PickDynamicChannel( ent, chan, sfx, &bIgnore ); @@ -1152,12 +1172,6 @@ void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, floa return; if( flags & SND_STOP ) return; } - - if( pitch == 0 ) - { - MsgDev( D_WARN, "S_AmbientSound: ( %s ) ignored, called with pitch 0\n", sfx->name ); - return; - } // pick a channel to play on from the static area ch = SND_PickStaticChannel( pos, sfx ); @@ -1375,7 +1389,13 @@ void S_UpdateAmbientSounds( void ) chan = &channels[ambient_channel]; chan->sfx = S_GetSfxByHandle( ambient_sfx[ambient_channel] ); - if( !chan->sfx ) continue; + // ambient is unused + if( !chan->sfx ) + { + chan->rightvol = 0; + chan->leftvol = 0; + continue; + } vol = s_ambient_level->value * leaf->ambient_sound_level[ambient_channel]; if( vol < 0 ) vol = 0; @@ -1958,7 +1978,7 @@ void SND_UpdateSound( void ) S_SpatializeRawChannels(); // debugging output - if( s_show->value ) + if( CVAR_TO_BOOL( s_show )) { info.color[0] = 1.0f; info.color[1] = 0.6f; @@ -2174,7 +2194,7 @@ qboolean S_Init( void ) { if( Sys_CheckParm( "-nosound" )) { - MsgDev( D_INFO, "Audio: Disabled\n" ); + Con_Printf( "Audio: Disabled\n" ); return false; } @@ -2185,7 +2205,7 @@ qboolean S_Init( void ) s_lerping = Cvar_Get( "s_lerping", "0", FCVAR_ARCHIVE, "apply interpolation to sound output" ); s_ambient_level = Cvar_Get( "ambient_level", "0.3", FCVAR_ARCHIVE, "volume of environment noises (water and wind)" ); s_ambient_fade = Cvar_Get( "ambient_fade", "1000", FCVAR_ARCHIVE, "rate of volume fading when client is moving" ); - s_combine_sounds = Cvar_Get( "s_combine_channels", "1", FCVAR_ARCHIVE, "combine channels with same sounds" ); + s_combine_sounds = Cvar_Get( "s_combine_channels", "0", FCVAR_ARCHIVE, "combine channels with same sounds" ); snd_foliage_db_loss = Cvar_Get( "snd_foliage_db_loss", "4", 0, "foliage loss factor" ); snd_gain_max = Cvar_Get( "snd_gain_max", "1", 0, "gain maximal threshold" ); snd_gain_min = Cvar_Get( "snd_gain_min", "0.01", 0, "gain minimal threshold" ); @@ -2211,7 +2231,7 @@ qboolean S_Init( void ) if( !SNDDMA_Init( host.hWnd )) { - MsgDev( D_INFO, "S_Init: sound system can't be initialized\n" ); + Con_Printf( "Audio: sound system can't be initialized\n" ); return false; } @@ -2226,6 +2246,7 @@ qboolean S_Init( void ) SX_Init (); S_InitScaletable (); S_StopAllSounds ( true ); + S_InitSounds (); VOX_Init (); return true; diff --git a/engine/client/s_stream.c b/engine/client/s_stream.c index 40d0f99b..bc7a8acc 100644 --- a/engine/client/s_stream.c +++ b/engine/client/s_stream.c @@ -84,15 +84,16 @@ void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, long if( mainTrack && *mainTrack == '*' ) mainTrack = NULL; - if(( !introTrack || !*introTrack ) && ( !mainTrack || !*mainTrack )) + if( !COM_CheckString( introTrack ) && !COM_CheckString( mainTrack )) return; if( !introTrack ) introTrack = mainTrack; if( !*introTrack ) return; - if( !mainTrack || !*mainTrack ) s_bgTrack.loopName[0] = '\0'; + if( !COM_CheckString( mainTrack )) + s_bgTrack.loopName[0] = '\0'; else Q_strncpy( s_bgTrack.loopName, mainTrack, sizeof( s_bgTrack.loopName )); -if( fullpath ) Msg( "MP3:Playing: %s\n", introTrack ); + // open stream s_bgTrack.stream = FS_OpenStream( va( "media/%s", introTrack )); Q_strncpy( s_bgTrack.current, introTrack, sizeof( s_bgTrack.current )); diff --git a/engine/client/s_vox.c b/engine/client/s_vox.c index 4082f69a..bee8ae90 100644 --- a/engine/client/s_vox.c +++ b/engine/client/s_vox.c @@ -478,7 +478,7 @@ void VOX_LoadSound( channel_t *pchan, const char *pszin ) if( Q_strlen( psz ) > sizeof( buffer ) - 1 ) { - MsgDev( D_ERROR, "VOX_LoadSound: sentence is too long %s\n", psz ); + Con_Printf( S_ERROR "VOX_LoadSound: sentence is too long %s\n", psz ); return; } @@ -545,7 +545,7 @@ void VOX_ParseLineCommands( char *pSentenceData, int sentenceIndex ) length = pNext - pSentenceData; if( tempBufferPos + length > sizeof( tempBuffer )) { - MsgDev( D_ERROR, "sentence too long!\n" ); + Con_Printf( S_ERROR "sentence too long!\n" ); return; } diff --git a/engine/client/sound.h b/engine/client/sound.h index 2d790a3b..6da7ec9d 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -292,6 +292,7 @@ char *S_SkipSoundChar( const char *pch ); sfx_t *S_FindName( const char *name, int *pfInCache ); sound_t S_RegisterSound( const char *name ); void S_FreeSound( sfx_t *sfx ); +void S_InitSounds( void ); // s_dsp.c void SX_Init( void ); diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index ef087985..0fbc8027 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -50,7 +50,7 @@ void VGUI_DrawShutdown( void ) for( i = 1; i < g_textureId; i++ ) { - GL_FreeImage( va( "*vgui%i", i )); + GL_FreeTexture( g_textures[i] ); } } @@ -82,7 +82,7 @@ void VGUI_UploadTexture( int id, const char *buffer, int width, int height ) if( id <= 0 || id >= VGUI_MAX_TEXTURES ) { - MsgDev( D_ERROR, "VGUI_UploadTexture: bad texture %i. Ignored\n", id ); + Con_DPrintf( S_ERROR "VGUI_UploadTexture: bad texture %i. Ignored\n", id ); return; } @@ -96,7 +96,7 @@ void VGUI_UploadTexture( int id, const char *buffer, int width, int height ) r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA; r_image.buffer = (byte *)buffer; - g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE, false ); + g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE ); } /* diff --git a/engine/client/vgui/vgui_main.h b/engine/client/vgui/vgui_main.h index 9d8145ca..0fe2857c 100644 --- a/engine/client/vgui/vgui_main.h +++ b/engine/client/vgui/vgui_main.h @@ -90,6 +90,7 @@ protected: int _drawTextColor[4]; int _translateX, _translateY; int _currentTexture; + Panel *currentPanel; }; // initialize VGUI::App as external (part of engine) diff --git a/engine/client/vgui/vgui_surf.cpp b/engine/client/vgui/vgui_surf.cpp index 26f00430..30e106a6 100644 --- a/engine/client/vgui/vgui_surf.cpp +++ b/engine/client/vgui/vgui_surf.cpp @@ -75,6 +75,7 @@ void CEngineSurface :: SetupPaintState( const PaintStack *paintState ) _translateX = paintState->iTranslateX; _translateY = paintState->iTranslateY; SetScissorRect( paintState->iScissorLeft, paintState->iScissorTop, paintState->iScissorRight, paintState->iScissorBottom ); + currentPanel = paintState->m_pPanel; } void CEngineSurface :: InitVertex( vpoint_t &vertex, int x, int y, float u, float v ) @@ -196,11 +197,7 @@ void CEngineSurface :: drawSetTextFont( Font *font ) if( y + tall + 1 > FONT_SIZE ) { if( !staticFontInfo->bindIndex[currentPage] ) - { - int bindIndex = createNewTextureID(); - staticFontInfo->bindIndex[currentPage] = bindIndex; - } - + staticFontInfo->bindIndex[currentPage] = createNewTextureID(); drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); currentPage++; @@ -223,11 +220,7 @@ void CEngineSurface :: drawSetTextFont( Font *font ) if( currentPage != FONT_PAGES ) { if( !staticFontInfo->bindIndex[currentPage] ) - { - int bindIndex = createNewTextureID(); - staticFontInfo->bindIndex[currentPage] = bindIndex; - } - + staticFontInfo->bindIndex[currentPage] = createNewTextureID(); drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); } staticFontInfo->pageCount = currentPage + 1; @@ -327,13 +320,14 @@ void CEngineSurface :: drawPrintText( const char *text, int textLen ) static bool hasColor = 0; static int numColor = 7; - if( !text || !staticFont || !staticFontInfo ) + if( !COM_CheckString( text ) || !staticFont || !staticFontInfo ) return; int x = _drawTextPos[0] + _translateX; int y = _drawTextPos[1] + _translateY; int tall = staticFont->getTall(); int curTextColor[4]; + int iTotalWidth = 0; // HACKHACK: allow color strings in VGUI if( numColor != 7 && vgui_colorstrings->value ) @@ -377,12 +371,13 @@ void CEngineSurface :: drawPrintText( const char *text, int textLen ) float t1 = staticFontInfo->texCoord[curCh][3]; int wide = abcB; + iTotalWidth += abcA; drawSetTexture( staticFontInfo->bindIndex[staticFontInfo->pageForChar[curCh]] ); - drawPrintChar( x, y, wide, tall, s0, t0, s1, t1, curTextColor ); - x += abcA + abcB + abcC; + drawPrintChar( x + iTotalWidth, y, wide, tall, s0, t0, s1, t1, curTextColor ); + iTotalWidth += wide + abcC; } - _drawTextPos[0] += x; + _drawTextPos[0] += iTotalWidth; } void CEngineSurface :: drawSetTextureRGBA( int id, const char* rgba, int wide, int tall ) @@ -406,6 +401,7 @@ void CEngineSurface :: drawTexturedRect( int x0, int y0, int x1, int y1 ) vpoint_t rect[2]; vpoint_t clippedRect[2]; + // it's not a vertex, just fill rectangle InitVertex( rect[0], x0, y0, 0, 0 ); InitVertex( rect[1], x1, y1, 1, 1 ); diff --git a/engine/common/avikit.c b/engine/common/avikit.c index deca2199..e2c80705 100644 --- a/engine/common/avikit.c +++ b/engine/common/avikit.c @@ -141,7 +141,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) // WMA codecs, both versions - they simply don't work. if( Avi->audio_header->wFormatTag == 0x160 || Avi->audio_header->wFormatTag == 0x161 ) { - if( !Avi->quiet ) MsgDev( D_ERROR, "ACM does not support this audio codec.\n" ); + if( !Avi->quiet ) + Con_Reportf( S_ERROR "ACM does not support this audio codec.\n" ); return false; } @@ -150,7 +151,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) if( Avi->audio_header_size < sizeof( WAVEFORMATEX )) { - if( !Avi->quiet ) MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" ); + if( !Avi->quiet ) + Con_Reportf( S_ERROR "ACM failed to open conversion stream.\n" ); return false; } @@ -180,7 +182,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) if( pacmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR ) { - if( !Avi->quiet ) MsgDev( D_ERROR, "ACM failed to open conversion stream.\n" ); + if( !Avi->quiet ) + Con_Reportf( S_ERROR "ACM failed to open conversion stream.\n" ); return false; } } @@ -200,7 +203,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) // get the size of the output buffer for streaming the compressed audio if( pacmStreamSize( Avi->cpa_conversion_stream, Avi->cpa_blockalign, &dest_length, ACM_STREAMSIZEF_SOURCE ) != MMSYSERR_NOERROR ) { - if( !Avi->quiet ) MsgDev( D_ERROR, "Couldn't get ACM conversion stream size.\n" ); + if( !Avi->quiet ) + Con_Reportf( S_ERROR "Couldn't get ACM conversion stream size.\n" ); pacmStreamClose( Avi->cpa_conversion_stream, 0 ); return false; } @@ -223,7 +227,8 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi ) if( pacmStreamPrepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 ) != MMSYSERR_NOERROR ) { - if( !Avi->quiet ) MsgDev( D_ERROR, "couldn't prep headers.\n" ); + if( !Avi->quiet ) + Con_Reportf( S_ERROR "couldn't prepare stream headers.\n" ); pacmStreamClose( Avi->cpa_conversion_stream, 0 ); return false; } @@ -481,20 +486,25 @@ void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audi switch( hr ) { case AVIERR_BADFORMAT: - if( !Avi->quiet ) MsgDev( D_ERROR, "corrupt file or unknown format.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "corrupt file or unknown format.\n" ); break; case AVIERR_MEMORY: - if( !Avi->quiet ) MsgDev( D_ERROR, "insufficient memory to open file.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "insufficient memory to open file.\n" ); break; case AVIERR_FILEREAD: - if( !Avi->quiet ) MsgDev( D_ERROR, "disk error reading file.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "disk error reading file.\n" ); break; case AVIERR_FILEOPEN: - if( !Avi->quiet ) MsgDev( D_ERROR, "disk error opening file.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "disk error opening file.\n" ); break; case REGDB_E_CLASSNOTREG: default: - if( !Avi->quiet ) MsgDev( D_ERROR, "no handler found (or file not found).\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "no handler found (or file not found).\n" ); break; } return; @@ -564,7 +574,8 @@ void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audi { if( Avi->pfile ) // if file is open, close it pAVIFileRelease( Avi->pfile ); - if( !Avi->quiet ) MsgDev( D_ERROR, "couldn't find a valid video stream.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "couldn't find a valid video stream.\n" ); return; } @@ -573,7 +584,8 @@ void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audi if( Avi->video_getframe == NULL ) { - if( !Avi->quiet ) MsgDev( D_ERROR, "error attempting to read video frames.\n" ); + if( !Avi->quiet ) + Con_DPrintf( S_ERROR "error attempting to read video frames.\n" ); return; // couldn't open frame getter. } @@ -618,10 +630,7 @@ movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio ) // fast reject if( !avi_initialized ) - { - MsgDev( D_ERROR, "AVI_LoadVideo: movie support is disabled\n" ); return NULL; - } // open cinematic Q_snprintf( path, sizeof( path ), "media/%s", filename ); @@ -630,7 +639,7 @@ movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio ) if( FS_FileExists( path, false ) && !fullpath ) { - MsgDev( D_ERROR, "AVI_LoadVideo: Couldn't load %s from packfile. Please extract it\n", path ); + Con_Printf( "Couldn't load %s from packfile. Please extract it\n", path ); return NULL; } @@ -667,35 +676,29 @@ qboolean AVI_Initailize( void ) { if( Sys_CheckParm( "-noavi" )) { - MsgDev( D_INFO, "AVI: Disabled\n" ); + Con_Printf( "AVI: Disabled\n" ); return false; } if( !Sys_LoadLibrary( &avifile_dll )) - { - MsgDev( D_ERROR, "AVI_Initailize: failed\n" ); return false; - } if( !Sys_LoadLibrary( &msvfw_dll )) { - MsgDev( D_ERROR, "AVI_Initailize: failed\n" ); Sys_FreeLibrary( &avifile_dll ); return false; } if( !Sys_LoadLibrary( &msacm_dll )) { - MsgDev( D_ERROR, "AVI_Initailize: failed\n" ); Sys_FreeLibrary( &avifile_dll ); Sys_FreeLibrary( &msvfw_dll ); return false; } - pAVIFileInit(); avi_initialized = true; - MsgDev( D_NOTE, "AVI_Initailize: done\n" ); - + pAVIFileInit(); + return true; } diff --git a/engine/common/build.c b/engine/common/build.c index a978fd07..dac721ac 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -23,7 +23,7 @@ static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int Q_buildnum( void ) { // do not touch this! Only author of Xash3D can increase buildnumbers! -#if 0 +#if 1 int m = 0, d = 0, y = 0; static int b = 0; @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 4150; + return 4140; #endif -} +} \ No newline at end of file diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 8e9faff8..e508025b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -106,7 +106,7 @@ void Cbuf_AddText( const char *text ) if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) { - MsgDev( D_WARN, "Cbuf_AddText: overflow\n" ); + Con_Reportf( S_WARN "Cbuf_AddText: overflow\n" ); } else { @@ -128,7 +128,7 @@ void Cbuf_InsertText( const char *text ) if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) { - MsgDev( D_WARN, "Cbuf_InsertText: overflow\n" ); + Con_Reportf( S_WARN "Cbuf_InsertText: overflow\n" ); } else { @@ -184,7 +184,7 @@ void Cbuf_Execute( void ) if( i >= ( MAX_CMD_LINE - 1 )) { - MsgDev( D_ERROR, "Cbuf_Execute: command string owerflow\n" ); + Con_DPrintf( S_ERROR "Cbuf_Execute: command string owerflow\n" ); line[0] = 0; } else @@ -604,23 +604,20 @@ void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function ) { cmd_t *cmd, *cur, *prev; - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddServerCommand: NULL name\n" ); + if( !COM_CheckString( cmd_name )) return; - } // fail if the command is a variable name if( Cvar_FindVar( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined as a var\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddServerCommand: %s already defined as a var\n", cmd_name ); return; } // fail if the command already exists if( Cmd_Exists( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddServerCommand: %s already defined\n", cmd_name ); return; } @@ -648,23 +645,20 @@ int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function ) { cmd_t *cmd, *cur, *prev; - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddClientCommand: NULL name\n" ); + if( !COM_CheckString( cmd_name )) return 0; - } // fail if the command is a variable name if( Cvar_FindVar( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddClientCommand: %s already defined as a var\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddClientCommand: %s already defined as a var\n", cmd_name ); return 0; } // fail if the command already exists if( Cmd_Exists( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddClientCommand: %s already defined\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddClientCommand: %s already defined\n", cmd_name ); return 0; } @@ -694,23 +688,20 @@ int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function ) { cmd_t *cmd, *cur, *prev; - if( !cmd_name || !*cmd_name ) - { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: NULL name\n" ); + if( !COM_CheckString( cmd_name )) return 0; - } // fail if the command is a variable name if( Cvar_FindVar( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: %s already defined as a var\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddGameUICommand: %s already defined as a var\n", cmd_name ); return 0; } // fail if the command already exists if( Cmd_Exists( cmd_name )) { - MsgDev( D_ERROR, "Cmd_AddGameUICommand: %s already defined\n", cmd_name ); + Con_DPrintf( S_ERROR "Cmd_AddGameUICommand: %s already defined\n", cmd_name ); return 0; } diff --git a/engine/common/common.c b/engine/common/common.c index df4b928d..402ce1d5 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -40,8 +40,8 @@ void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, if( fExpr ) return; if( szMessage != NULL ) - MsgDev( at_error, "ASSERT FAILED:\n %s \n(%s@%d)\n%s\n", szExpr, szFile, szLine, szMessage ); - else MsgDev( at_error, "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine ); + Con_DPrintf( S_ERROR "ASSERT FAILED:\n %s \n(%s@%d)\n%s\n", szExpr, szFile, szLine, szMessage ); + else Con_DPrintf( S_ERROR "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine ); } #endif // DEBUG @@ -769,7 +769,7 @@ COM_CheckString */ int COM_CheckString( const char *string ) { - if( !string || (byte)*string <= ' ' ) + if( !string || !*string ) return 0; return 1; } diff --git a/engine/common/common.h b/engine/common/common.h index 49082dc4..11cc46f4 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -123,7 +123,7 @@ typedef enum #include "crtlib.h" #include "cvar.h" -#define XASH_VERSION 0.99f // engine current version +#define XASH_VERSION "0.99" // engine current version // PERFORMANCE INFO #define MIN_FPS 20.0 // host minimum fps value for maxfps. @@ -379,6 +379,7 @@ typedef struct host_parm_s string finalmsg; // server shutdown final message string downloadfile; // filename to be downloading int downloadcount; // how many files remain to downloading + char deferred_cmd[128]; // deferred commands host_redirect_t rd; // remote console // command line parms @@ -503,7 +504,7 @@ NOTE: number at end of pixelformat name it's a total bitscount e.g. PF_RGB_24 == ======================================================================== */ #define ImageRAW( type ) (type == PF_RGBA_32 || type == PF_BGRA_32 || type == PF_RGB_24 || type == PF_BGR_24) -#define ImageDXT( type ) (type == PF_DXT1 || type == PF_DXT3 || type == PF_DXT5) +#define ImageDXT( type ) (type == PF_DXT1 || type == PF_DXT3 || type == PF_DXT5 || type == PF_ATI2) typedef enum { @@ -517,6 +518,7 @@ typedef enum PF_DXT1, // s3tc DXT1 format PF_DXT3, // s3tc DXT3 format PF_DXT5, // s3tc DXT5 format + PF_ATI2, // latc ATI2N format PF_TOTALCOUNT, // must be last } pixformat_t; diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 710667b1..be3f4500 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -675,10 +675,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) file_t *f; if( FS_FileSize( "maps.lst", onlyingamedir ) > 0 && !fRefresh ) - { - MsgDev( D_NOTE, "maps.lst is exist: %s\n", onlyingamedir ? "basedir" : "gamedir" ); return true; // exist - } t = FS_Search( "maps/*.bsp", false, onlyingamedir ); @@ -917,10 +914,11 @@ void Host_WriteConfig( void ) if( !clgame.hInstance ) return; - MsgDev( D_NOTE, "Host_WriteConfig()\n" ); + f = FS_Open( "config.cfg", "w", false ); if( f ) { + Con_Reportf( "Host_WriteConfig()\n" ); FS_Printf( f, "//=======================================================================\n"); FS_Printf( f, "//\t\t\tCopyright XashXT Group %s (C)\n", Q_timestamp( TIME_YEAR_ONLY )); FS_Printf( f, "//\t\t\tconfig.cfg - archive of cvars\n" ); @@ -945,7 +943,7 @@ void Host_WriteConfig( void ) FS_Close( f ); } - else MsgDev( D_ERROR, "Couldn't write config.cfg.\n" ); + else Con_DPrintf( S_ERROR "Couldn't write config.cfg.\n" ); } /* @@ -974,7 +972,7 @@ void Host_WriteServerConfig( const char *name ) CSCR_WriteGameCVars( f, "settings.scr" ); FS_Close( f ); } - else MsgDev( D_ERROR, "Couldn't write %s.\n", name ); + else Con_DPrintf( S_ERROR "Couldn't write %s.\n", name ); SV_FreeGameProgs(); // release progs with all variables } @@ -990,10 +988,11 @@ void Host_WriteOpenGLConfig( void ) { file_t *f; - MsgDev( D_NOTE, "Host_WriteGLConfig()\n" ); + f = FS_Open( "opengl.cfg", "w", false ); if( f ) { + Con_Reportf( "Host_WriteGLConfig()\n" ); FS_Printf( f, "//=======================================================================\n" ); FS_Printf( f, "//\t\t\tCopyright XashXT Group %s (C)\n", Q_timestamp( TIME_YEAR_ONLY )); FS_Printf( f, "//\t\t opengl.cfg - archive of opengl extension cvars\n"); @@ -1002,7 +1001,7 @@ void Host_WriteOpenGLConfig( void ) Cmd_WriteOpenGLVariables( f ); FS_Close( f ); } - else MsgDev( D_ERROR, "can't update opengl.cfg.\n" ); + else Con_DPrintf( S_ERROR "can't update opengl.cfg.\n" ); } /* @@ -1016,10 +1015,10 @@ void Host_WriteVideoConfig( void ) { file_t *f; - MsgDev( D_NOTE, "Host_WriteVideoConfig()\n" ); f = FS_Open( "video.cfg", "w", false ); if( f ) { + Con_Reportf( "Host_WriteVideoConfig()\n" ); FS_Printf( f, "//=======================================================================\n" ); FS_Printf( f, "//\t\t\tCopyright XashXT Group %s (C)\n", Q_timestamp( TIME_YEAR_ONLY )); FS_Printf( f, "//\t\tvideo.cfg - archive of renderer variables\n"); @@ -1027,7 +1026,7 @@ void Host_WriteVideoConfig( void ) Cvar_WriteVariables( f, FCVAR_RENDERINFO ); FS_Close( f ); } - else MsgDev( D_ERROR, "can't update video.cfg.\n" ); + else Con_DPrintf( S_ERROR "can't update video.cfg.\n" ); } void Key_EnumCmds_f( void ) diff --git a/engine/common/console.c b/engine/common/console.c index b0b0dee3..17c40965 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -468,7 +468,7 @@ void Con_CheckResize( void ) int i, width; if( con.curFont && con.curFont->hFontTexture ) - charWidth = con.curFont->charWidths['M'] - 1; + charWidth = con.curFont->charWidths['O'] - 1; width = ( glState.width / charWidth ) - 2; if( !glw_state.initialized ) width = (640 / 5); @@ -973,7 +973,7 @@ void Con_Init( void ) Cmd_AddCommand( "contimes", Con_SetTimes_f, "change number of console overlay lines (4-64)" ); con.initialized = true; - MsgDev( D_INFO, "Console initialized.\n" ); + Con_Printf( "Console initialized.\n" ); } /* @@ -1746,6 +1746,7 @@ void Key_Console( int key ) Con_ClearField( &con.input ); con.input.widthInChars = con.linewidth; + Con_Bottom(); if( cls.state == ca_disconnected ) { @@ -1759,6 +1760,7 @@ void Key_Console( int key ) if( key == K_TAB ) { Con_CompleteCommand( &con.input ); + Con_Bottom(); return; } @@ -2096,7 +2098,7 @@ void Con_DrawSolidConsole( int lines ) memcpy( color, g_color_table[7], sizeof( color )); - Q_snprintf( curbuild, MAX_STRING, "Xash3D %i/%g (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, "Xash3D %i/%s (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = glState.width - stringLen; stringLen = Con_StringLength( curbuild ); @@ -2246,8 +2248,8 @@ void Con_DrawVersion( void ) } if( host.force_draw_version || draw_version ) - Q_snprintf( curbuild, MAX_STRING, "Xash3D v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); - else Q_snprintf( curbuild, MAX_STRING, "v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, "Xash3D v%i/%s (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = glState.width - stringLen * 1.05f; stringLen = Con_StringLength( curbuild ); @@ -2368,7 +2370,7 @@ void Con_VidInit( void ) { qboolean draw_to_console = false; int length = 0; - gltexture_t *chars; + gl_texture_t *chars; // NOTE: only these games want to draw build number into console background if( !Q_stricmp( FS_Gamedir(), "id1" )) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 6aabfec8..42e28605 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -320,7 +320,7 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char * // check for command coexisting if( Cmd_Exists( name )) { - MsgDev( D_ERROR, "can't register variable '%s', is already defined as command\n", name ); + Con_DPrintf( S_ERROR "can't register variable '%s', is already defined as command\n", name ); return NULL; } @@ -356,7 +356,7 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char * if( FBitSet( var->flags, FCVAR_ALLOCATED ) && Q_strcmp( var_desc, var->desc )) { if( !FBitSet( flags, FCVAR_GLCONFIG )) - MsgDev( D_REPORT, "%s change description from %s to %s\n", var->name, var->desc, var_desc ); + Con_Reportf( "%s change description from %s to %s\n", var->name, var->desc, var_desc ); // update description if needs freestring( var->desc ); var->desc = copystring( var_desc ); @@ -410,7 +410,7 @@ void Cvar_RegisterVariable( convar_t *var ) { if( !FBitSet( dup->flags, FCVAR_TEMPORARY )) { - MsgDev( D_ERROR, "can't register variable '%s', is already defined\n", var->name ); + Con_DPrintf( S_ERROR "can't register variable '%s', is already defined\n", var->name ); return; } @@ -421,7 +421,7 @@ void Cvar_RegisterVariable( convar_t *var ) // check for overlap with a command if( Cmd_Exists( var->name )) { - MsgDev( D_ERROR, "can't register variable '%s', is already defined as command\n", var->name ); + Con_DPrintf( S_ERROR "can't register variable '%s', is already defined as command\n", var->name ); return; } @@ -469,16 +469,12 @@ void Cvar_DirectSet( convar_t *var, const char *value ) if( CVAR_CHECK_SENTINEL( var ) || ( var->next == NULL && !FBitSet( var->flags, FCVAR_EXTENDED|FCVAR_ALLOCATED ))) { // need to registering cvar fisrt - MsgDev( D_WARN, "Cvar_DirectSet: called for unregistered cvar '%s'\n", var->name ); Cvar_RegisterVariable( var ); // ok, register it } // lookup for registration again if( var != Cvar_FindVar( var->name )) - { - MsgDev( D_ERROR, "Cvar_DirectSet: couldn't find cvar '%s' in linked list\n", var->name ); - return; - } + return; // how this possible? if( FBitSet( var->flags, FCVAR_READ_ONLY|FCVAR_GLCONFIG )) { @@ -565,8 +561,7 @@ void Cvar_Set( const char *var_name, const char *value ) if( !var ) { // there is an error in C code if this happens - if( host.type != HOST_DEDICATED ) - MsgDev( D_ERROR, "Cvar_Set: variable '%s' not found\n", var_name ); + Con_Printf( "Cvar_Set: variable '%s' not found\n", var_name ); return; } diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 2cc940f8..1f0b2e25 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -109,7 +109,6 @@ char fs_basedir[MAX_SYSPATH]; // base game directory char fs_gamedir[MAX_SYSPATH]; // game current directory char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes -static const wadtype_t wad_hints[10]; static void FS_InitMemory( void ); static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); @@ -1005,6 +1004,11 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g pfile = COM_ParseFile( pfile, token ); GameInfo->size = Q_atoi( token ); } + else if( !Q_stricmp( token, "edicts" )) + { + pfile = COM_ParseFile( pfile, token ); + GameInfo->max_edicts = Q_atoi( token ); + } else if( !Q_stricmp( token, "mpentity" )) { pfile = COM_ParseFile( pfile, GameInfo->mp_entity ); @@ -2641,7 +2645,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) continue; // build the lumpname with image suffix (if present) - Q_snprintf( temp, sizeof( temp ), "%s%s", wad->lumps[i].name, wad_hints[wad->lumps[i].img_type].ext ); + Q_strncpy( temp, wad->lumps[i].name, sizeof( temp )); while( temp[0] ) { @@ -2764,21 +2768,6 @@ static const wadtype_t wad_types[7] = { NULL, TYP_NONE } }; -// suffix converts to img_type and back -static const wadtype_t wad_hints[10] = -{ -{ "", IMG_DIFFUSE }, // no suffix -{ "_mask", IMG_ALPHAMASK }, // alpha-channel stored to another lump -{ "_norm", IMG_NORMALMAP }, // indexed normalmap -{ "_spec", IMG_GLOSSMAP }, // grayscale\color specular -{ "_gpow", IMG_GLOSSPOWER }, // grayscale gloss power -{ "_hmap", IMG_HEIGHTMAP }, // heightmap (can be converted to normalmap) -{ "_luma", IMG_LUMA }, // self-illuminate parts on the diffuse -{ "_adec", IMG_DECAL_ALPHA }, // classic HL-decal (with alpha-channel) -{ "_cdec", IMG_DECAL_COLOR }, // paranoia decal (base 127 127 127) -{ NULL, 0 } // terminator -}; - /* =========== W_TypeFromExt @@ -2826,40 +2815,6 @@ static const char *W_ExtFromType( char lumptype ) return ""; } -/* -=========== -W_HintFromSuf - -Convert name suffix into image type -=========== -*/ -char W_HintFromSuf( const char *lumpname ) -{ - char barename[64]; - char suffix[8]; - size_t namelen; - const wadtype_t *hint; - - // trying to extract hint from the name - COM_FileBase( lumpname, barename ); - namelen = Q_strlen( barename ); - - if( namelen <= HINT_NAMELEN ) - return IMG_DIFFUSE; - - Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix )); - - // we not known about filetype, so match only by filename - for( hint = wad_hints; hint->ext; hint++ ) - { - if( !Q_stricmp( suffix, hint->ext )) - return hint->type; - } - - // no any special type was found - return IMG_DIFFUSE; -} - /* =========== W_FindLump @@ -2869,37 +2824,11 @@ Serach for already existed lump */ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype ) { - char img_type = IMG_DIFFUSE; - char barename[64], suffix[8]; - int left, right; - size_t namelen; - const wadtype_t *hint; + int left, right; if( !wad || !wad->lumps || matchtype == TYP_NONE ) return NULL; - // trying to extract hint from the name - COM_FileBase( name, barename ); - namelen = Q_strlen( barename ); - - if( namelen > HINT_NAMELEN ) - { - Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix )); - - // we not known about filetype, so match only by filename - for( hint = wad_hints; hint->ext; hint++ ) - { - if( !Q_stricmp( suffix, hint->ext )) - { - img_type = hint->type; - break; - } - } - - if( img_type != IMG_DIFFUSE ) - barename[namelen - HINT_NAMELEN] = '\0'; // kill the suffix - } - // look for the file (binary search) left = 0; right = wad->numlumps - 1; @@ -2907,15 +2836,11 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match while( left <= right ) { int middle = (left + right) / 2; - int diff = Q_stricmp( wad->lumps[middle].name, barename ); + int diff = Q_stricmp( wad->lumps[middle].name, name ); if( !diff ) { - if( wad->lumps[middle].img_type > img_type ) - diff = 1; - else if( wad->lumps[middle].img_type < img_type ) - diff = -1; - else if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type )) + if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type )) return &wad->lumps[middle]; // found else if( wad->lumps[middle].type < matchtype ) diff = 1; @@ -2956,11 +2881,7 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t if( !diff ) { - if( wad->lumps[middle].img_type > newlump->img_type ) - diff = 1; - else if( wad->lumps[middle].img_type < newlump->img_type ) - diff = -1; - else if( wad->lumps[middle].type < newlump->type ) + if( wad->lumps[middle].type < newlump->type ) diff = 1; else if( wad->lumps[middle].type > newlump->type ) diff = -1; @@ -3143,10 +3064,6 @@ wfile_t *W_Open( const char *filename, int *error ) if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) srclumps[i].type = TYP_GFXPIC; - // fixups bad image types (some quake wads) - if( srclumps[i].img_type < 0 || srclumps[i].img_type > IMG_DECAL_COLOR ) - srclumps[i].img_type = IMG_DIFFUSE; - W_AddFileToWad( name, wad, &srclumps[i] ); } diff --git a/engine/common/filesystem.h b/engine/common/filesystem.h index b08f62b1..f407f6f2 100644 --- a/engine/common/filesystem.h +++ b/engine/common/filesystem.h @@ -62,10 +62,6 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps] #define HINT_NAMELEN 5 // e.g. _mask, _norm #define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount -// hidden virtual lump types -#define TYP_ANY -1 // any type can be accepted -#define TYP_NONE 0 // unknown lump type - #include "const.h" typedef struct @@ -82,8 +78,8 @@ typedef struct int size; // uncompressed char type; // TYP_* char attribs; // file attribs - char img_type; // IMG_* - char pad; + char pad0; + char pad1; char name[WAD3_NAMELEN]; // must be null terminated } dlumpinfo_t; diff --git a/engine/common/host.c b/engine/common/host.c index 82048720..cb25e0d9 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -65,19 +65,19 @@ Host_PrintEngineFeatures void Host_PrintEngineFeatures( void ) { if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD )) - MsgDev( D_REPORT, "^3EXT:^7 big world support enabled\n" ); + Con_Reportf( "^3EXT:^7 big world support enabled\n" ); if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA )) - MsgDev( D_REPORT, "^3EXT:^7 deluxemap support enabled\n" ); + Con_Reportf( "^3EXT:^7 deluxemap support enabled\n" ); if( FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT )) - MsgDev( D_REPORT, "^3EXT:^7 Improved MOVETYPE_PUSH is used\n" ); + Con_Reportf( "^3EXT:^7 Improved MOVETYPE_PUSH is used\n" ); if( FBitSet( host.features, ENGINE_LARGE_LIGHTMAPS )) - MsgDev( D_REPORT, "^3EXT:^7 Large lightmaps enabled\n" ); + Con_Reportf( "^3EXT:^7 Large lightmaps enabled\n" ); if( FBitSet( host.features, ENGINE_COMPENSATE_QUAKE_BUG )) - MsgDev( D_REPORT, "^3EXT:^7 Compensate quake bug enabled\n" ); + Con_Reportf( "^3EXT:^7 Compensate quake bug enabled\n" ); } /* @@ -94,7 +94,7 @@ void Host_EndGame( qboolean abort, const char *message, ... ) Q_vsnprintf( string, sizeof( string ), message, argptr ); va_end( argptr ); - MsgDev( D_INFO, "Host_EndGame: %s\n", string ); + Con_Printf( "Host_EndGame: %s\n", string ); SV_Shutdown( "\n" ); CL_Disconnect(); @@ -228,7 +228,7 @@ void Host_Exec_f( void ) f = FS_LoadFile( cfgpath, &len, false ); if( !f ) { - MsgDev( D_NOTE, "couldn't exec %s\n", Cmd_Argv( 1 )); + Con_Reportf( "couldn't exec %s\n", Cmd_Argv( 1 )); return; } @@ -242,7 +242,7 @@ void Host_Exec_f( void ) Mem_Free( f ); if( !host.apply_game_config ) - MsgDev( D_INFO, "execing %s\n", Cmd_Argv( 1 )); + Con_Printf( "execing %s\n", Cmd_Argv( 1 )); Cbuf_InsertText( txt ); Mem_Free( txt ); } @@ -325,7 +325,7 @@ qboolean Host_RegisterDecal( const char *name, int *count ) if( i == MAX_DECALS ) { - MsgDev( D_ERROR, "MAX_DECALS limit exceeded (%d)\n", MAX_DECALS ); + Con_DPrintf( S_ERROR "MAX_DECALS limit exceeded (%d)\n", MAX_DECALS ); return false; } @@ -739,7 +739,7 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) Con_CreateConsole(); // system console used by dedicated server or show fatal errors // NOTE: this message couldn't be passed into game console but it doesn't matter - MsgDev( D_NOTE, "Sys_LoadLibrary: Loading xash.dll - ok\n" ); + Con_Reportf( "Sys_LoadLibrary: Loading xash.dll - ok\n" ); // get default screen res VID_InitDefaultResolution(); @@ -823,8 +823,8 @@ int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func host_clientloaded = Cvar_Get( "host_clientloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded client.dll" ); host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" ); con_gamemaps = Cvar_Get( "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" ); - build = Cvar_Get( "build", va( "%i", Q_buildnum()), FCVAR_READ_ONLY, "returns a current build number" ); - ver = Cvar_Get( "ver", va( "%i/%g (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum()), FCVAR_READ_ONLY, "shows an engine version" ); + build = Cvar_Get( "buildnum", va( "%i", Q_buildnum()), FCVAR_READ_ONLY, "returns a current build number" ); + ver = Cvar_Get( "ver", va( "%i/%s (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum()), FCVAR_READ_ONLY, "shows an engine version" ); Mod_Init(); NET_Init(); diff --git a/engine/common/host_state.c b/engine/common/host_state.c index d76551a6..2b3dbc67 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -25,6 +25,14 @@ static void Host_SetState( host_state_t newState, qboolean clearNext ) if( clearNext ) GameState->nextstate = newState; GameState->curstate = newState; + + if( clearNext && newState == STATE_RUNFRAME ) + { + // states finished here + GameState->backgroundMap = false; + GameState->loadGame = false; + GameState->newGame = false; + } } static void Host_SetNextState( host_state_t nextState ) @@ -38,6 +46,9 @@ void COM_NewGame( char const *pMapName ) if( GameState->nextstate != STATE_RUNFRAME ) return; + if( UI_CreditsActive( )) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); Host_SetNextState( STATE_LOAD_LEVEL ); @@ -52,6 +63,9 @@ void COM_LoadLevel( char const *pMapName, qboolean background ) if( GameState->nextstate != STATE_RUNFRAME ) return; + if( UI_CreditsActive( )) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); Host_SetNextState( STATE_LOAD_LEVEL ); @@ -66,6 +80,9 @@ void COM_LoadGame( char const *pMapName ) if( GameState->nextstate != STATE_RUNFRAME ) return; + if( UI_CreditsActive( )) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); Host_SetNextState( STATE_LOAD_GAME ); GameState->backgroundMap = false; @@ -78,6 +95,9 @@ void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName, qboolean if( GameState->nextstate != STATE_RUNFRAME ) return; + if( UI_CreditsActive( )) + return; + Q_strncpy( GameState->levelName, pNewLevel, sizeof( GameState->levelName )); GameState->backgroundMap = background; diff --git a/engine/common/imagelib/img_dds.c b/engine/common/imagelib/img_dds.c index 3b6c3b3a..1e24176d 100644 --- a/engine/common/imagelib/img_dds.c +++ b/engine/common/imagelib/img_dds.c @@ -117,6 +117,9 @@ void Image_DXTGetPixelFormat( dds_t *hdr ) case TYPE_DXT5: image.type = PF_DXT5; break; + case TYPE_ATI2: + image.type = PF_ATI2; + break; default: image.type = PF_UNKNOWN; // assume error break; @@ -157,7 +160,8 @@ size_t Image_DXTGetLinearSize( int type, int width, int height, int depth ) { case PF_DXT1: return ((( width + 3 ) / 4 ) * (( height + 3 ) / 4 ) * depth * 8 ); case PF_DXT3: - case PF_DXT5: return ((( width + 3 ) / 4 ) * (( height + 3 ) / 4 ) * depth * 16 ); + case PF_DXT5: + case PF_ATI2: return ((( width + 3 ) / 4 ) * (( height + 3 ) / 4 ) * depth * 16 ); case PF_BGR_24: case PF_RGB_24: return (width * height * depth * 3); case PF_BGRA_32: @@ -214,7 +218,8 @@ uint Image_DXTCalcSize( const char *name, dds_t *hdr, size_t filesize ) if( filesize != buffsize ) // main check { MsgDev( D_WARN, "Image_LoadDDS: (%s) probably corrupted(%i should be %i)\n", name, buffsize, filesize ); - return false; + if( buffsize > filesize ) + return false; } return buffsize; @@ -274,7 +279,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) Image_DXTGetPixelFormat( &header ); // and image type too :) Image_DXTAdjustVolume( &header ); - if( !Image_CheckFlag( IL_DDS_HARDWARE ) && ( image.type == PF_DXT1 || image.type == PF_DXT3 || image.type == PF_DXT5 )) + if( !Image_CheckFlag( IL_DDS_HARDWARE ) && ImageDXT( image.type )) return false; // silently rejected if( image.type == PF_UNKNOWN ) @@ -324,7 +329,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) // dds files will be uncompressed on a render. requires minimal of info for set this image.rgba = Mem_Malloc( host.imagepool, image.size ); memcpy( image.rgba, fin, image.size ); - image.flags |= IMAGE_DDS_FORMAT; + SetBits( image.flags, IMAGE_DDS_FORMAT ); return true; } \ No newline at end of file diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 9fdf95d5..57cf79d4 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -47,16 +47,6 @@ static const suffix_t skybox_qv2[6] = static const suffix_t cubemap_v1[6] = { -{ "posx", 0, CB_HINT_POSX }, -{ "negx", 0, CB_HINT_NEGX }, -{ "posy", 0, CB_HINT_POSY }, -{ "negy", 0, CB_HINT_NEGY }, -{ "posz", 0, CB_HINT_POSZ }, -{ "negz", 0, CB_HINT_NEGZ }, -}; - -static const suffix_t cubemap_v2[6] = -{ { "px", 0, CB_HINT_POSX }, { "nx", 0, CB_HINT_NEGX }, { "py", 0, CB_HINT_POSY }, @@ -75,8 +65,7 @@ static const cubepack_t load_cubemap[] = { { "3Ds Sky1", skybox_qv1 }, { "3Ds Sky2", skybox_qv2 }, -{ "3Ds Cube", cubemap_v2 }, -{ "Tenebrae", cubemap_v1 }, +{ "3Ds Cube", cubemap_v1 }, { NULL, NULL }, }; @@ -93,6 +82,7 @@ const bpc_desc_t PFDesc[] = {PF_DXT1, "DXT 1", 0x83F1, 4 }, {PF_DXT3, "DXT 3", 0x83F2, 4 }, {PF_DXT5, "DXT 5", 0x83F3, 4 }, +{PF_ATI2, "ATI 2", 0x8837, 4 }, }; void Image_Reset( void ) @@ -128,7 +118,6 @@ rgbdata_t *ImagePack( void ) if( image.cubemap && image.num_sides != 6 ) { // this never be happens, just in case - MsgDev( D_NOTE, "ImagePack: inconsistent cubemap pack %d\n", image.num_sides ); FS_FreeImage( pack ); return NULL; } @@ -229,8 +218,8 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) const cubepack_t *cmap; byte *f; - Image_Reset(); // clear old image Q_strncpy( loadname, filename, sizeof( loadname )); + Image_Reset(); // clear old image if( Q_stricmp( ext, "" )) { @@ -259,6 +248,7 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) Q_sprintf( path, format->formatstring, loadname, "", format->ext ); image.hint = format->hint; f = FS_LoadFile( path, &filesize, false ); + if( f && filesize > 0 ) { if( format->loadfunc( path, f, filesize )) @@ -266,7 +256,7 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) Mem_Free( f ); // release buffer return ImagePack(); // loaded } - else Mem_Free(f); // release buffer + else Mem_Free( f ); // release buffer } } } @@ -308,8 +298,6 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) // first side not found, probably it's not cubemap // it contain info about image_type and dimensions, don't generate black cubemaps if( !image.cubemap ) break; - MsgDev( D_ERROR, "FS_LoadImage: couldn't load (%s%s), create black image\n", loadname, cmap->type[i].suf ); - // Mem_Alloc already filled memblock with 0x00, no need to do it again image.cubemap = Mem_Realloc( host.imagepool, image.cubemap, image.ptr + image.size ); image.ptr += image.size; // move to next @@ -345,10 +333,8 @@ load_internal: } } - if( !image.loadformats || image.loadformats->ext == NULL ) - MsgDev( D_NOTE, "FS_LoadImage: imagelib offline\n" ); - else if( filename[0] != '#' ) - MsgDev( D_WARN, "FS_LoadImage: couldn't load \"%s\"\n", loadname ); + if( filename[0] != '#' ) + Con_Reportf( S_WARN "FS_LoadImage: couldn't load \"%s\"\n", loadname ); // clear any force flags image.force_flags = 0; @@ -390,7 +376,7 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) if( pix->flags & IMAGE_SKYBOX ) box = skybox_qv1; else if( pix->flags & IMAGE_CUBEMAP ) - box = cubemap_v2; + box = cubemap_v1; else { // clear any force flags @@ -456,13 +442,10 @@ free RGBA buffer */ void FS_FreeImage( rgbdata_t *pack ) { - if( pack ) - { - if( pack->buffer ) Mem_Free( pack->buffer ); - if( pack->palette ) Mem_Free( pack->palette ); - Mem_Free( pack ); - } - else MsgDev( D_WARN, "FS_FreeImage: trying to free NULL image\n" ); + if( !pack ) return; + if( pack->buffer ) Mem_Free( pack->buffer ); + if( pack->palette ) Mem_Free( pack->palette ); + Mem_Free( pack ); } /* diff --git a/engine/common/imagelib/img_quant.c b/engine/common/imagelib/img_quant.c index c44569b4..92af3809 100644 --- a/engine/common/imagelib/img_quant.c +++ b/engine/common/imagelib/img_quant.c @@ -245,15 +245,14 @@ int inxsearch( int r, int g, int b ) // Search for biased BGR values int contest( int r, int g, int b ) { - // finds closest neuron (min dist) and updates freq - // finds best neuron (min dist-bias) and returns position - // for frequently chosen neurons, freq[i] is high and bias[i] is negative - // bias[i] = gamma * ((1 / netsize) - freq[i]) - register int *p, *f, *n; register int i, dist, a, biasdist, betafreq; int bestpos, bestbiaspos, bestd, bestbiasd; + // finds closest neuron (min dist) and updates freq + // finds best neuron (min dist-bias) and returns position + // for frequently chosen neurons, freq[i] is high and bias[i] is negative + // bias[i] = gamma * ((1 / netsize) - freq[i]) bestd = ~(1<<31); bestbiasd = bestd; bestpos = -1; diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 640e584a..e8e48141 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -250,7 +250,7 @@ qboolean Image_ValidSize( const char *name ) { if( image.width > IMAGE_MAXWIDTH || image.height > IMAGE_MAXHEIGHT || image.width <= 0 || image.height <= 0 ) { - MsgDev( D_ERROR, "Image: %s has invalid sizes %i x %i\n", name, image.width, image.height ); + Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width, image.height ); return false; } return true; @@ -260,7 +260,7 @@ qboolean Image_LumpValidSize( const char *name ) { if( image.width > LUMP_MAXWIDTH || image.height > LUMP_MAXHEIGHT || image.width <= 0 || image.height <= 0 ) { - MsgDev(D_WARN, "Image_LumpValidSize: (%s) dims out of range[%dx%d]\n", name, image.width,image.height ); + Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width,image.height ); return false; } return true; @@ -309,7 +309,6 @@ void Image_SetPalette( const byte *pal, uint *d_table ) rgba[3] = i; d_table[i] = *(uint *)rgba; } -// d_table[0] = 0x00808080; break; case LUMP_MASKED: for( i = 0; i < 255; i++ ) @@ -390,11 +389,11 @@ void Image_GetPaletteLMP( const byte *pal, int rendermode ) Image_GetPaletteQ1(); break; case LUMP_HALFLIFE: - Image_GetPaletteHL(); // default half-life palette + Image_GetPaletteHL(); break; default: - MsgDev( D_ERROR, "Image_GetPaletteLMP: invalid palette specified\n" ); - Image_GetPaletteHL(); // defaulting to half-life palette + // defaulting to half-life palette + Image_GetPaletteHL(); break; } } @@ -574,17 +573,8 @@ qboolean Image_Copy8bitRGBA( const byte *in, byte *out, int pixels ) byte *col; int i; - if( !image.d_currentpal ) - { - MsgDev( D_ERROR, "Image_Copy8bitRGBA: no palette set\n" ); + if( !in || !image.d_currentpal ) return false; - } - - if( !in ) - { - MsgDev( D_ERROR, "Image_Copy8bitRGBA: no input image\n" ); - return false; - } // this is a base image with luma - clear luma pixels if( image.flags & IMAGE_HAS_LUMA ) @@ -741,7 +731,7 @@ void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void * if( yi != oldy ) { inrow = (byte *)indata + inwidth4 * yi; - if (yi == oldy+1) memcpy( resamplerow1, resamplerow2, outwidth4 ); + if( yi == oldy + 1 ) memcpy( resamplerow1, resamplerow2, outwidth4 ); else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth ); Image_Resample32LerpLine( inrow + inwidth4, resamplerow2, inwidth, outwidth ); oldy = yi; @@ -806,7 +796,7 @@ void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void * { if( yi != oldy ) { - inrow = (byte *)indata + inwidth4*yi; + inrow = (byte *)indata + inwidth4 * yi; if( yi == oldy + 1 ) memcpy( resamplerow1, resamplerow2, outwidth4 ); else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth); oldy = yi; @@ -1085,7 +1075,6 @@ byte *Image_ResampleInternal( const void *indata, int inwidth, int inheight, int else Image_Resample32Nolerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight ); break; default: - MsgDev( D_WARN, "Image_Resample: unsupported format %s\n", PFDesc[type].name ); *resampled = false; return (byte *)indata; } @@ -1105,9 +1094,9 @@ byte *Image_FlipInternal( const byte *in, word *srcwidth, word *srcheight, int t word width = *srcwidth; word height = *srcheight; int samples = PFDesc[type].bpp; - qboolean flip_x = ( flags & IMAGE_FLIP_X ) ? true : false; - qboolean flip_y = ( flags & IMAGE_FLIP_Y ) ? true : false; - qboolean flip_i = ( flags & IMAGE_ROT_90 ) ? true : false; + qboolean flip_x = FBitSet( flags, IMAGE_FLIP_X ) ? true : false; + qboolean flip_y = FBitSet( flags, IMAGE_FLIP_Y ) ? true : false; + qboolean flip_i = FBitSet( flags, IMAGE_ROT_90 ) ? true : false; int row_inc = ( flip_y ? -samples : samples ) * width; int col_inc = ( flip_x ? -samples : samples ); int row_ofs = ( flip_y ? ( height - 1 ) * width * samples : 0 ); @@ -1130,7 +1119,6 @@ byte *Image_FlipInternal( const byte *in, word *srcwidth, word *srcheight, int t image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, width * height * samples ); break; default: - MsgDev( D_WARN, "Image_Flip: unsupported format %s\n", PFDesc[type].name ); return (byte *)in; } @@ -1334,10 +1322,7 @@ rgbdata_t *Image_LightGamma( rgbdata_t *pic ) qboolean Image_RemapInternal( rgbdata_t *pic, int topColor, int bottomColor ) { if( !pic->palette ) - { - MsgDev( D_ERROR, "Image_Remap: palette is missed\n" ); return false; - } switch( pic->type ) { @@ -1347,7 +1332,6 @@ qboolean Image_RemapInternal( rgbdata_t *pic, int topColor, int bottomColor ) Image_ConvertPalTo24bit( pic ); break; default: - MsgDev( D_ERROR, "Image_Remap: unsupported format %s\n", PFDesc[pic->type].name ); return false; } @@ -1495,7 +1479,6 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgf // check for buffers if( !pic || !pic->buffer ) { - MsgDev( D_WARN, "Image_Process: NULL image\n" ); image.force_flags = 0; return false; } @@ -1540,7 +1523,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgf if( resampled ) // resampled or filled { - MsgDev( D_NOTE, "Image_Resample: from[%d x %d] to [%d x %d]\n", pic->width, pic->height, w, h ); + Con_Reportf( "Image_Resample: from[%d x %d] to [%d x %d]\n", pic->width, pic->height, w, h ); pic->width = w, pic->height = h; pic->size = w * h * PFDesc[pic->type].bpp; Mem_Free( pic->buffer ); // free original image buffer diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index e64c304b..83fae094 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -384,13 +384,13 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) // NOTE: decals with 'blue base' can be interpret as colored decals if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal[765] == 0 && pal[766] == 0 && pal[767] == 255 )) { + SetBits( image.flags, IMAGE_ONEBIT_ALPHA ); rendermode = LUMP_MASKED; - image.flags |= IMAGE_ONEBIT_ALPHA; } else { // classic gradient decals - image.flags |= IMAGE_COLORINDEX; + SetBits( image.flags, IMAGE_COLORINDEX ); rendermode = LUMP_GRADIENT; } @@ -405,8 +405,8 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) // this is a good reason for using fullbright pixels pal_type = Image_ComparePalette( pal ); - // check for luma pixels (but ignore liquid textures, this a Xash3D limitation) - if( mip.name[0] != '!' && pal_type == PAL_QUAKE1 ) + // check for luma pixels (but ignore liquid textures because they have no lightmap) + if( mip.name[0] != '*' && mip.name[0] != '!' && pal_type == PAL_QUAKE1 ) { for( i = 0; i < image.width * image.height; i++ ) { @@ -440,11 +440,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) { if( fin[i] > 224 && fin[i] != 255 ) { - // don't apply luma to water surfaces because - // we use glpoly->next for store luma chain each frame - // and can't modify glpoly_t because many-many HL mods - // expected unmodified glpoly_t and can crashes on changed struct - // water surfaces uses glpoly->next as pointer to subdivided surfaces (as q1) + // don't apply luma to water surfaces because they have no lightmap if( mip.name[0] != '*' && mip.name[0] != '!' ) image.flags |= IMAGE_HAS_LUMA; break; @@ -503,8 +499,9 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) // calc the decal reflectivity image.fogParams[3] = VectorAvg( image.fogParams ); } - else if( pal != NULL )// calc texture reflectivity + else if( pal != NULL ) { + // calc texture reflectivity for( i = 0; i < 256; i++ ) { reflectivity[0] += pal[i*3+0]; diff --git a/engine/common/infostring.c b/engine/common/infostring.c index fd299ac1..7f7594e3 100644 --- a/engine/common/infostring.c +++ b/engine/common/infostring.c @@ -416,7 +416,7 @@ qboolean Info_SetValueForStarKey( char *s, const char *key, const char *value, i if( Q_strstr( key, "\\" ) || Q_strstr( value, "\\" )) { - MsgDev( D_ERROR, "SetValueForKey: can't use keys or values with a \\\n" ); + Con_Printf( S_ERROR "SetValueForKey: can't use keys or values with a \\\n" ); return false; } @@ -425,15 +425,12 @@ qboolean Info_SetValueForStarKey( char *s, const char *key, const char *value, i if( Q_strstr( key, "\"" ) || Q_strstr( value, "\"" )) { - MsgDev( D_ERROR, "SetValueForKey: can't use keys or values with a \"\n" ); + Con_Printf( S_ERROR "SetValueForKey: can't use keys or values with a \"\n" ); return false; } if( Q_strlen( key ) > ( MAX_KV_SIZE - 1 ) || Q_strlen( value ) > ( MAX_KV_SIZE - 1 )) - { - MsgDev( D_ERROR, "SetValueForKey: keys and values must be < %i characters.\n", MAX_KV_SIZE ); return false; - } Info_RemoveKey( s, key ); @@ -458,14 +455,12 @@ qboolean Info_SetValueForStarKey( char *s, const char *key, const char *value, i if( largekey[0] == 0 ) { // no room to add setting - MsgDev( D_ERROR, "SetValueForKey: info string length exceeded\n" ); return true; // info changed, new value can't saved } } else { // no room to add setting - MsgDev( D_ERROR, "SetValueForKey: info string length exceeded\n" ); return true; // info changed, new value can't saved } } @@ -492,7 +487,7 @@ qboolean Info_SetValueForKey( char *s, const char *key, const char *value, int m { if( key[0] == '*' ) { - MsgDev( D_ERROR, "Can't set *keys\n" ); + Con_Printf( S_ERROR "Can't set *keys\n" ); return false; } diff --git a/engine/common/keys.c b/engine/common/keys.c index 65d2e527..8ddcc66e 100644 --- a/engine/common/keys.c +++ b/engine/common/keys.c @@ -20,6 +20,7 @@ GNU General Public License for more details. typedef struct key_s { qboolean down; + qboolean gamedown; int repeats; // if > 1, it is autorepeating const char *binding; } key_t; @@ -265,16 +266,27 @@ const char *Key_GetBinding( int keynum ) Key_GetKey =================== */ -int Key_GetKey( const char *binding ) +int Key_GetKey( const char *pBinding ) { int i; - if( !binding ) return -1; + if( !pBinding ) return -1; for( i = 0; i < 256; i++ ) { - if( keys[i].binding && !Q_stricmp( binding, keys[i].binding )) - return i; + if( !keys[i].binding ) + continue; + + if( *keys[i].binding == '+' ) + { + if( !Q_strnicmp( keys[i].binding + 1, pBinding, Q_strlen( pBinding ))) + return i; + } + else + { + if( !Q_strnicmp( keys[i].binding, pBinding, Q_strlen( pBinding ))) + return i; + } } return -1; @@ -459,18 +471,17 @@ void Key_Init( void ) /* =================== -Key_AddKeyUpCommands +Key_AddKeyCommands =================== */ -void Key_AddKeyUpCommands( int key, const char *kb ) +void Key_AddKeyCommands( int key, const char *kb, qboolean down ) { - int i; - char button[1024], *buttonPtr; + char button[1024]; + char *buttonPtr; char cmd[1024]; - qboolean keyevent; + int i; if( !kb ) return; - keyevent = false; buttonPtr = button; for( i = 0; ; i++ ) @@ -481,18 +492,15 @@ void Key_AddKeyUpCommands( int key, const char *kb ) if( button[0] == '+' ) { // button commands add keynum as a parm - Q_sprintf( cmd, "-%s %i\n", button+1, key ); + if( down ) Q_sprintf( cmd, "%s %i\n", button, key ); + else Q_sprintf( cmd, "-%s %i\n", button + 1, key ); Cbuf_AddText( cmd ); - keyevent = true; } - else + else if( down ) { - if( keyevent ) - { - // down-only command - Cbuf_AddText( button ); - Cbuf_AddText( "\n" ); - } + // down-only command + Cbuf_AddText( button ); + Cbuf_AddText( "\n" ); } buttonPtr = button; @@ -505,6 +513,29 @@ void Key_AddKeyUpCommands( int key, const char *kb ) } } +/* +=================== +Key_IsAllowedAutoRepeat + +List of keys that allows auto-repeat +=================== +*/ +qboolean Key_IsAllowedAutoRepeat( int key ) +{ + switch( key ) + { + case K_BACKSPACE: + case K_PAUSE: + case K_PGUP: + case K_KP_PGUP: + case K_PGDN: + case K_KP_PGDN: + return true; + default: + return false; + } +} + /* =================== Key_Event @@ -515,30 +546,58 @@ Called by the system for both key up and key down events void Key_Event( int key, qboolean down ) { const char *kb; - char cmd[1024]; // key was pressed before engine was run if( !keys[key].down && !down ) return; - // update auto-repeat status and BUTTON_ANY status + kb = keys[key].binding; keys[key].down = down; +#ifdef HACKS_RELATED_HLMODS + if(( cls.key_dest == key_game ) && ( cls.state == ca_cinematic ) && ( key != K_ESCAPE || !down )) + { + // only escape passed when cinematic is playing + // HLFX 0.6 bug: crash in vgui3.dll while press +attack during movie playback + return; + } +#endif + // distribute the key down event to the apropriate handler + if( cls.key_dest == key_game && ( down || keys[key].gamedown )) + { + if( !clgame.dllFuncs.pfnKey_Event( down, key, keys[key].binding )) + { + if( keys[key].repeats == 0 && down ) + { + keys[key].gamedown = true; + } + + if( !down ) + { + keys[key].gamedown = false; + keys[key].repeats = 0; + } + return; // handled in client.dll + } + } + + // update auto-repeat status if( down ) { keys[key].repeats++; - if( key != K_BACKSPACE && key != K_PAUSE && keys[key].repeats > 1 ) + if( !Key_IsAllowedAutoRepeat( key ) && keys[key].repeats > 1 ) { - if( cls.key_dest == key_game ) - { - // ignore most autorepeats - return; - } + // ignore most autorepeats + return; } + + if( key >= 200 && !kb ) + Con_Printf( "%s is unbound.\n", Key_KeynumToString( key )); } else { + keys[key].gamedown = false; keys[key].repeats = 0; } @@ -563,13 +622,13 @@ void Key_Event( int key, qboolean down ) switch( cls.key_dest ) { case key_game: - if( gl_showtextures->value ) + if( CVAR_TO_BOOL( gl_showtextures )) { // close texture atlas Cvar_SetValue( "r_showtextures", 0.0f ); return; } - if( host.mouse_visible && cls.state != ca_cinematic ) + else if( host.mouse_visible && cls.state != ca_cinematic ) { clgame.dllFuncs.pfnKey_Event( down, key, keys[key].binding ); return; // handled in client.dll @@ -586,9 +645,7 @@ void Key_Event( int key, qboolean down ) case key_menu: UI_KeyEvent( key, true ); return; - default: - MsgDev( D_ERROR, "Key_Event: bad cls.key_dest\n" ); - return; + default: return; } } @@ -605,72 +662,14 @@ void Key_Event( int key, qboolean down ) // an action started before a mode switch. if( !down ) { - kb = keys[key].binding; - - if( cls.key_dest == key_game && ( key != K_ESCAPE )) - clgame.dllFuncs.pfnKey_Event( down, key, kb ); - - Key_AddKeyUpCommands( key, kb ); + Key_AddKeyCommands( key, kb, down ); return; } // distribute the key down event to the apropriate handler if( cls.key_dest == key_game ) { - if( cls.state == ca_cinematic && ( key != K_ESCAPE || !down )) - { - // only escape passed when cinematic is playing - // HLFX 0.6 bug: crash in vgui3.dll while press +attack during movie playback - return; - } - - // send the bound action - kb = keys[key].binding; - - if( !clgame.dllFuncs.pfnKey_Event( down, key, keys[key].binding )) - { - // handled in client.dll - } - else if( kb != NULL ) - { - if( kb[0] == '+' ) - { - int i; - char button[1024], *buttonPtr; - - for( i = 0, buttonPtr = button; ; i++ ) - { - if( kb[i] == ';' || !kb[i] ) - { - *buttonPtr = '\0'; - if( button[0] == '+' ) - { - Q_sprintf( cmd, "%s %i\n", button, key ); - Cbuf_AddText( cmd ); - } - else - { - // down-only command - Cbuf_AddText( button ); - Cbuf_AddText( "\n" ); - } - - buttonPtr = button; - while (( kb[i] <= ' ' || kb[i] == ';' ) && kb[i] != 0 ) - i++; - } - - *buttonPtr++ = kb[i]; - if( !kb[i] ) break; - } - } - else - { - // down-only command - Cbuf_AddText( kb ); - Cbuf_AddText( "\n" ); - } - } + Key_AddKeyCommands( key, kb, down ); } else if( cls.key_dest == key_console ) { @@ -730,6 +729,7 @@ void Key_ClearStates( void ) keys[i].down = 0; keys[i].repeats = 0; + keys[i].gamedown = 0; } if( clgame.hInstance ) diff --git a/engine/common/library.c b/engine/common/library.c index 67d67fad..26115d7c 100644 --- a/engine/common/library.c +++ b/engine/common/library.c @@ -187,7 +187,7 @@ static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta ) *patchAddrHL += delta; break; default: - MsgDev( D_ERROR, "PerformBaseRelocation: unknown relocation: %d\n", type ); + Con_Reportf( S_ERROR "PerformBaseRelocation: unknown relocation: %d\n", type ); break; } } @@ -272,7 +272,7 @@ static int BuildImportTable( MEMORYMODULE *module ) if( handle == NULL ) { - MsgDev( D_ERROR, "couldn't load library %s\n", libname ); + Con_Printf( S_ERROR "couldn't load library %s\n", libname ); result = 0; break; } @@ -294,20 +294,23 @@ static int BuildImportTable( MEMORYMODULE *module ) for( ; *thunkRef; thunkRef++, funcRef++ ) { + LPCSTR funcName; + if( IMAGE_SNAP_BY_ORDINAL( *thunkRef )) { - LPCSTR funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef ); + funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef ); *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); } else { PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS( codeBase, *thunkRef ); - LPCSTR funcName = (LPCSTR)&thunkData->Name; + funcName = (LPCSTR)&thunkData->Name; *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); } if( *funcRef == 0 ) { + Con_Printf( S_ERROR "%s unable to find address: %s\n", libname, funcName ); result = 0; break; } @@ -470,7 +473,7 @@ library_error: // cleanup if( data ) Mem_Free( data ); MemoryFreeLibrary( result ); - MsgDev( D_ERROR, "LoadLibrary: %s\n", errorstring ); + Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring ); return NULL; } @@ -816,7 +819,7 @@ void *COM_GetProcAddress( void *hInstance, const char *name ) if( hInst->custom_loader ) return (void *)MemoryGetProcAddress( hInst->hInstance, name ); - return (void *)GetProcAddress( hInst->hInstance, GetMSVCName( name )); + return (void *)GetProcAddress( hInst->hInstance, name ); } void COM_FreeLibrary( void *hInstance ) @@ -829,10 +832,10 @@ void COM_FreeLibrary( void *hInstance ) if( host.status == HOST_CRASHED ) { // we need to hold down all modules, while MSVC can find error - MsgDev( D_NOTE, "Sys_FreeLibrary: hold %s for debugging\n", hInst->dllName ); + Con_Reportf( "Sys_FreeLibrary: hold %s for debugging\n", hInst->dllName ); return; } - else MsgDev( D_NOTE, "Sys_FreeLibrary: Unloading %s\n", hInst->dllName ); + else Con_Reportf( "Sys_FreeLibrary: Unloading %s\n", hInst->dllName ); if( hInst->custom_loader ) MemoryFreeLibrary( hInst->hInstance ); diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c index 84e02d95..64dcaace 100644 --- a/engine/common/mathlib.c +++ b/engine/common/mathlib.c @@ -135,6 +135,7 @@ void RoundUpHullSize( vec3_t size ) value = size[i]; if( value < 0.0f ) negative = true; value = Q_ceil( fabs( value )); + result = Q_ceil( size[i] ); // lookup hull table to find nearest supposed value for( j = 0; j < NUM_HULL_ROUNDS; j++ ) @@ -321,6 +322,25 @@ void SinCos( float radians, float *sine, float *cosine ) } } +/* +============== +VectorCompareEpsilon + +============== +*/ +qboolean VectorCompareEpsilon( const vec3_t vec1, const vec3_t vec2, vec_t epsilon ) +{ + vec_t ax, ay, az; + + ax = fabs( vec1[0] - vec2[0] ); + ay = fabs( vec1[1] - vec2[1] ); + az = fabs( vec1[2] - vec2[2] ); + + if(( ax <= epsilon ) && ( ay <= epsilon ) && ( az <= epsilon )) + return true; + return false; +} + float VectorNormalizeLength2( const vec3_t v, vec3_t out ) { float length, ilength; diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 598eadb5..2b898780 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -69,7 +69,7 @@ GNU General Public License for more details. #define Q_recip( a ) ((float)(1.0f / (float)(a))) #define Q_floor( a ) ((float)(long)(a)) #define Q_ceil( a ) ((float)(long)((a) + 1)) - +#define Q_round( x, y ) (floor( x / y + 0.5 ) * y ) #define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f))) #define IS_NAN(x) (((*(int *)&x) & (255<<23)) == (255<<23)) @@ -131,6 +131,7 @@ int PlaneTypeForNormal( const vec3_t normal ); int NearestPOW( int value, qboolean roundDown ); void SinCos( float radians, float *sine, float *cosine ); float VectorNormalizeLength2( const vec3_t v, vec3_t out ); +qboolean VectorCompareEpsilon( const vec3_t vec1, const vec3_t vec2, vec_t epsilon ); void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up ); void VectorAngles( const float *forward, float *angles ); void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up ); @@ -166,10 +167,12 @@ void Matrix3x4_ConcatTransforms( matrix3x4 out, const matrix3x4 in1, const matri void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin ); void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale ); void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist ); +void Matrix3x4_TransformAABB( const matrix3x4 world, const vec3_t mins, const vec3_t maxs, vec3_t absmin, vec3_t absmax ); void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z ); void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 ); void Matrix3x4_OriginFromMatrix( const matrix3x4 in, float *out ); void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out ); +void Matrix3x4_Transpose( matrix3x4 out, const matrix3x4 in1 ); #define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, matrix4x4_identity ) #define Matrix4x4_Copy( out, in ) memcpy( out, in, sizeof( matrix4x4 )) diff --git a/engine/common/matrixlib.c b/engine/common/matrixlib.c index fdeea667..d656d53a 100644 --- a/engine/common/matrixlib.c +++ b/engine/common/matrixlib.c @@ -251,6 +251,47 @@ void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 ) out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]); } +void Matrix3x4_Transpose( matrix3x4 out, const matrix3x4 in1 ) +{ + // transpose only rotational component + out[0][0] = in1[0][0]; + out[0][1] = in1[1][0]; + out[0][2] = in1[2][0]; + out[1][0] = in1[0][1]; + out[1][1] = in1[1][1]; + out[1][2] = in1[2][1]; + out[2][0] = in1[0][2]; + out[2][1] = in1[1][2]; + out[2][2] = in1[2][2]; + + // copy origin + out[0][3] = in1[0][3]; + out[1][3] = in1[1][3]; + out[2][3] = in1[2][3]; +} + +/* +================== +Matrix3x4_TransformAABB +================== +*/ +void Matrix3x4_TransformAABB( const matrix3x4 world, const vec3_t mins, const vec3_t maxs, vec3_t absmin, vec3_t absmax ) +{ + vec3_t localCenter, localExtents; + vec3_t worldCenter, worldExtents; + + VectorAverage( mins, maxs, localCenter ); + VectorSubtract( maxs, localCenter, localExtents ); + + Matrix3x4_VectorTransform( world, localCenter, worldCenter ); + worldExtents[0] = DotProductAbs( localExtents, world[0] ); // auto-transposed! + worldExtents[1] = DotProductAbs( localExtents, world[1] ); + worldExtents[2] = DotProductAbs( localExtents, world[2] ); + + VectorSubtract( worldCenter, worldExtents, absmin ); + VectorAdd( worldCenter, worldExtents, absmax ); +} + const matrix4x4 matrix4x4_identity = { { 1, 0, 0, 0 }, // PITCH diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 016bfb1e..c357f0bf 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -135,7 +135,6 @@ typedef struct int lightmap_samples; // samples per lightmap (1 or 3) int version; // model version qboolean isworld; - qboolean vis_errors; // don't spam about vis decompression errors } dbspmodel_t; typedef struct @@ -179,6 +178,7 @@ world_static_t world; static dbspmodel_t srcmodel; static loadstat_t loadstat; static model_t *worldmodel; +static byte g_visdata[(MAX_MAP_LEAFS+7)/8]; // intermediate buffer static mlumpstat_t worldstats[HEADER_LUMPS+EXTRA_LUMPS]; static mlumpinfo_t srclumps[HEADER_LUMPS] = { @@ -291,7 +291,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, { if( !FBitSet( flags, LUMP_SILENT )) { - MsgDev( D_WARN, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + Con_DPrintf( S_WARN "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); loadstat.numwarnings++; } } @@ -299,7 +299,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, { // it has the mincount and the lump is completely missed! if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_ERROR, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + Con_DPrintf( S_ERROR "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); loadstat.numerrors++; } } @@ -309,7 +309,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, if( l->filelen % real_entrysize ) { if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_ERROR, "Mod_Load%s: funny lump size\n", msg2 ); + Con_DPrintf( S_ERROR "Mod_Load%s: funny lump size\n", msg2 ); loadstat.numerrors++; return; } @@ -320,7 +320,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, { // it has the mincount and it's smaller than this limit if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_ERROR, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + Con_DPrintf( S_ERROR "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); loadstat.numerrors++; return; } @@ -331,14 +331,14 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, if( FBitSet( info->flags, CHECK_OVERFLOW )) { if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_ERROR, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); + Con_DPrintf( S_ERROR "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); loadstat.numerrors++; return; } else if( !FBitSet( flags, LUMP_SILENT )) { // just throw warning - MsgDev( D_WARN, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); + Con_DPrintf( S_WARN "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); loadstat.numwarnings++; } } @@ -449,62 +449,46 @@ void Mod_PrintWorldStats_f( void ) */ /* =================== -Mod_DecompressVis +Mod_DecompressPVS =================== */ -static void Mod_DecompressVis( dbspmodel_t *bmod, const byte *in, const byte *inend, byte *out, byte *outend ) +byte *Mod_DecompressPVS( const byte *in, int visbytes ) { - byte *outstart = out; + byte *out; int c; - while( out < outend ) - { - if( in == inend ) - { - if( !bmod->vis_errors ) - { - MsgDev( D_WARN, "Mod_DecompressVis: input underrun (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - bmod->vis_errors = true; - } - return; - } + out = g_visdata; - c = *in++; - - if( c ) + if( !in ) + { + // no vis info, so make all visible + while( visbytes ) { - *out++ = c; - } - else - { - if( in == inend ) - { - if( !bmod->vis_errors ) - { - MsgDev( D_NOTE, "Mod_DecompressVis: input underrun (during zero-run) (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - bmod->vis_errors = true; - } - return; - } - - for( c = *in++; c > 0; c-- ) - { - if( out == outend ) - { - if( !bmod->vis_errors ) - { - MsgDev( D_NOTE, "Mod_DecompressVis: output overrun (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - bmod->vis_errors = true; - } - return; - } - *out++ = 0; - } + *out++ = 0xff; + visbytes--; } + return g_visdata; } + + do + { + if( *in ) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + + while( c ) + { + *out++ = 0; + c--; + } + } while( out - g_visdata < visbytes ); + + return g_visdata; } /* @@ -556,7 +540,7 @@ byte *Mod_GetPVSForPoint( const vec3_t p ) } if( leaf && leaf->cluster >= 0 ) - return world.visdata + leaf->cluster * world.visbytes; + return Mod_DecompressPVS( leaf->compressed_vis, world.visbytes ); return NULL; } @@ -589,7 +573,7 @@ static void Mod_FatPVS_RecursiveBSPNode( const vec3_t org, float radius, byte *v // if this leaf is in a cluster, accumulate the vis bits if(((mleaf_t *)node)->cluster >= 0 ) { - byte *vis = world.visdata + ((mleaf_t *)node)->cluster * world.visbytes; + byte *vis = Mod_DecompressPVS( ((mleaf_t *)node)->compressed_vis, world.visbytes ); for( i = 0; i < visbytes; i++ ) visbuffer[i] |= vis[i]; @@ -615,7 +599,7 @@ int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, q bytes = Q_min( bytes, visbytes ); // enable full visibility for some reasons - if( fullvis || !world.visclusters || !leaf || leaf->cluster < 0 ) + if( fullvis || !worldmodel->visdata || !leaf || leaf->cluster < 0 ) { memset( visbuffer, 0xFF, bytes ); return bytes; @@ -863,7 +847,7 @@ static qboolean Mod_CheckWaterAlphaSupport( dbspmodel_t *bmod ) { if(( leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME ) && leaf->cluster >= 0 ) { - pvs = world.visdata + leaf->cluster * world.visbytes; + pvs = Mod_DecompressPVS( leaf->compressed_vis, world.visbytes ); for( j = 0; j < loadmodel->numleafs; j++ ) { @@ -1049,8 +1033,8 @@ static void Mod_CalcSurfaceExtents( msurface_t *surf ) info->lightextents[i] = surf->extents[i]; } - if( !FBitSet( tex->flags, TEX_SPECIAL ) && surf->extents[i] > 4096 ) - MsgDev( D_ERROR, "Bad surface extents %i\n", surf->extents[i] ); + if( !FBitSet( tex->flags, TEX_SPECIAL ) && ( surf->extents[i] > 16384 ) && ( tr.block_size == BLOCK_SIZE_DEFAULT )) + Con_Reportf( S_ERROR "Bad surface extents %i\n", surf->extents[i] ); } } @@ -1259,7 +1243,7 @@ static qboolean Mod_LoadColoredLighting( dbspmodel_t *bmod ) return false; if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg - MsgDev( D_WARN, "%s probably is out of date\n", path ); + Con_Printf( S_WARN "%s probably is out of date\n", path ); in = FS_LoadFile( path, &litdatasize, false ); @@ -1314,7 +1298,7 @@ static void Mod_LoadDeluxemap( dbspmodel_t *bmod ) return; if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg - MsgDev( D_WARN, "%s probably is out of date\n", path ); + Con_Printf( S_WARN "%s probably is out of date\n", path ); in = FS_LoadFile( path, &deluxdatasize, false ); @@ -1331,7 +1315,7 @@ static void Mod_LoadDeluxemap( dbspmodel_t *bmod ) if( deluxdatasize != bmod->lightdatasize ) { - MsgDev( D_ERROR, "%s has mismatched size (%i should be %i)\n", path, deluxdatasize, bmod->lightdatasize ); + Con_Reportf( S_ERROR "%s has mismatched size (%i should be %i)\n", path, deluxdatasize, bmod->lightdatasize ); Mem_Free( in ); return; } @@ -1828,11 +1812,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) mt = (mip_t *)((byte *)in + in->dataofs[i] ); if( !mt->name[0] ) - { - MsgDev( D_WARN, "unnamed texture in %s\n", loadstat.name ); Q_snprintf( mt->name, sizeof( mt->name ), "miptex_%i", i ); - } - tx = Mem_Calloc( loadmodel->mempool, sizeof( *tx )); loadmodel->textures[i] = tx; @@ -1915,7 +1895,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( !tx->gl_texturenum ) { if( host.type != HOST_DEDICATED ) - MsgDev( D_ERROR, "couldn't load %s.mip\n", mt->name ); + Con_DPrintf( S_ERROR "unable to find %s.mip\n", mt->name ); tx->gl_texturenum = tr.defaultTexture; } @@ -1994,7 +1974,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) altanims[altmax] = tx; altmax++; } - else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); + else Con_Printf( S_ERROR "Mod_LoadTextures: bad animating texture %s\n", tx->name ); for( j = i + 1; j < loadmodel->numtextures; j++ ) { @@ -2022,7 +2002,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( num + 1 > altmax ) altmax = num + 1; } - else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); + else Con_Printf( S_ERROR "Mod_LoadTextures: bad animating texture %s\n", tx->name ); } // link them all together @@ -2032,7 +2012,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( !tx2 ) { - MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); + Con_Printf( S_ERROR "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); tx->anim_total = 0; break; } @@ -2050,7 +2030,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( !tx2 ) { - MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); + Con_Printf( S_ERROR "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); tx->anim_total = 0; break; } @@ -2100,11 +2080,7 @@ static void Mod_LoadTexInfo( dbspmodel_t *bmod ) miptex = in->miptex; if( miptex < 0 || miptex > loadmodel->numtextures ) - { - MsgDev( D_WARN, "Mod_LoadTexInfo: bad miptex number %i in '%s'\n", miptex, loadmodel->name ); - miptex = 0; - } - + miptex = 0; // this is possible? out->texture = loadmodel->textures[miptex]; out->flags = in->flags; @@ -2150,11 +2126,7 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) dface32_t *in = &bmod->surfaces32[i]; if(( in->firstedge + in->numedges ) > loadmodel->numsurfedges ) - { - MsgDev( D_ERROR, "bad surface %i from %i\n", i, bmod->numsurfaces ); - continue; - } - + continue; // corrupted level? out->firstedge = in->firstedge; out->numedges = in->numedges; if( in->side ) SetBits( out->flags, SURF_PLANEBACK ); @@ -2171,7 +2143,7 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) if(( in->firstedge + in->numedges ) > loadmodel->numsurfedges ) { - MsgDev( D_ERROR, "bad surface %i from %i\n", i, bmod->numsurfaces ); + Con_Reportf( S_ERROR "bad surface %i from %i\n", i, bmod->numsurfaces ); continue; } @@ -2189,15 +2161,15 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) tex = out->texinfo->texture; if( !Q_strncmp( tex->name, "sky", 3 )) - SetBits( out->flags, SURF_DRAWTILED|SURF_DRAWSKY ); + SetBits( out->flags, SURF_DRAWSKY ); if(( tex->name[0] == '*' && Q_stricmp( tex->name, "*default" )) || tex->name[0] == '!' ) - SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED ); + SetBits( out->flags, SURF_DRAWTURB ); if( !CL_IsQuakeCompatible( )) { if( !Q_strncmp( tex->name, "water", 5 ) || !Q_strnicmp( tex->name, "laser", 5 )) - SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED ); + SetBits( out->flags, SURF_DRAWTURB ); } if( !Q_strncmp( tex->name, "scroll", 6 )) @@ -2259,10 +2231,10 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) if( samples == 1 || samples == 3 ) { bmod->lightmap_samples = (int)samples; - MsgDev( D_REPORT, "lighting: %s\n", (bmod->lightmap_samples == 1) ? "monochrome" : "colored" ); + Con_Reportf( "lighting: %s\n", (bmod->lightmap_samples == 1) ? "monochrome" : "colored" ); bmod->lightmap_samples = Q_max( bmod->lightmap_samples, 1 ); // avoid division by zero } - else MsgDev( D_WARN, "lighting invalid samplecount: %g, defaulting to %i\n", samples, bmod->lightmap_samples ); + else Con_DPrintf( S_WARN "lighting invalid samplecount: %g, defaulting to %i\n", samples, bmod->lightmap_samples ); } } @@ -2340,20 +2312,16 @@ static void Mod_LoadLeafs( dbspmodel_t *bmod ) { mleaf_t *out; int i, j, p; + int visclusters = 0; loadmodel->leafs = out = (mleaf_t *)Mem_Calloc( loadmodel->mempool, bmod->numleafs * sizeof( *out )); loadmodel->numleafs = bmod->numleafs; if( bmod->isworld ) { - // get visleafs from the submodel data - world.visclusters = loadmodel->submodels[0].visleafs; - world.visbytes = (world.visclusters + 7) >> 3; - world.visdata = (byte *)Mem_Malloc( loadmodel->mempool, world.visclusters * world.visbytes ); - world.fatbytes = (world.visclusters + 31) >> 3; - - // enable full visibility as default - memset( world.visdata, 0xFF, world.visclusters * world.visbytes ); + visclusters = loadmodel->submodels[0].visleafs; + world.visbytes = (visclusters + 7) >> 3; + world.fatbytes = (visclusters + 31) >> 3; } for( i = 0; i < bmod->numleafs; i++, out++ ) @@ -2401,22 +2369,15 @@ static void Mod_LoadLeafs( dbspmodel_t *bmod ) { out->cluster = ( i - 1 ); // solid leaf 0 has no visdata - if( out->cluster >= world.visclusters ) + if( out->cluster >= visclusters ) out->cluster = -1; // ignore visofs errors on leaf 0 (solid) if( p >= 0 && out->cluster >= 0 && loadmodel->visdata ) { if( p < bmod->visdatasize ) - { - byte *inrow = loadmodel->visdata + p; - byte *inrowend = loadmodel->visdata + bmod->visdatasize; - byte *outrow = world.visdata + out->cluster * world.visbytes; - byte *outrowend = world.visdata + (out->cluster + 1) * world.visbytes; - - Mod_DecompressVis( bmod, inrow, inrowend, outrow, outrowend ); - } - else MsgDev( D_WARN, "Mod_LoadLeafs: invalid visofs for leaf #%i\n", i ); + out->compressed_vis = loadmodel->visdata + p; + else Con_Reportf( S_WARN "Mod_LoadLeafs: invalid visofs for leaf #%i\n", i ); } } else out->cluster = -1; // no visclusters on bmodels @@ -2510,7 +2471,7 @@ static void Mod_LoadLightVecs( dbspmodel_t *bmod ) if( bmod->deluxdatasize != bmod->lightdatasize ) { if( bmod->deluxdatasize > 0 ) - MsgDev( D_ERROR, "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", bmod->deluxdatasize, bmod->lightdatasize ); + Con_Printf( S_ERROR "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", bmod->deluxdatasize, bmod->lightdatasize ); else Mod_LoadDeluxemap( bmod ); // old method return; } @@ -2529,7 +2490,7 @@ static void Mod_LoadShadowmap( dbspmodel_t *bmod ) if( bmod->shadowdatasize != ( bmod->lightdatasize / 3 )) { if( bmod->shadowdatasize > 0 ) - MsgDev( D_ERROR, "Mod_LoadShadowmap: has mismatched size (%i should be %i)\n", bmod->shadowdatasize, bmod->lightdatasize / 3 ); + Con_Printf( S_ERROR "Mod_LoadShadowmap: has mismatched size (%i should be %i)\n", bmod->shadowdatasize, bmod->lightdatasize / 3 ); return; } @@ -2650,7 +2611,7 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) case QBSP2_VERSION: break; default: - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); + Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); loadstat.numerrors++; return false; } @@ -2712,7 +2673,7 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) if( COM_CheckString( wadvalue )) { wadvalue[Q_strlen( wadvalue ) - 2] = '\0'; // kill the last semicolon - Con_DPrintf( "Wad files required to run the map: \"%s\"\n", wadvalue ); + Con_Reportf( "Wad files required to run the map: \"%s\"\n", wadvalue ); } return true; @@ -2754,7 +2715,7 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s default: // don't early out: let me analyze errors if( !FBitSet( flags, LUMP_SILENT )) - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", name, header->version, HLBSP_VERSION ); + Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", name, header->version, HLBSP_VERSION ); loadstat.numerrors++; break; } diff --git a/engine/common/mod_dbghulls.c b/engine/common/mod_dbghulls.c index ffa96a1e..41d78e82 100644 --- a/engine/common/mod_dbghulls.c +++ b/engine/common/mod_dbghulls.c @@ -775,4 +775,4 @@ void R_DrawModelHull( void ) } pglEnable( GL_TEXTURE_2D ); pglDisable( GL_POLYGON_OFFSET_FILL ); -} +} \ No newline at end of file diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index ef2150ef..73f1768f 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -140,10 +140,8 @@ typedef struct int num_hull_models; // visibility info - byte *visdata; // uncompressed visdata size_t visbytes; // cluster size size_t fatbytes; // fatpvs size - int visclusters; // num visclusters // world bounds vec3_t mins; // real accuracy world bounds diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 7414a86c..23b5a1bd 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -429,7 +429,7 @@ static void SV_StudioSetupBones( model_t *pModel, float frame, int sequence, con { // only show warn if sequence that out of range was specified intentionally if( sequence > mod_studiohdr->numseq ) - MsgDev( D_WARN, "SV_StudioSetupBones: sequence %i/%i out of range for model %s\n", sequence, mod_studiohdr->numseq, mod_studiohdr->name ); + Con_Reportf( S_WARN "SV_StudioSetupBones: sequence %i/%i out of range for model %s\n", sequence, mod_studiohdr->numseq, pModel->name ); sequence = 0; } @@ -783,7 +783,7 @@ studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer ) if( i != STUDIO_VERSION ) { - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", mod->name, i, STUDIO_VERSION ); + Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, STUDIO_VERSION ); return NULL; } @@ -818,7 +818,7 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) if( !thdr ) { - MsgDev( D_WARN, "Mod_LoadStudioModel: %s missing textures file\n", mod->name ); + Con_Printf( S_WARN "Mod_LoadStudioModel: %s missing textures file\n", mod->name ); if( buffer2 ) Mem_Free( buffer2 ); } else @@ -933,7 +933,7 @@ void Mod_InitStudioAPI( void ) pBlendIface = (STUDIOAPI)COM_GetProcAddress( svgame.hInstance, "Server_GetBlendingInterface" ); if( pBlendIface && pBlendIface( SV_BLENDING_INTERFACE_VERSION, &pBlendAPI, &gStudioAPI, &studio_transform, &studio_bones )) { - MsgDev( D_REPORT, "SV_LoadProgs: ^2initailized Server Blending interface ^7ver. %i\n", SV_BLENDING_INTERFACE_VERSION ); + Con_Reportf( "SV_LoadProgs: ^2initailized Server Blending interface ^7ver. %i\n", SV_BLENDING_INTERFACE_VERSION ); return; } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 1d0a5346..64a8da95 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -705,7 +705,7 @@ void Netchan_CheckForCompletion( netchan_t *chan, int stream, int intotalbuffers { if( chan->sock == NS_CLIENT ) { - MsgDev( D_ERROR, "Lost/dropped fragment would cause stall, retrying connection\n" ); + Con_DPrintf( S_ERROR "Lost/dropped fragment would cause stall, retrying connection\n" ); Cbuf_AddText( "reconnect\n" ); } } @@ -714,10 +714,7 @@ void Netchan_CheckForCompletion( netchan_t *chan, int stream, int intotalbuffers // received final message if( c == intotalbuffers ) - { -// MsgDev( D_NOTE, "\n%s: incoming is complete %i bytes waiting\n", ns_strings[chan->sock], size ); chan->incomingready[stream] = true; - } } /* diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index d1d3550d..58cf59bf 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -317,7 +317,7 @@ delta_info_t *Delta_FindStruct( const char *name ) return &dt_info[i]; } - MsgDev( D_WARN, "Struct %s not found in delta_info\n", name ); + Con_DPrintf( S_WARN "Struct %s not found in delta_info\n", name ); // found nothing return NULL; @@ -427,7 +427,7 @@ qboolean Delta_AddField( const char *pStructName, const char *pName, int flags, { if( !Q_strcmp( pField->name, pName )) { - MsgDev( D_NOTE, "Delta_Add: %s->%s already existing\n", pStructName, pName ); + Con_Reportf( "Delta_Add: %s->%s already existing\n", pStructName, pName ); return false; // field already exist } } @@ -436,13 +436,13 @@ qboolean Delta_AddField( const char *pStructName, const char *pName, int flags, pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName ); if( !pFieldInfo ) { - MsgDev( D_ERROR, "Delta_Add: couldn't find description for %s->%s\n", pStructName, pName ); + Con_DPrintf( S_ERROR "Delta_Add: couldn't find description for %s->%s\n", pStructName, pName ); return false; } if( dt->numFields + 1 > dt->maxFields ) { - MsgDev( D_WARN, "Delta_Add: can't add %s->%s encoder list is full\n", pStructName, pName ); + Con_DPrintf( S_WARN "Delta_Add: can't add %s->%s encoder list is full\n", pStructName, pName ); return false; // too many fields specified (duplicated ?) } @@ -543,28 +543,28 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt *delta_script = COM_ParseFile( *delta_script, token ); if( Q_strcmp( token, "(" )) { - MsgDev( D_ERROR, "Delta_ParseField: expected '(', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ParseField: expected '(', found '%s' instead\n", token ); return false; } // read the variable name if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL ) { - MsgDev( D_ERROR, "Delta_ParseField: missing field name\n" ); + Con_DPrintf( S_ERROR "Delta_ParseField: missing field name\n" ); return false; } pFieldInfo = Delta_FindFieldInfo( pInfo, token ); if( !pFieldInfo ) { - MsgDev( D_ERROR, "Delta_ParseField: unable to find field %s\n", token ); + Con_DPrintf( S_ERROR "Delta_ParseField: unable to find field %s\n", token ); return false; } *delta_script = COM_ParseFile( *delta_script, token ); if( Q_strcmp( token, "," )) { - MsgDev( D_ERROR, "Delta_ParseField: expected ',', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ParseField: expected ',', found '%s' instead\n", token ); return false; } @@ -605,7 +605,7 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt if( Q_strcmp( token, "," )) { - MsgDev( D_ERROR, "Delta_ParseField: expected ',', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ParseField: expected ',', found '%s' instead\n", token ); return false; } @@ -613,7 +613,7 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL ) { - MsgDev( D_ERROR, "Delta_ReadField: %s field bits argument is missing\n", pField->name ); + Con_DPrintf( S_ERROR "Delta_ReadField: %s field bits argument is missing\n", pField->name ); return false; } @@ -622,14 +622,14 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt *delta_script = COM_ParseFile( *delta_script, token ); if( Q_strcmp( token, "," )) { - MsgDev( D_ERROR, "Delta_ReadField: expected ',', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ReadField: expected ',', found '%s' instead\n", token ); return false; } // read delta-multiplier if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL ) { - MsgDev( D_ERROR, "Delta_ReadField: %s missing 'multiplier' argument\n", pField->name ); + Con_DPrintf( S_ERROR "Delta_ReadField: %s missing 'multiplier' argument\n", pField->name ); return false; } @@ -640,14 +640,14 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt *delta_script = COM_ParseFile( *delta_script, token ); if( Q_strcmp( token, "," )) { - MsgDev( D_ERROR, "Delta_ReadField: expected ',', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ReadField: expected ',', found '%s' instead\n", token ); return false; } // read delta-postmultiplier if(( *delta_script = COM_ParseFile( *delta_script, token )) == NULL ) { - MsgDev( D_ERROR, "Delta_ReadField: %s missing 'post_multiply' argument\n", pField->name ); + Con_DPrintf( S_ERROR "Delta_ReadField: %s missing 'post_multiply' argument\n", pField->name ); return false; } @@ -663,7 +663,7 @@ qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delt *delta_script = COM_ParseFile( *delta_script, token ); if( Q_strcmp( token, ")" )) { - MsgDev( D_ERROR, "Delta_ParseField: expected ')', found '%s' instead\n", token ); + Con_DPrintf( S_ERROR "Delta_ParseField: expected ')', found '%s' instead\n", token ); return false; } @@ -1911,13 +1911,13 @@ void Delta_AddEncoder( char *name, pfnDeltaEncode encodeFunc ) if( !dt || !dt->bInitialized ) { - MsgDev( D_ERROR, "Delta_AddEncoder: couldn't find delta with specified custom encode %s\n", name ); + Con_DPrintf( S_ERROR "Delta_AddEncoder: couldn't find delta with specified custom encode %s\n", name ); return; } if( dt->customEncode == CUSTOM_NONE ) { - MsgDev( D_ERROR, "Delta_AddEncoder: %s not supposed for custom encoding\n", dt->pName ); + Con_DPrintf( S_ERROR "Delta_AddEncoder: %s not supposed for custom encoding\n", dt->pName ); return; } diff --git a/engine/common/netchan.h b/engine/common/netchan.h index 6805ea45..7fed5438 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -69,6 +69,7 @@ GNU General Public License for more details. #define NET_MAX_MESSAGE PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 ) #define MASTERSERVER_ADR "ms.xash.su:27010" +#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" #define PORT_MASTER 27010 #define PORT_CLIENT 27005 #define PORT_SERVER 27015 diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 41fe15ef..742d065e 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -73,7 +73,7 @@ GNU General Public License for more details. #define svc_director 51 // #define svc_voiceinit 52 // #define svc_voicedata 53 // [byte][short][...] -// reserved +#define svc_deltapacketbones 54 // [short][byte][...] // reserved #define svc_resourcelocation 56 // [string] #define svc_querycvarvalue 57 // [string] diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index 415fd7f0..703905b4 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -117,10 +117,8 @@ load_internal: } } - if( !sound.loadformats || sound.loadformats->ext == NULL ) - MsgDev( D_NOTE, "FS_LoadSound: soundlib offline\n" ); - else if( filename[0] != '#' ) - MsgDev( D_WARN, "FS_LoadSound: couldn't load \"%s\"\n", loadname ); + if( filename[0] != '#' ) + Con_Reportf( S_WARN "FS_LoadSound: couldn't load \"%s\"\n", loadname ); return NULL; } @@ -134,12 +132,9 @@ free WAV buffer */ void FS_FreeSound( wavdata_t *pack ) { - if( pack ) - { - if( pack->buffer ) Mem_Free( pack->buffer ); - Mem_Free( pack ); - } - else MsgDev( D_WARN, "FS_FreeSound: trying to free NULL sound\n" ); + if( !pack ) return; + if( pack->buffer ) Mem_Free( pack->buffer ); + Mem_Free( pack ); } /* @@ -189,9 +184,7 @@ stream_t *FS_OpenStream( const char *filename ) } } - if( !sound.streamformat || sound.streamformat->ext == NULL ) - MsgDev( D_NOTE, "FS_OpenStream: soundlib offline\n" ); - else MsgDev( D_NOTE, "FS_OpenStream: couldn't open \"%s\"\n", loadname ); + Con_Reportf( "FS_OpenStream: couldn't open \"%s\"\n", loadname ); return NULL; } diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index 66d2780f..bc2acbdc 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -131,7 +131,7 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) else size = outsize; memcpy( &sound.wav[bytesWrite], out, size ); - bytesWrite += outsize; + bytesWrite += size; } sound.samples = bytesWrite / ( sound.width * sound.channels ); diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index 8add19be..daef860a 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -200,7 +200,7 @@ qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int out } } - MsgDev( D_NOTE, "Sound_Resample: from[%d bit %d kHz] to [%d bit %d kHz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); + Con_Reportf( "Sound_Resample: from[%d bit %d kHz] to [%d bit %d kHz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); } sc->rate = outrate; @@ -216,10 +216,7 @@ qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ) // check for buffers if( !snd || !snd->buffer ) - { - MsgDev( D_WARN, "Sound_Process: NULL sound\n" ); return false; - } if(( flags & SOUND_RESAMPLE ) && ( width > 0 || rate > 0 )) { diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 8488f97a..79ff1465 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -290,7 +290,7 @@ void Con_CreateConsole( void ) rect.top = 0; rect.bottom = 364; Q_strncpy( FontName, "Fixedsys", sizeof( FontName )); - Q_strncpy( s_wcd.title, va( "Xash3D %g", XASH_VERSION ), sizeof( s_wcd.title )); + Q_strncpy( s_wcd.title, va( "Xash3D %s", XASH_VERSION ), sizeof( s_wcd.title )); Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path )); fontsize = 8; } @@ -312,7 +312,7 @@ void Con_CreateConsole( void ) if( !RegisterClass( &wc )) { // print into log - MsgDev( D_ERROR, "Can't register window class '%s'\n", SYSCONSOLE ); + Con_DPrintf( S_ERROR "Can't register window class '%s'\n", SYSCONSOLE ); return; } @@ -329,7 +329,7 @@ void Con_CreateConsole( void ) s_wcd.hWnd = CreateWindowEx( WS_EX_DLGMODALFRAME, SYSCONSOLE, s_wcd.title, DEDSTYLE, ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1, NULL, NULL, host.hInst, NULL ); if( s_wcd.hWnd == NULL ) { - MsgDev( D_ERROR, "Can't create window '%s'\n", s_wcd.title ); + Con_DPrintf( S_ERROR "Can't create window '%s'\n", s_wcd.title ); return; } @@ -399,7 +399,7 @@ destroy win32 console void Con_DestroyConsole( void ) { // last text message into console or log - MsgDev( D_NOTE, "Sys_FreeLibrary: Unloading xash.dll\n" ); + Con_Reportf( "Sys_FreeLibrary: Unloading xash.dll\n" ); Sys_CloseLog(); @@ -486,7 +486,12 @@ void Sys_InitLog( void ) if( s_wcd.log_active ) { s_wcd.logfile = fopen( s_wcd.log_path, mode ); - if( !s_wcd.logfile ) MsgDev( D_ERROR, "Sys_InitLog: can't create log file %s\n", s_wcd.log_path ); + + if( !s_wcd.logfile ) + { + MSGBOX( va( "can't create log file %s\n", s_wcd.log_path )); + return; + } fprintf( s_wcd.logfile, "=================================================================================\n" ); fprintf( s_wcd.logfile, "\t%s (build %i) started at %s\n", s_wcd.title, Q_buildnum(), Q_timestamp( TIME_FULL )); diff --git a/engine/common/sys_win.c b/engine/common/sys_win.c index 775baf6d..c476ae6e 100644 --- a/engine/common/sys_win.c +++ b/engine/common/sys_win.c @@ -98,7 +98,7 @@ void Sys_SetClipboardData( const byte *buffer, size_t size ) if( SetClipboardData( CF_DIB, hResult ) == NULL ) { - MsgDev( D_ERROR, "unable to write screenshot\n" ); + Con_Printf( S_ERROR "unable to write screenshot\n" ); GlobalFree( hResult ); } CloseClipboard(); @@ -371,7 +371,7 @@ qboolean Sys_LoadLibrary( dll_info_t *dll ) if( !dll->name || !*dll->name ) return false; // nothing to load - MsgDev( D_NOTE, "Sys_LoadLibrary: Loading %s", dll->name ); + Con_Reportf( "Sys_LoadLibrary: Loading %s", dll->name ); if( dll->fcts ) { @@ -398,14 +398,14 @@ qboolean Sys_LoadLibrary( dll_info_t *dll ) goto error; } } - MsgDev( D_NOTE, " - ok\n" ); + Con_Reportf( " - ok\n" ); return true; error: - MsgDev( D_NOTE, " - failed\n" ); + Con_Reportf( " - failed\n" ); Sys_FreeLibrary( dll ); // trying to free if( dll->crash ) Sys_Error( errorstring ); - else MsgDev( D_ERROR, errorstring ); + else Con_DPrintf( "%s%s", S_ERROR, errorstring ); return false; } @@ -427,10 +427,10 @@ qboolean Sys_FreeLibrary( dll_info_t *dll ) if( host.status == HOST_CRASHED ) { // we need to hold down all modules, while MSVC can find error - MsgDev( D_NOTE, "Sys_FreeLibrary: hold %s for debugging\n", dll->name ); + Con_Reportf( "Sys_FreeLibrary: hold %s for debugging\n", dll->name ); return false; } - else MsgDev( D_NOTE, "Sys_FreeLibrary: Unloading %s\n", dll->name ); + else Con_Reportf( "Sys_FreeLibrary: Unloading %s\n", dll->name ); FreeLibrary( dll->link ); dll->link = NULL; diff --git a/engine/common/titles.c b/engine/common/titles.c index 8d77b9ee..23b55fcb 100644 --- a/engine/common/titles.c +++ b/engine/common/titles.c @@ -201,7 +201,7 @@ static int ParseDirective( const char *pText ) } else { - MsgDev( D_ERROR, "unknown token: %s\n", pText ); + Con_DPrintf( S_ERROR "unknown token: %s\n", pText ); } return 1; } @@ -249,7 +249,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if( IsEndOfText( trim )) { - MsgDev( D_ERROR, "TextMessage: unexpected '}' found, line %d\n", lineNumber ); + Con_Reportf( "TextMessage: unexpected '}' found, line %d\n", lineNumber ); return; } Q_strcpy( currentName, trim ); @@ -260,9 +260,9 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) int length = Q_strlen( currentName ); // save name on name heap - if( lastNamePos + length > 16384 ) + if( lastNamePos + length > 32768 ) { - MsgDev( D_ERROR, "TextMessage: error while parsing!\n" ); + Con_Reportf( "TextMessage: error while parsing!\n" ); return; } @@ -285,7 +285,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) } if( IsStartOfText( trim )) { - MsgDev( D_ERROR, "TextMessage: unexpected '{' found, line %d\n", lineNumber ); + Con_Reportf( "TextMessage: unexpected '{' found, line %d\n", lineNumber ); return; } break; @@ -296,12 +296,12 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if( messageCount >= MAX_MESSAGES ) { - MsgDev( D_WARN, "Too many messages in titles.txt, max is %d\n", MAX_MESSAGES ); + Con_Printf( S_WARN "Too many messages in titles.txt, max is %d\n", MAX_MESSAGES ); break; } } - MsgDev( D_NOTE, "TextMessage: parsed %d text messages\n", messageCount ); + Con_Reportf( "TextMessage: parsed %d text messages\n", messageCount ); nameHeapSize = lastNamePos; textHeapSize = 0; @@ -339,7 +339,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) } if(( pCurrentText - (char *)clgame.titles ) != ( textHeapSize + nameHeapSize + messageSize )) - MsgDev( D_ERROR, "TextMessage: overflow text message buffer!\n" ); + Con_DPrintf( S_ERROR "TextMessage: overflow text message buffer!\n" ); clgame.numTitles = messageCount; } \ No newline at end of file diff --git a/engine/common/world.c b/engine/common/world.c index 2178b957..7ec87b45 100644 --- a/engine/common/world.c +++ b/engine/common/world.c @@ -158,7 +158,6 @@ void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t m { if( outmins[i] > outmaxs[i] ) { - MsgDev( D_ERROR, "World_TransformAABB: backwards mins/maxs\n" ); VectorClear( outmins ); VectorClear( outmaxs ); return; diff --git a/engine/common/world.h b/engine/common/world.h index b9e2819e..d5057da3 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -20,10 +20,6 @@ GNU General Public License for more details. #define MOVE_NOMONSTERS 1 // ignore monsters (edicts with flags (FL_MONSTER|FL_FAKECLIENT|FL_CLIENT) set) #define MOVE_MISSILE 2 // extra size for monsters -#define FMOVE_IGNORE_GLASS 0x100 -#define FMOVE_SIMPLEBOX 0x200 -#define FMOVE_MONSTERCLIP 0x400 - #define CONTENTS_NONE 0 // no custom contents specified /* diff --git a/engine/physint.h b/engine/physint.h index 654c09fe..c2538950 100644 --- a/engine/physint.h +++ b/engine/physint.h @@ -140,7 +140,7 @@ typedef struct physics_interface_s // called at end the frame of SV_Physics call void ( *SV_EndFrame )( void ); // obsolete - void (*pfnReserved)( void ); + void (*pfnPrepWorldFrame)( void ); // called through save\restore process void (*pfnCreateEntitiesInRestoreList)( SAVERESTOREDATA *pSaveData, int levelMask, qboolean create_world ); // allocate custom string (e.g. using user implementation of stringtable, not engine strings) diff --git a/engine/server/server.h b/engine/server/server.h index 735021f1..ac19976b 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -387,6 +387,7 @@ extern convar_t sv_unlagpush; extern convar_t sv_unlagsamples; extern convar_t rcon_password; extern convar_t sv_instancedbaseline; +extern convar_t sv_background_freeze; extern convar_t sv_minupdaterate; extern convar_t sv_maxupdaterate; extern convar_t sv_downloadurl; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index f305c8b2..38861d85 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -116,7 +116,7 @@ void SV_RejectConnection( netadr_t from, char *fmt, ... ) Q_vsnprintf( text, sizeof( text ), fmt, argptr ); va_end( argptr ); - MsgDev( D_REPORT, "%s connection refused. Reason: %s\n", NET_AdrToString( from ), text ); + Con_Reportf( "%s connection refused. Reason: %s\n", NET_AdrToString( from ), text ); Netchan_OutOfBandPrint( NS_SERVER, from, "print\n^1Server was reject the connection:^7 %s", text ); Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" ); } @@ -858,7 +858,7 @@ void SV_RemoteCommand( netadr_t from, sizebuf_t *msg ) char remaining[1024]; int i; - MsgDev( D_INFO, "Rcon from %s:\n%s\n", NET_AdrToString( from ), MSG_GetData( msg ) + 4 ); + Con_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( from ), MSG_GetData( msg ) + 4 ); Log_Printf( "Rcon: \"%s\" from \"%s\"\n", MSG_GetData( msg ) + 4, NET_AdrToString( from )); SV_BeginRedirect( from, RD_PACKET, outputbuf, sizeof( outputbuf ) - 16, SV_FlushRedirect ); @@ -872,7 +872,7 @@ void SV_RemoteCommand( netadr_t from, sizebuf_t *msg ) } Cmd_ExecuteString( remaining ); } - else MsgDev( D_ERROR, "Bad rcon_password.\n" ); + else Con_Printf( S_ERROR "Bad rcon_password.\n" ); SV_EndRedirect(); } @@ -1241,10 +1241,11 @@ void SV_PutClientInServer( sv_client_t *cl ) } } +#ifdef HACKS_RELATED_HLMODS // enable dev-mode to prevent crash cheat-protecting from Invasion mod if( FBitSet( ent->v.flags, FL_GODMODE|FL_NOTARGET ) && !Q_stricmp( GI->gamefolder, "invasion" )) SV_ExecuteClientCommand( cl, "test\n" ); - +#endif // refresh the userinfo and movevars // NOTE: because movevars can be changed during the connection process SetBits( cl->flags, FCL_RESEND_USERINFO|FCL_RESEND_MOVEVARS ); @@ -1965,7 +1966,7 @@ void SV_ExecuteClientCommand( sv_client_t *cl, char *s ) { if( !u->func( cl )) Con_Printf( "'%s' is not valid from the console\n", u->name ); - else MsgDev( D_NOTE, "ucmd->%s()\n", u->name ); + else Con_Reportf( "ucmd->%s()\n", u->name ); break; } } @@ -2095,7 +2096,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // user out of band message (must be handled in CL_ConnectionlessPacket) if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf ); } - else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); + else Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } /* @@ -2139,7 +2140,7 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) if( totalcmds < 0 || totalcmds >= CMD_MASK ) { - MsgDev( D_ERROR, "SV_ParseClientMove: %s sending too many commands %i\n", cl->name, totalcmds ); + Con_Reportf( S_ERROR "SV_ParseClientMove: %s sending too many commands %i\n", cl->name, totalcmds ); SV_DropClient( cl, false ); return; } @@ -2162,12 +2163,16 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) if( checksum2 != checksum1 ) { - MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 ); + Con_Reportf( S_ERROR "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 ); return; } cl->packet_loss = packet_loss; + // freeze player for some reasons if loadgame was executed + if( GameState->loadGame ) + return; + // check for pause or frozen if( sv.paused || !CL_IsInGame() || SV_PlayerIsFrozen( player )) { @@ -2274,13 +2279,12 @@ void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg ) SV_AddToResourceList( resource, &cl->resourcesneeded ); } - if( sv_allow_upload.value ) - MsgDev( D_REPORT, "Verifying and uploading resources...\n" ); - totalsize = COM_SizeofResourceList( &cl->resourcesneeded, &ri ); if( totalsize != 0 && sv_allow_upload.value ) { + Con_DPrintf( "Verifying and uploading resources...\n" ); + if( totalsize != 0 ) { Con_DPrintf( "Custom resources total %.2fK\n", totalsize / 1024.0 ); @@ -2314,7 +2318,7 @@ void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg ) SV_ClearResourceList( &cl->resourcesonhand ); return; } - MsgDev( D_REPORT, "resources to request: %s\n", Q_memprint( totalsize )); + Con_DPrintf( "resources to request: %s\n", Q_memprint( totalsize )); } cl->upstate = us_processing; @@ -2334,7 +2338,7 @@ void SV_ParseCvarValue( sv_client_t *cl, sizebuf_t *msg ) if( svgame.dllFuncs2.pfnCvarValue != NULL ) svgame.dllFuncs2.pfnCvarValue( cl->edict, value ); - MsgDev( D_REPORT, "Cvar query response: name:%s, value:%s\n", cl->name, value ); + Con_Reportf( "Cvar query response: name:%s, value:%s\n", cl->name, value ); } /* @@ -2354,7 +2358,7 @@ void SV_ParseCvarValue2( sv_client_t *cl, sizebuf_t *msg ) if( svgame.dllFuncs2.pfnCvarValue2 != NULL ) svgame.dllFuncs2.pfnCvarValue2( cl->edict, requestID, name, value ); - MsgDev( D_REPORT, "Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, name, value ); + Con_Reportf( "Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, name, value ); } /* @@ -2368,7 +2372,6 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) { qboolean move_issued = false; client_frame_t *frame; - char *s; int c; ASSERT( cl->frames != NULL ); @@ -2394,7 +2397,7 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) { if( MSG_CheckOverflow( msg )) { - MsgDev( D_ERROR, "SV_ReadClientMessage: clc_bad\n" ); + Con_DPrintf( S_ERROR "incoming overflow for %s\n", cl->name ); SV_DropClient( cl, false ); return; } @@ -2418,10 +2421,9 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) SV_ParseClientMove( cl, msg ); break; case clc_stringcmd: - s = MSG_ReadString( msg ); - // malicious users may try using too many string commands - SV_ExecuteClientCommand( cl, s ); - if( cl->state == cs_zombie ) return; // disconnect command + SV_ExecuteClientCommand( cl, MSG_ReadString( msg )); + if( cl->state == cs_zombie ) + return; // disconnect command break; case clc_resourcelist: SV_ParseResourceList( cl, msg ); @@ -2436,7 +2438,7 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) SV_ParseCvarValue2( cl, msg ); break; default: - MsgDev( D_ERROR, "clc_bad\n" ); + Con_DPrintf( S_ERROR "%s: clc_bad\n", cl->name ); SV_DropClient( cl, false ); return; } diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 692fe5f1..ea704538 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -248,7 +248,8 @@ void SV_MapBackground_f( void ) if( SV_Active() && !sv.background ) { - MsgDev( D_ERROR, "can't set background map while game is active\n" ); + if( GameState->nextstate == STATE_RUNFRAME ) + Con_Printf( S_ERROR "can't set background map while game is active\n" ); return; } diff --git a/engine/server/sv_custom.c b/engine/server/sv_custom.c index e0fb73c3..4da79d95 100644 --- a/engine/server/sv_custom.c +++ b/engine/server/sv_custom.c @@ -313,7 +313,7 @@ void SV_MoveToOnHandList( sv_client_t *cl, resource_t *pResource ) { if( !pResource ) { - MsgDev( D_REPORT, "Null resource passed to SV_MoveToOnHandList\n" ); + Con_Reportf( "Null resource passed to SV_MoveToOnHandList\n" ); return; } @@ -325,7 +325,7 @@ void SV_AddToResourceList( resource_t *pResource, resource_t *pList ) { if( pResource->pPrev != NULL || pResource->pNext != NULL ) { - MsgDev( D_ERROR, "Resource already linked\n" ); + Con_Reportf( S_ERROR "Resource already linked\n" ); return; } @@ -524,7 +524,7 @@ void SV_BatchUploadRequest( sv_client_t *cl ) } else { - MsgDev( D_ERROR, "Non customization in upload queue!\n" ); + Con_Reportf( S_ERROR "Non customization in upload queue!\n" ); SV_MoveToOnHandList( cl, p ); } } diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index e87e5ab0..98e19aac 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -138,7 +138,7 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ } // if we are full, silently discard entities - if( ents->num_entities < MAX_VISIBLE_PACKET ) + if( ents->num_entities < ( MAX_VISIBLE_PACKET - 1 )) { ents->num_entities++; // entity accepted c_fullsend++; // debug counter @@ -729,7 +729,7 @@ void SV_SendClientDatagram( sv_client_t *cl ) { if( MSG_GetNumBytesWritten( &cl->datagram ) < MSG_GetNumBytesLeft( &msg )) MSG_WriteBits( &msg, MSG_GetData( &cl->datagram ), MSG_GetNumBitsWritten( &cl->datagram )); - else MsgDev( D_WARN, "Ignoring unreliable datagram for %s, would overflow on msg\n", cl->name ); + else Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow on msg\n", cl->name ); } MSG_Clear( &cl->datagram ); @@ -791,14 +791,14 @@ void SV_UpdateToReliableMessages( void ) // clear the server datagram if it overflowed. if( MSG_CheckOverflow( &sv.datagram )) { - MsgDev( D_ERROR, "sv.datagram overflowed!\n" ); + Con_DPrintf( S_ERROR "sv.datagram overflowed!\n" ); MSG_Clear( &sv.datagram ); } // clear the server datagram if it overflowed. if( MSG_CheckOverflow( &sv.spec_datagram )) { - MsgDev( D_ERROR, "sv.spec_datagram overflowed!\n" ); + Con_DPrintf( S_ERROR "sv.spec_datagram overflowed!\n" ); MSG_Clear( &sv.spec_datagram ); } @@ -823,7 +823,7 @@ void SV_UpdateToReliableMessages( void ) } else { - MsgDev( D_WARN, "Ignoring unreliable datagram for %s, would overflow\n", cl->name ); + Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name ); } if( FBitSet( cl->flags, FCL_HLTV_PROXY )) @@ -834,7 +834,7 @@ void SV_UpdateToReliableMessages( void ) } else { - MsgDev( D_WARN, "Ignoring spectator datagram for %s, would overflow\n", cl->name ); + Con_DPrintf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name ); } } } @@ -889,7 +889,7 @@ void SV_SendClientMessages( void ) MSG_Clear( &cl->netchan.message ); MSG_Clear( &cl->datagram ); SV_BroadcastPrintf( NULL, "%s overflowed\n", cl->name ); - MsgDev( D_WARN, "reliable overflow for %s\n", cl->name ); + Con_DPrintf( S_ERROR "reliable overflow for %s\n", cl->name ); SV_DropClient( cl, false ); SetBits( cl->flags, FCL_SEND_NET_MESSAGE ); cl->netchan.cleartime = 0.0; // don't choke this message @@ -1006,6 +1006,12 @@ void SV_InactivateClients( void ) COM_ClearCustomizationList( &cl->customdata, false ); memset( cl->physinfo, 0, MAX_PHYSINFO_STRING ); + + // NOTE: many mods sending messages that must be applied on a next level + // e.g. CryOfFear sending HideHud and PlayMp3 that affected after map change + if( svgame.globals->changelevel ) + continue; + MSG_Clear( &cl->netchan.message ); MSG_Clear( &cl->datagram ); } diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index c15374ad..d5fe83e6 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -1194,24 +1194,29 @@ pfnSetModel */ void pfnSetModel( edict_t *e, const char *m ) { + char name[MAX_QPATH]; model_t *mod; int i; if( !SV_IsValidEdict( e )) return; - if( COM_CheckString( m )) + if( *m == '\\' || *m == '/' ) m++; + Q_strncpy( name, m, sizeof( name )); + COM_FixSlashes( name ); + + if( COM_CheckString( name )) { // check to see if model was properly precached for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) { - if( !Q_stricmp( sv.model_precache[i], m )) + if( !Q_stricmp( sv.model_precache[i], name )) break; } if( i == MAX_MODELS ) { - Con_Printf( S_ERROR "no precache: %s\n", m ); + Con_Printf( S_ERROR "no precache: %s\n", name ); return; } } @@ -1223,7 +1228,7 @@ void pfnSetModel( edict_t *e, const char *m ) return; } - if( COM_CheckString( m )) + if( COM_CheckString( name )) { e->v.model = MAKE_STRING( sv.model_precache[i] ); e->v.modelindex = i; @@ -1250,18 +1255,23 @@ pfnModelIndex */ int pfnModelIndex( const char *m ) { + char name[MAX_QPATH]; int i; if( !COM_CheckString( m )) return 0; + if( *m == '\\' || *m == '/' ) m++; + Q_strncpy( name, m, sizeof( name )); + COM_FixSlashes( name ); + for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) { - if( !Q_stricmp( sv.model_precache[i], m )) + if( !Q_stricmp( sv.model_precache[i], name )) return i; } - Con_Printf( S_ERROR "no precache: %s\n", m ); + Con_Printf( S_ERROR "no precache: %s\n", name ); return 0; } @@ -3421,10 +3431,7 @@ void pfnFadeClientVolume( const edict_t *pEdict, int fadePercent, int fadeOutSec sv_client_t *cl; if(( cl = SV_ClientFromEdict( pEdict, true )) == NULL ) - { - MsgDev( D_ERROR, "SV_FadeClientVolume: client is not spawned!\n" ); return; - } if( FBitSet( cl->flags, FCL_FAKECLIENT )) return; @@ -4575,7 +4582,7 @@ qboolean SV_ParseEdict( char **pfile, edict_t *ent ) COM_ParseVector( &pstart, origin, 3 ); Mem_Free( pkvd[i].szValue ); // release old value, so we don't need these - copystring( va( "%g %g %g", origin[0], origin[1], origin[2] - 16.0f )); + pkvd[i].szValue = copystring( va( "%g %g %g", origin[0], origin[1], origin[2] - 16.0f )); } #endif if( !Q_strcmp( pkvd[i].szKeyName, "light" )) diff --git a/engine/server/sv_log.c b/engine/server/sv_log.c index fa02154a..f2dcc1db 100644 --- a/engine/server/sv_log.c +++ b/engine/server/sv_log.c @@ -73,7 +73,7 @@ void Log_Open( void ) } if( fp ) svs.log.file = fp; - Log_Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i/%.2f/%d\")\n", + Log_Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i/%s/%d\")\n", szTestFile, Info_ValueForKey( SV_Serverinfo(), "*gamedir" ), PROTOCOL_VERSION, XASH_VERSION, Q_buildnum() ); } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index f70ba8c2..b8831156 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -92,6 +92,7 @@ CVAR_DEFINE_AUTO( sv_skyvec_x, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight dir CVAR_DEFINE_AUTO( sv_skyvec_y, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight direction by y-axis" ); CVAR_DEFINE_AUTO( sv_skyvec_z, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight direction by z-axis" ); CVAR_DEFINE_AUTO( sv_wateralpha, "1", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "world surfaces water transparency factor. 1.0 - solid, 0.0 - fully transparent" ); +CVAR_DEFINE_AUTO( sv_background_freeze, "1", FCVAR_ARCHIVE, "freeze player movement on background maps (e.g. to prevent falling)" ); CVAR_DEFINE_AUTO( showtriggers, "0", FCVAR_LATCH, "debug cvar shows triggers" ); CVAR_DEFINE_AUTO( sv_airmove, "1", FCVAR_SERVER, "obsolete, compatibility issues" ); CVAR_DEFINE_AUTO( sv_version, "", FCVAR_READ_ONLY, "engine version string" ); @@ -512,6 +513,9 @@ void SV_PrepWorldFrame( void ) ClearBits( ent->v.effects, EF_MUZZLEFLASH|EF_NOINTERP ); } + + if( svgame.physFuncs.pfnPrepWorldFrame != NULL ) + svgame.physFuncs.pfnPrepWorldFrame(); } /* @@ -648,9 +652,8 @@ void Master_Add( void ) NET_Config( true ); // allow remote if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) - MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); - - NET_SendPacket( NS_SERVER, 2, "q\xFF", adr ); + Con_Printf( "can't resolve adr: %s\n", MASTERSERVER_ADR ); + else NET_SendPacket( NS_SERVER, 2, "q\xFF", adr ); } /* @@ -692,9 +695,8 @@ void Master_Shutdown( void ) NET_Config( true ); // allow remote if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) - MsgDev( D_INFO, "Can't resolve addr: %s\n", MASTERSERVER_ADR ); - - NET_SendPacket( NS_SERVER, 2, "\x62\x0A", adr ); + Con_Printf( "can't resolve addr: %s\n", MASTERSERVER_ADR ); + else NET_SendPacket( NS_SERVER, 2, "\x62\x0A", adr ); } /* @@ -739,7 +741,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) Info_SetValueForKey( s, "os", "w", len ); // Windows Info_SetValueForKey( s, "secure", "0", len ); // server anti-cheat Info_SetValueForKey( s, "lan", "0", len ); // LAN servers doesn't send info to master - Info_SetValueForKey( s, "version", va( "%g", XASH_VERSION ), len ); // server region. 255 -- all regions + Info_SetValueForKey( s, "version", va( "%s", XASH_VERSION ), len ); // server region. 255 -- all regions Info_SetValueForKey( s, "region", "255", len ); // server region. 255 -- all regions Info_SetValueForKey( s, "product", GI->gamefolder, len ); // product? Where is the difference with gamedir? @@ -847,13 +849,14 @@ void SV_Init( void ) Cvar_RegisterVariable (&violence_hgibs); Cvar_RegisterVariable (&mp_logecho); Cvar_RegisterVariable (&mp_logfile); + Cvar_RegisterVariable (&sv_background_freeze); // when we in developer-mode automatically turn cheats on if( host_developer.value ) Cvar_SetValue( "sv_cheats", 1.0f ); MSG_Init( &net_message, "NetMessage", net_message_buffer, sizeof( net_message_buffer )); - Q_snprintf( versionString, sizeof( versionString ), "%s: %.2f,%i,%i", "Xash3D", XASH_VERSION, PROTOCOL_VERSION, Q_buildnum() ); + Q_snprintf( versionString, sizeof( versionString ), "%s: %s,%i,%i", "Xash3D", XASH_VERSION, PROTOCOL_VERSION, Q_buildnum() ); Cvar_FullSet( "sv_version", versionString, FCVAR_READ_ONLY ); SV_ClearGameState (); // delete all temporary *.hl files diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index bacdcb01..533c52e5 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -74,8 +74,8 @@ realcheck: stop[2] = start[2] - 2.0f * svgame.movevars.stepsize; if( iMode == WALKMOVE_WORLDONLY ) - trace = SV_MoveNoEnts( start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent ); - else trace = SV_Move( start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, monsterClip ); + trace = SV_MoveNoEnts( start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent ); + else trace = SV_Move( start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip ); if( trace.fraction == 1.0f ) return false; @@ -91,8 +91,8 @@ realcheck: start[1] = stop[1] = y ? maxs[1] : mins[1]; if( iMode == WALKMOVE_WORLDONLY ) - trace = SV_MoveNoEnts( start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent ); - else trace = SV_Move( start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, monsterClip ); + trace = SV_MoveNoEnts( start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent ); + else trace = SV_Move( start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip ); if( trace.fraction != 1.0f && trace.endpos[2] > bottom ) bottom = trace.endpos[2]; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index a3bcade7..1b4b2ca6 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -815,8 +815,8 @@ trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apush, int if( blocked ) { // more accuracy blocking code - if( flDamage <= 0.0f ) - *blocked = !VectorCompare( ent->v.origin, end ); // can't move full distance + if( flDamage <= 0.0f && FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT )) + *blocked = !VectorCompareEpsilon( ent->v.origin, end, ON_EPSILON ); // can't move full distance else *blocked = true; } @@ -864,9 +864,8 @@ static qboolean SV_CanBlock( edict_t *ent ) if( ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_TRIGGER ) { // clear bounds for deadbody - ent->v.mins[0] = ent->v.mins[1] = 0.0f; - ent->v.maxs[0] = ent->v.maxs[1] = 0.0f; - ent->v.maxs[2] = ent->v.mins[2]; + ent->v.mins[0] = ent->v.mins[1] = 0; + VectorCopy( ent->v.mins, ent->v.maxs ); return false; } @@ -936,7 +935,7 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) if( block ) continue; // if the entity is standing on the pusher, it will definately be moved - if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) + if( !( FBitSet( check->v.flags, FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] @@ -2046,7 +2045,7 @@ qboolean SV_InitPhysicsAPI( void ) { if( pPhysIface( SV_PHYSICS_INTERFACE_VERSION, &gPhysicsAPI, &svgame.physFuncs )) { - MsgDev( D_REPORT, "SV_LoadProgs: ^2initailized extended PhysicAPI ^7ver. %i\n", SV_PHYSICS_INTERFACE_VERSION ); + Con_Reportf( "SV_LoadProgs: ^2initailized extended PhysicAPI ^7ver. %i\n", SV_PHYSICS_INTERFACE_VERSION ); if( svgame.physFuncs.SV_CheckFeatures != NULL ) { diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 5f166a7b..d5198bf6 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -32,6 +32,9 @@ void SV_ClearPhysEnts( void ) qboolean SV_PlayerIsFrozen( edict_t *pClient ) { + if( sv_background_freeze.value && sv.background ) + return true; + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) return false; @@ -352,10 +355,7 @@ static void pfnStuckTouch( int hitent, pmtrace_t *tr ) } if( svgame.pmove->numtouch >= MAX_PHYSENTS ) - { - MsgDev( D_ERROR, "PM_StuckTouch: MAX_TOUCHENTS limit exceeded\n" ); return; - } VectorCopy( svgame.pmove->velocity, tr->deltavelocity ); tr->ent = hitent; @@ -567,7 +567,7 @@ void SV_InitClientMove( void ) for( i = 0; i < MAX_MAP_HULLS; i++ ) { if( svgame.dllFuncs.pfnGetHullBounds( i, host.player_mins[i], host.player_maxs[i] )) - MsgDev( D_NOTE, "SV: hull%i, player_mins: %g %g %g, player_maxs: %g %g %g\n", i, + Con_Reportf( "SV: hull%i, player_mins: %g %g %g, player_maxs: %g %g %g\n", i, host.player_mins[i][0], host.player_mins[i][1], host.player_mins[i][2], host.player_maxs[i][0], host.player_maxs[i][1], host.player_maxs[i][2] ); } diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index daf4b66d..69dc6123 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -422,7 +422,7 @@ static int IsValidSave( void ) } // ignore autosave during background - if( sv.background ) + if( sv.background || UI_CreditsActive( )) return 0; if( svgame.physFuncs.SV_AllowSaveGame != NULL ) @@ -539,9 +539,6 @@ static qboolean SaveGetName( int lastnum, char *filename ) { int a, b, c; - if( !COM_CheckString( filename )) - return false; - if( lastnum < 0 || lastnum > 999 ) return false; @@ -664,7 +661,7 @@ static void SaveClear( SAVERESTOREDATA *pSaveData ) /* ============= -SaveInit +SaveFinish release global save-restore buffer ============= @@ -1790,7 +1787,7 @@ static int CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask } else { - Con_DPrintf( "Transferring %s (%d)\n", STRING( pTable->classname ), NUM_FOR_EDICT( pent )); + Con_Reportf( "Transferring %s (%d)\n", STRING( pTable->classname ), NUM_FOR_EDICT( pent )); if( svgame.dllFuncs.pfnRestore( pent, pSaveData, 0 ) < 0 ) { @@ -1802,7 +1799,7 @@ static int CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask { // this can happen during normal processing - PVS is just a guess, // some map areas won't exist in the new map - Con_DPrintf( "Suppressing %s\n", STRING( pTable->classname )); + Con_Reportf( "Suppressing %s\n", STRING( pTable->classname )); SetBits( pent->v.flags, FL_KILLME ); } else @@ -2014,6 +2011,9 @@ qboolean SV_LoadGame( const char *pPath ) if( host.type == HOST_DEDICATED ) return false; + if( UI_CreditsActive( )) + return false; + if( !COM_CheckString( pPath )) return false; diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index 159d75e3..70d65186 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -28,7 +28,8 @@ typedef struct moveclip_s edict_t *passedict; trace_t trace; int type; // move type - int flags; // trace flags + qboolean ignoretrans; + qboolean monsterclip; } moveclip_t; /* @@ -820,18 +821,18 @@ returns true if the entity is in solid currently */ qboolean SV_TestEntityPosition( edict_t *ent, edict_t *blocker ) { - trace_t trace; qboolean monsterClip = FBitSet( ent->v.flags, FL_MONSTERCLIP ) ? true : false; + trace_t trace; - if( ent->v.flags & (FL_CLIENT|FL_FAKECLIENT)) + if( FBitSet( ent->v.flags, FL_CLIENT|FL_FAKECLIENT )) { // to avoid falling through tracktrain update client mins\maxs here - if( ent->v.flags & FL_DUCKING ) + if( FBitSet( ent->v.flags, FL_DUCKING )) SV_SetMinMaxSize( ent, svgame.pmove->player_mins[1], svgame.pmove->player_maxs[1], true ); else SV_SetMinMaxSize( ent, svgame.pmove->player_mins[0], svgame.pmove->player_maxs[0], true ); } - trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL|FMOVE_SIMPLEBOX, ent, monsterClip ); + trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent, monsterClip ); if( SV_IsValidEdict( blocker ) && SV_IsValidEdict( trace.ent )) { @@ -839,6 +840,7 @@ qboolean SV_TestEntityPosition( edict_t *ent, edict_t *blocker ) return trace.startsolid; return false; } + return trace.startsolid; } @@ -1159,18 +1161,15 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) if( svgame.dllFuncs2.pfnShouldCollide ) { if( !svgame.dllFuncs2.pfnShouldCollide( touch, clip->passedict )) - return true; // originally this was 'return' but is completely wrong! + return true; } // monsterclip filter (solid custom is a static or dynamic bodies) if( touch->v.solid == SOLID_BSP || touch->v.solid == SOLID_CUSTOM ) { - if( FBitSet( touch->v.flags, FL_MONSTERCLIP )) - { - // func_monsterclip works only with monsters that have same flag! - if( !FBitSet( clip->flags, FMOVE_MONSTERCLIP )) - return true; - } + // func_monsterclip works only with monsters that have same flag! + if( FBitSet( touch->v.flags, FL_MONSTERCLIP ) && !clip->monsterclip ) + return true; } else { @@ -1181,7 +1180,7 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) mod = SV_ModelHandle( touch->v.modelindex ); - if( mod && mod->type == mod_brush && FBitSet( clip->flags, FMOVE_IGNORE_GLASS )) + if( mod && mod->type == mod_brush && clip->ignoretrans ) { // we ignore brushes with rendermode != kRenderNormal and without FL_WORLDBRUSH set if( touch->v.rendermode != kRenderNormal && !FBitSet( touch->v.flags, FL_WORLDBRUSH )) @@ -1226,7 +1225,7 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) if( touch->v.solid == SOLID_CUSTOM ) SV_CustomClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); - else if( touch->v.flags & FL_MONSTER ) + else if( FBitSet( touch->v.flags, FL_MONSTER )) SV_ClipMoveToEntity( touch, clip->start, clip->mins2, clip->maxs2, clip->end, &trace ); else SV_ClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); @@ -1363,13 +1362,14 @@ trace_t SV_Move( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, clip.start = start; clip.end = trace_endpos; clip.type = (type & 0xFF); - clip.flags = (type & 0xFF00); + clip.ignoretrans = type >> 8; + clip.monsterclip = false; clip.passedict = (e) ? e : EDICT_NUM( 0 ); clip.mins = mins; clip.maxs = maxs; if( monsterclip && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) - SetBits( clip.flags, FMOVE_MONSTERCLIP ); + clip.monsterclip = true; if( clip.type == MOVE_MISSILE ) { @@ -1422,7 +1422,8 @@ trace_t SV_MoveNoEnts( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_ clip.start = start; clip.end = trace_endpos; clip.type = (type & 0xFF); - clip.flags = (type & 0xFF00); + clip.ignoretrans = type >> 8; + clip.monsterclip = false; clip.passedict = (e) ? e : EDICT_NUM( 0 ); clip.mins = mins; clip.maxs = maxs; From 891c984c567cacb7886e2027f8b0521ce70720af Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 16:04:47 +0700 Subject: [PATCH 035/205] Port some config changes --- engine/common/con_utils.c | 49 ++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 78e110ec..cf175e98 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1182,6 +1182,23 @@ void Cmd_WriteOpenGLVariables( file_t *f ) } #ifndef XASH_DEDICATED + + +#define CFG_END(f,x) \ + if( FS_Printf( f,"// end of " x "\n" ) >= (int)sizeof( "// end of " x "\n" ) - 2 )\ + { \ + FS_Close( f );\ + FS_Delete( x ".bak" ); \ + FS_Rename( x, x ".bak" ); \ + FS_Delete( x ); \ + FS_Rename( x ".new", x );\ + }\ + else\ + {\ + FS_Close( f );\ + MsgDev( D_ERROR, "could not update " x "\n" );\ + } + /* =============== Host_WriteConfig @@ -1195,10 +1212,10 @@ void Host_WriteConfig( void ) kbutton_t *jlook = NULL; file_t *f; - if( !clgame.hInstance ) return; + if( !clgame.hInstance || Sys_CheckParm( "-nowriteconfig" ) ) return; - f = FS_Open( "config.cfg", "w", false ); + f = FS_Open( "config.cfg.new", "w", false ); if( f ) { Con_Reportf( "Host_WriteConfig()\n" ); @@ -1224,7 +1241,7 @@ void Host_WriteConfig( void ) FS_Printf( f, "exec userconfig.cfg" ); - FS_Close( f ); + CFG_END( f, "config.cfg" ); } else Con_DPrintf( S_ERROR "Couldn't write config.cfg.\n" ); @@ -1242,21 +1259,32 @@ save serverinfo variables into server.cfg (using for dedicated server too) void Host_WriteServerConfig( const char *name ) { file_t *f; + string oldconfigfile, newconfigfile; + + Q_snprintf( oldconfigfile, MAX_STRING, "%s.bak", name ); + Q_snprintf( newconfigfile, MAX_STRING, "%s.new", name ); SV_InitGameProgs(); // collect user variables // FIXME: move this out until menu parser is done CSCR_LoadDefaultCVars( "settings.scr" ); - if(( f = FS_Open( name, "w", false )) != NULL ) + if(( f = FS_Open( newconfigfile, "w", false )) != NULL ) { FS_Printf( f, "//=======================================================================\n" ); FS_Printf( f, "//\t\t\tCopyright XashXT Group %s (C)\n", Q_timestamp( TIME_YEAR_ONLY )); FS_Printf( f, "//\t\tgame.cfg - multiplayer server temporare config\n" ); FS_Printf( f, "//=======================================================================\n" ); + Cvar_WriteVariables( f, FCVAR_SERVER ); CSCR_WriteGameCVars( f, "settings.scr" ); + FS_Close( f ); + + FS_Rename( name, oldconfigfile ); + FS_Delete( name ); + FS_Rename( newconfigfile, name ); + FS_Delete( oldconfigfile ); } else Con_DPrintf( S_ERROR "Couldn't write %s.\n", name ); @@ -1274,8 +1302,10 @@ void Host_WriteOpenGLConfig( void ) { file_t *f; + if( Sys_CheckParm( "-nowriteconfig" ) ) + return; - f = FS_Open( "opengl.cfg", "w", false ); + f = FS_Open( "opengl.cfg.new", "w", false ); if( f ) { Con_Reportf( "Host_WriteGLConfig()\n" ); @@ -1285,7 +1315,7 @@ void Host_WriteOpenGLConfig( void ) FS_Printf( f, "//=======================================================================\n" ); FS_Printf( f, "\n" ); Cmd_WriteOpenGLVariables( f ); - FS_Close( f ); + CFG_END( f, "opengl.cfg" ); } else Con_DPrintf( S_ERROR "can't update opengl.cfg.\n" ); } @@ -1301,7 +1331,10 @@ void Host_WriteVideoConfig( void ) { file_t *f; - f = FS_Open( "video.cfg", "w", false ); + if( Sys_CheckParm( "-nowriteconfig" ) ) + return; + + f = FS_Open( "video.cfg.new", "w", false ); if( f ) { Con_Reportf( "Host_WriteVideoConfig()\n" ); @@ -1310,7 +1343,7 @@ void Host_WriteVideoConfig( void ) FS_Printf( f, "//\t\tvideo.cfg - archive of renderer variables\n"); FS_Printf( f, "//=======================================================================\n" ); Cvar_WriteVariables( f, FCVAR_RENDERINFO ); - FS_Close( f ); + CFG_END( f, "video.cfg" ); } else Con_DPrintf( S_ERROR "can't update video.cfg.\n" ); } From e74513556a916e263d64c6f6639273019c2360fb Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 16:05:33 +0700 Subject: [PATCH 036/205] Fix gcc colors --- wscript | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index 4776c906..be1d8dc5 100644 --- a/wscript +++ b/wscript @@ -51,6 +51,10 @@ def options(opt): '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') + opt.add_option( + '--no-gcc-colors', action = 'store_false', dest = 'GCC_COLORS', default = True, + help = 'do not enable gcc colors') + opt.recurse(SUBDIRS) def configure(conf): @@ -91,8 +95,11 @@ def configure(conf): conf.env.append_unique('CFLAGS', ['-O2']) conf.env.append_unique('CXXFLAGS', ['-O2']) else: - conf.env.append_unique('CFLAGS', ['-Og', '-g', '-fdiagnostics-color=always', '-w']) + conf.env.append_unique('CFLAGS', ['-Og', '-g']) conf.env.append_unique('CXXFLAGS', ['-Og', '-g']) + if conf.options.GCC_COLORS: + conf.env.append_unique('CFLAGS', ['-fdiagnostics-color=always']) + conf.env.append_unique('CXXFLAGS', ['-fdiagnostics-color=always']) else: if(conf.options.RELEASE): conf.env.append_unique('CFLAGS', ['/O2']) From a0f0eca77c63e0efe29977dc28690cbb9eda82c7 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 16:38:39 +0700 Subject: [PATCH 037/205] Backup console input when using history --- engine/client/console.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index e12960d5..13c36d8f 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -114,6 +114,7 @@ typedef struct field_t historyLines[CON_HISTORY]; int historyLine; // the line being displayed from history buffer will be <= nextHistoryLine int nextHistoryLine; // the last line in the history buffer, not masked + field_t backup; notify_t notify[MAX_DBG_NOTIFY]; // for Con_NXPrintf qboolean draw_notify; // true if we have NXPrint message @@ -1650,6 +1651,8 @@ void Key_Console( int key ) // command history (ctrl-p ctrl-n for unix style) if(( key == K_MWHEELUP && Key_IsDown( K_SHIFT )) || ( key == K_UPARROW ) || (( Q_tolower(key) == 'p' ) && Key_IsDown( K_CTRL ))) { + if( con.historyLine == con.nextHistoryLine ) + con.backup = con.input; if( con.nextHistoryLine - con.historyLine < CON_HISTORY && con.historyLine > 0 ) con.historyLine--; con.input = con.historyLines[con.historyLine % CON_HISTORY]; @@ -1658,9 +1661,13 @@ void Key_Console( int key ) if(( key == K_MWHEELDOWN && Key_IsDown( K_SHIFT )) || ( key == K_DOWNARROW ) || (( Q_tolower(key) == 'n' ) && Key_IsDown( K_CTRL ))) { - if( con.historyLine == con.nextHistoryLine ) return; - con.historyLine++; - con.input = con.historyLines[con.historyLine % CON_HISTORY]; + if( con.historyLine >= con.nextHistoryLine - 1 ) + con.input = con.backup; + else + { + con.historyLine++; + con.input = con.historyLines[con.historyLine % CON_HISTORY]; + } return; } From b0c077ccf83856f7837d3501b3684ef893b14271 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 4 Oct 2018 18:10:12 +0700 Subject: [PATCH 038/205] Persistent console history --- engine/client/cl_main.c | 1 + engine/client/client.h | 1 + engine/client/console.c | 62 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index b271d7be..a0e56d1c 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2836,6 +2836,7 @@ void CL_Init( void ) MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf )); IN_TouchInit(); + Con_LoadHistory(); if( !CL_LoadProgs( va( "%s/%s", GI->dll_path, SI.clientlib))) Host_Error( "can't initialize %s: %s\n", SI.clientlib, COM_GetLibraryError() ); diff --git a/engine/client/client.h b/engine/client/client.h index 5b6c695c..b093b30a 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1051,6 +1051,7 @@ void Con_Bottom( void ); void Con_Top( void ); void Con_PageDown( int lines ); void Con_PageUp( int lines ); +void Con_LoadHistory( void ); // // s_main.c diff --git a/engine/client/console.c b/engine/client/console.c index 13c36d8f..87268412 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1068,6 +1068,61 @@ int Con_DrawString( int x, int y, const char *string, rgba_t setColor ) return Con_DrawGenericString( x, y, string, setColor, false, -1 ); } +void Con_LoadHistory( void ) +{ + const char *aFile = FS_LoadFile( "console_history.txt", NULL, true ); + const char *pLine = aFile, *pFile = aFile; + int i; + + if( !aFile ) + return; + + while( true ) + { + if( !*pFile ) + break; + if( *pFile == '\n') + { + int len = pFile - pLine + 1; + if( len > 255 ) len = 255; + Con_ClearField( &con.historyLines[con.nextHistoryLine] ); + field_t *f = &con.historyLines[con.nextHistoryLine % CON_HISTORY]; + f->widthInChars = con.linewidth; + f->cursor = len - 1; + Q_strncpy( f->buffer, pLine, len); + con.nextHistoryLine++; + pLine = pFile + 1; + } + pFile++; + } + + for( i = con.nextHistoryLine; i < CON_HISTORY; i++ ) + { + Con_ClearField( &con.historyLines[i] ); + con.historyLines[i].widthInChars = con.linewidth; + } + + con.historyLine = con.nextHistoryLine; + +} + +void Con_SaveHistory( void ) +{ + int historyStart = con.nextHistoryLine - CON_HISTORY; + int i; + file_t *f; + + if( historyStart < 0 ) + historyStart = 0; + + f = FS_Open("console_history.txt", "w", true ); + + for( i = historyStart; i < con.nextHistoryLine; i++ ) + FS_Printf( f, "%s\n", con.historyLines[i % CON_HISTORY].buffer ); + + FS_Close(f); +} + /* ================ Con_Init @@ -1104,12 +1159,6 @@ void Con_Init( void ) Con_ClearField( &con.chat ); con.chat.widthInChars = con.linewidth; - for( i = 0; i < CON_HISTORY; i++ ) - { - Con_ClearField( &con.historyLines[i] ); - con.historyLines[i].widthInChars = con.linewidth; - } - Cmd_AddCommand( "toggleconsole", Con_ToggleConsole_f, "opens or closes the console" ); Cmd_AddCommand( "con_color", Con_SetColor_f, "set a custom console color" ); Cmd_AddCommand( "clear", Con_Clear_f, "clear console history" ); @@ -1138,6 +1187,7 @@ void Con_Shutdown( void ) con.buffer = NULL; con.lines = NULL; + Con_SaveHistory(); } /* From f1d90345467dec089530ddc9af9081a1ff1a3b2e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Oct 2018 03:28:40 +0300 Subject: [PATCH 039/205] GameUI: fix menu APIs loading order --- engine/client/cl_gameui.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index ef1444fb..7a6bc0c9 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -1101,6 +1101,20 @@ qboolean UI_LoadProgs( void ) gameui.use_text_api = false; + // make local copy of engfuncs to prevent overwrite it with user dll + memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs )); + + gameui.mempool = Mem_AllocPool( "Menu Pool" ); + + if( !GetMenuAPI( &gameui.dllFuncs, &gpEngfuncs, gameui.globals )) + { + COM_FreeLibrary( gameui.hInstance ); + Con_Reportf( "UI_LoadProgs: can't init menu API\n" ); + Mem_FreePool( &gameui.mempool ); + gameui.hInstance = NULL; + return false; + } + if( ( GiveTextApi = (UITEXTAPI)COM_GetProcAddress( gameui.hInstance, "GiveTextAPI" ) ) ) { MsgDev( D_NOTE, "UI_LoadProgs: extended Text API initialized\n" ); @@ -1116,20 +1130,6 @@ qboolean UI_LoadProgs( void ) MsgDev( D_NOTE, "UI_LoadProgs: AddTouchButtonToList call found\n" ); } - // make local copy of engfuncs to prevent overwrite it with user dll - memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs )); - - gameui.mempool = Mem_AllocPool( "Menu Pool" ); - - if( !GetMenuAPI( &gameui.dllFuncs, &gpEngfuncs, gameui.globals )) - { - COM_FreeLibrary( gameui.hInstance ); - Con_Reportf( "UI_LoadProgs: can't init menu API\n" ); - Mem_FreePool( &gameui.mempool ); - gameui.hInstance = NULL; - return false; - } - Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY ); // setup gameinfo From eafe5d6c68594bb9d88f244a2952e73cea857c46 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Oct 2018 03:30:38 +0300 Subject: [PATCH 040/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 9b088935..0f8b8d80 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 9b0889354f91b9703a75fcb1233b0afc5c212bc1 +Subproject commit 0f8b8d809a8406757cebb72077d608e512f7d9db From 48d1aa5115c4ba7343f24c1c88330d453d562c6c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Oct 2018 03:34:01 +0300 Subject: [PATCH 041/205] engine: fix warnings --- common/net_api.h | 3 ++- engine/cdll_exp.h | 8 ++++++++ engine/client/cl_main.c | 2 +- engine/client/console.c | 3 ++- engine/client/gl_sprite.c | 4 ++-- engine/common/filesystem.c | 2 +- engine/common/masterlist.c | 2 +- engine/common/zone.c | 2 +- engine/eiface.h | 6 ++++++ engine/menu_int.h | 2 ++ 10 files changed, 26 insertions(+), 8 deletions(-) diff --git a/common/net_api.h b/common/net_api.h index 00831394..956b16ef 100644 --- a/common/net_api.h +++ b/common/net_api.h @@ -28,6 +28,7 @@ // kill the request hook after receiving the first response #define FNETAPI_MULTIPLE_RESPONSE ( 1<<0 ) +struct net_response_s; typedef void (*net_api_response_func_t) ( struct net_response_s *response ); #define NET_SUCCESS ( 0 ) @@ -94,4 +95,4 @@ typedef struct net_api_s void (*SetValueForKey)( char *s, const char *key, const char *value, int maxsize ); } net_api_t; -#endif//NET_APIH \ No newline at end of file +#endif//NET_APIH diff --git a/engine/cdll_exp.h b/engine/cdll_exp.h index 9d72df37..240d9c25 100644 --- a/engine/cdll_exp.h +++ b/engine/cdll_exp.h @@ -15,6 +15,14 @@ GNU General Public License for more details. #ifndef CDLL_EXP_H #define CDLL_EXP_H +struct tempent_s; +struct usercmd_s; +struct physent_s; +struct playermove_s; +struct mstudioevent_s; +struct engine_studio_api_s; +struct r_studio_interface_s; + // NOTE: ordering is important! typedef struct cldll_func_s { diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index a0e56d1c..f692285b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1839,7 +1839,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { // packet was sucessfully delivered, adjust the fragment size and get challenge - Con_DPrintf( "CRC %p is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); + Con_DPrintf( "CRC %x is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); Netchan_OutOfBandPrint( NS_CLIENT, from, "getchallenge\n" ); Cvar_SetValue( "cl_dlmax", cls.max_fragment_size ); cls.connect_time = host.realtime; diff --git a/engine/client/console.c b/engine/client/console.c index 87268412..9f431506 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -122,6 +122,7 @@ typedef struct static console_t con; +void Con_ClearField( field_t *edit ); void Field_CharEvent( field_t *edit, int ch ); /* @@ -1388,7 +1389,7 @@ EDIT FIELDS Con_ClearField ================ */ -void Con_ClearField(field_t *edit) +void Con_ClearField( field_t *edit ) { memset(edit->buffer, 0, MAX_STRING); edit->cursor = 0; diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index fdf09cfe..4792ff8a 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -243,7 +243,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui // install palette switch( psprite->texFormat ) { - case SPR_INDEXALPHA: + case SPR_INDEXALPHA: pal = FS_LoadImage( "#gradient.pal", src, 768 ); break; case SPR_ALPHTEST: @@ -1092,4 +1092,4 @@ void R_DrawSpriteModel( cl_entity_t *e ) pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); pglEnable( GL_DEPTH_TEST ); } -} \ No newline at end of file +} diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index b9fbee00..5263c013 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -2500,7 +2500,7 @@ Filename are relative to the xash directory. Always appends a 0 byte. ============ */ -byte *FS_LoadDirectFile(const char *path, long *filesizeptr ) +byte *FS_LoadDirectFile( const char *path, long *filesizeptr ) { file_t *file; byte *buf = NULL; diff --git a/engine/common/masterlist.c b/engine/common/masterlist.c index dff68b04..f067824f 100644 --- a/engine/common/masterlist.c +++ b/engine/common/masterlist.c @@ -92,7 +92,7 @@ NET_AddMaster Add master to the list ======================== */ -static void NET_AddMaster( char *addr, qboolean save ) +static void NET_AddMaster( const char *addr, qboolean save ) { master_t *master, *last; diff --git a/engine/common/zone.c b/engine/common/zone.c index 1fa46dc6..015b3c9a 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -307,7 +307,7 @@ void Mem_PrintStats( void ) realsize += pool->realsize; } - Con_Printf( "^3%lu^7 memory pools, totalling: ^1%s\n", (dword)count, Q_memprint( size )); + Con_Printf( "^3%lu^7 memory pools, totalling: ^1%s\n", count, Q_memprint( size )); Con_Printf( "total allocated size: ^1%s\n", Q_memprint( realsize )); } diff --git a/engine/eiface.h b/engine/eiface.h index bd197786..c25093b6 100644 --- a/engine/eiface.h +++ b/engine/eiface.h @@ -393,6 +393,12 @@ typedef struct #undef ARRAYSIZE #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) +struct weapon_data_s; +struct playermove_s; +struct clientdata_s; +struct usercmd_s; +struct edict_s; + typedef struct { // Initialize/shutdown the game (one-time call after loading of game .dll ) diff --git a/engine/menu_int.h b/engine/menu_int.h index 2f7810d6..ab59bc48 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -47,6 +47,8 @@ typedef struct ui_globalvars_s char maptitle[64]; // title of active map } ui_globalvars_t; +struct ref_viewpass_s; + typedef struct ui_enginefuncs_s { // image handlers From 8ea5536b421796114ee8d0ba02ced1f9ea2e7c58 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 8 Oct 2018 22:27:54 +0300 Subject: [PATCH 042/205] wscript: fix win32 build --- engine/wscript | 13 ++++++++++++- game_launch/wscript | 3 ++- mainui | 2 +- vgui_support/wscript | 4 +++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/engine/wscript b/engine/wscript index 4be3ff93..a05bf2c7 100644 --- a/engine/wscript +++ b/engine/wscript @@ -8,6 +8,9 @@ import os top = '.' def options(opt): + opt.add_option( + '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, + help = 'SDL2 path to build(required for Windows)') opt.add_option( '--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, help = 'build engine with BSP2 map support(recommended for Quake, breaks compability!)') @@ -29,7 +32,15 @@ def configure(conf): msg='Checking for SDL2', uselib_store='SDL2') except conf.errors.ConfigurationError: - conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + if(conf.options.SDL2_PATH): + conf.start_msg('Configuring SDL2 by provided path') + conf.env.HAVE_SDL2 = 1 + conf.env.INCLUDES_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include'))] + conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'lib/x86'))] + conf.env.LIB_SDL2 = ['SDL2'] + conf.end_msg('ok') + else: + conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') if(conf.options.SUPPORT_BSP2_FORMAT): diff --git a/game_launch/wscript b/game_launch/wscript index 9983dea6..db762e4d 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -31,6 +31,7 @@ def configure(conf): conf.env.append_unique('DEFINES', 'XASH_SDL') else: conf.check(lib='USER32') + conf.check(lib='SHELL32') def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) @@ -53,7 +54,7 @@ def build(bld): else: # compile resource on Windows bld.load('winres') - libs += ['USER32'] + libs += ['USER32', 'SHELL32'] source += ['game.rc'] bld( diff --git a/mainui b/mainui index 0f8b8d80..c8f22ee6 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 0f8b8d809a8406757cebb72077d608e512f7d9db +Subproject commit c8f22ee638d6009db1998e72956ddae26fa39724 diff --git a/vgui_support/wscript b/vgui_support/wscript index 21f12af4..c88f4619 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -34,7 +34,7 @@ def configure(conf): conf.fatal('vgui is not supported on this CPU: ' + conf.env.DEST_CPU) if conf.env.DEST_OS == 'win32': - conf.env.LIB_VGUI = ['vgui.lib'] + conf.env.LIB_VGUI = ['vgui'] conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib/win32_vc6/'))] else: if conf.env.DEST_OS == 'linux': @@ -59,6 +59,8 @@ def build(bld): if bld.env.DEDICATED or bld.env.NO_VGUI: return + libs = [] + # basic build: dedicated only, no dependencies if bld.env.DEST_OS != 'win32': libs = [ 'DL', 'M' ] From 8710922a6177dadf4c3f8526a09d16a6012fea71 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 8 Oct 2018 22:28:03 +0300 Subject: [PATCH 043/205] waf: update --- waf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/waf b/waf index 9178805e..c8dade98 100755 --- a/waf +++ b/waf @@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.0.8" -REVISION="1f8dadaddc20912ba4dd275428a78e44" -GIT="80aba755c114c6b14ba334483c2baf30e579fe96" +VERSION="2.0.12" +REVISION="009555f9c2f7207a25556e6aa50119b7" +GIT="88e9382f1be4083121fe73d9172addafdf42f3c7" INSTALL='' -C1='#<' -C2='#;' -C3='#)' +C1='#-' +C2='#,' +C3='#$' cwd = os.getcwd() join = os.path.join @@ -160,10 +160,10 @@ wafdir = find_lib() sys.path.insert(0, wafdir) if __name__ == '__main__': - import waflib.extras.wurf.waf_entry_point + from waflib import Scripting Scripting.waf_entry_point(cwd, VERSION, wafdir) #==> -#BZh91AY&SY×C9³îÿÿÿü‡ðÿÿÿÿÿÿÿÿÿÿÿÿßð¢„+20T¸(bx\=Þo¸ðâ#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)ïv}:öšÊ5•ªÂ»¾ïGmk-жÐ6ÙÛg¯¹§´Ùššª}Å;Yh­×¯¼;ßg}Úݶ¹DaÙVTÚ[Ož>žùóÍî2‡å0Ø>÷w]ïs¾ž¸#)>€ #)h£@‡J“_`5]ž¼‹›vÌ#íöw‰©®ûÆñîÝžîw®Ûn8˜ëï½æi³,ñ÷C·»ì÷y§mj=ç¯xë[ë=¯{ã½K;–äW;…ÚÄ×]h|îãÖZ„nË3­·a®”Hom×\žÞæ|#ÝËOM}÷}^î­ÛcO–«Olƒ³Ö“mÎÝ´v­k·jo Ø#)®¯V;¹ãÞðzÙßp÷¶ç¼¨;bŠ#)TØjŠ•U(‘J…žµÙ™1U±Rµ;.š}·}öŸSèöôq;–mÙ¯K{¢#;¸åœ™UíáÞ²¨®÷µì¸: ëk#W«tð#)·[«#)*®‚÷«–çÝ®{äï·Ü|ûhï'[8š2nî9mm¯§öùqññÒæ9Šk§E>mGCm¯Y£©³En»@ïn==³‹ëÙ=Ú;w5­àVÁí½Þë½ÞóÞ7†\š÷y=æu;«¶^ÁUv÷¸QŽåZ ç}o<û!²u5³oq×¼V/p×¾Þt½³]»œ»>7®=3uÙëž¾ûž¾l“6À,U¶¨¡ôé½âínÝìêPæ{ÕvÜj[ÛGfµ$?Æ• 2¢ò#)0@ (:oŠH,‚Ä‹Ý)hR¦(™9áç\ªÍ)ÞñJ¬ˆÆ±Yz¶{¸0äáE”>*fp=+»k¹¢Næ0÷4êŸiUPïªL'ŒDÉQx0‘UQŒ³˜ `7ùW=kçûWt·¦)•&¨Õš©m)£%+º»Z©µI­£mljɶ¬Ím¦mªÉU[تÖõZ©jÛr¢´+¤–¨‘#)CCJ`%$VEB#)2ˆ¨YP‚#;ˆªòˆ4EK#) ŤÄ-e ÑlV6òêí±]×-»«kV­ï­k[QS*-2ÌɘbH-d„d ÌÔ@)M6P$5C"fT$6H R(¥¶M…RL±´P`‰ˆ%+ME¢‰$Ú3Id¤i¡’M¢!$F"DÔ¥€F¥#)i‹*mH¤Ù-Q%,´¥fÔ˜€@… -!i£#)°eS1ŒÅch«&ŠCI²ÂBF¦2P"ChÒËI1F”¶54ØKiZÙV¬3bM™&H”EK&¬ªË-52’²™S[µ-³#;L´™ L™#Dm#))2Í´ÂÆÅDQ Ó#FȘ´"A¨³HÖ4‰B&‹DÁ •2#4¨ˆ5)ˆ(Ø„!A"$¢@ËC$Œ’+H™¬±BÌБ#2˜H†dÙ¨ËFf$@ÐY%,ÍdØÆÅDÃR,É,¤$Y-4¤bJLŠ2ŒšLšRd˜5%D¢,D†$ÚI%"¨±$XŠ&dM¤%…1“b#bÙ!‘b$Å4Ó#A0±¤Ø &0Ø,!, &“bÒF$ÅQERE&Ä3FŠIDÄXL ¡Œ’’#Sš$d¬h´Í4[ R‰©„I1„ÚRÈ I‚Ø€’,³(¤Y’Q6#;"MB2’™ÒHÍ‘0LÕ4¢HF” ³!RA¦ÅE#,lÊ“T„¤SdÌBMHˆ›M(ÀÃF£H„’Ó%J2ƒ“4¤2E%LÔlJ´#<ˆ(’Ê#;&2#¿õuéÐåON•œ¨C¬W¿f#)GG[ 3›¹uKc›l¸û¹ã—DáL®UÓHh¢®mÓ×q£b¢}믔ñX´E,šUIïªLø¡ièaH Ä7iQ¹Ò'ù#;Ö¯æáô}]­u,<<3=í<ɘŠÍ³O®!¶Úm&×áó­{«Ò÷U\¤Ú3ºå8›]§<Ç„##;êQ•¨ÖØF†Ð6ÜMuÊï;±rŽ•Ë™/•K™î¿ QMs‡&…Õˆ"-´ÄTêÈ{qfçŸïKÁ²Q8°¤PŽÅ"(ˆ[BSãÆ¯FµMFZ±`¯²ŠDõÔÓô¸ÓSó=ØËÁJÿ‘AHl”‡=,µ]B¤õÚÿ ^©Zý_[¯‹öWÁ £c<ëçÀN9 c®kå‚ï–Øþã&`Ý,“ŒB0íU¾lˆ"žFšö[ë±É"ƒ"™½g¸ta_™Ùqûì³Äü2œAÉժͼMFp°ƒ²-0l#*S4òþÐæÉ­53ÌgÓ8p®ê÷²˜<* ¥>îŸ]úó/ƒØñ{85Ázs÷m¹¤g«–¾¯³º“YênÖ›¦tj#ן³æ×¦Ó0b’%58•ªÂWAÆ”k«^Ü€`Îåç{.ÍK€l•®kšŠÔyß^õêò!…x»)VM‡½Ü¨Ôk÷²«òÏ÷î/‡v/Ç×·ÀB˜,ñaJ3¥×Ò":ÕPµ)±´XÉWÍ÷W’)-d&Í#<¨¤Y4•N©ë½D8â»t Ý& |ÍN5”–À¦’6 -”ÊbÅ»79:!¯2ƒTF16­lчؒb ƒ4iøÝ?nVfQ²TSí:ÉBy!K»PZI]xUÇ#;A" µ¹].mñxµçW'.˜9[êx=dbFÛë^|=Ï9dQ¨4ج•”Šõì«YøÝ³ÅÑ7×ÇC#Ô+òæë 8“~ø¥­m_ökÛåååXªJLÂŒÈÁ{[´¨tºó¬#;Á©*¦Á§$Eÿ1œó¯~%ö{f{"]/• î›Ô<Ú4þ©'œìÃn’8ÅèÊ1£€ë‡Dé{_²L¿eÌ„¹t÷M˜çÒê|ÜpgüOÅÖä†ì™âq·z+:²ªñZ$ÓvFM4KÆÓðya² 'wÏ-Ç)ßö,!c÷ÌÞ¯ИJ_ÓRà™‘º)ŠXú¾QP竺Àâ·Wß8¤#„ãÂQA"â9-kyM DAJæE"‹{¾€ÏJÊU ëž+’ù§T-LêNùœIõøºGëåØ @É çA÷lÅļ‘ú`x/tLäê}yß}ÒjD46¢È¯FŒh¡úOHÑÄšŒou[¼µºâòdM—©ò¤xÞôÃ<釥 déK‰ë.JƒÊ'ÏãÎn¯jwíÁî_HVñdŒá— ­¹á­»{ËŒ®O½R4?ªc,]°Æðóëq½BªÔká%È[:xUæ«eCšP“.Y–¡tÅú.ûS"m’C÷´'vG^8ª\8è)s¤EWiŸ«ðó£Wz^û´;é’ö|‹+%Áê%#áÁ´ßl#<{ºAq‡guÑèÑçÒ¼9mŽã‘HjQoP…êä/8r” J8ÿZ‹Ûf66Í·mV6¬‚cȶ_§àû¡"U}c™JNm—¨óÚLG»‡?:!|]ÎH3U"&é>é8~Ÿ-l<öú‚°ƒ,M¨xxT–ayµ|"3n#;ƒhvayÐÞ+¯;³ên2¥/µ×2&¨‹¥#õ°àÛÅ&¾»•äÒ­U)TWûm#<Œ’gÎÿouaø¿oÜíbt¯ÎO€–"*/Ìüø£Í}«îaõ%¸ñß]Û~nºñ³êë•û¦ú'°÷^b¿wvÚŸ:«¶¸é½YŠœÑÚгF£µ—Eh£áE–f¤Gx1Çs$´”Íüà0¡JÛGó†Ð¬þ#¸þÎmE~D]~é7Ä':':¥ðÕç=2>rÓ¨„Ù¾ØTÎû¼‹ÖèßlëõžRÜÀ£…ŠŠþTÀÎÙ¸¾§#;ÅàUУ‹#IÛW~\êðÔÖ¡Ë®NÝëRÑᆭø^àƒ ˆvtº”;8ˆÏ^“Õ5UTœO—«î4zN¬­síV±œ¯Î#±mwÿ³µ­êNRÌyxU>yÄÓ #²œ‡ßÊ&_¤[ÏJzeÛ¤ÖÑŠmÔ$É„%Ö¬xm¥Øe[­åò4ˆ«Ä^¢üQ›q’SQû”ÿ–§·<žêõzç¬ïÁä;² v_ ×HÕqiŒ;&ð¶ð¯ÞO—é´ÚtŸÝ#)瓺E/{™–Ý\o-.`O™—¾ ‚¬Úæð¯‚¨!OÈ·w\|¯‡Óëá›Û$B7;# :þYRշªô³|Ê*‚Ž©ã¥a žÈF»µ_+ÑÑþ]¯†MHdò©A¶ÚϾª$yó•‡Hº½äŽ;Ò¢³†)žêx<\øUùç{7y26Ø?8Žãúš82•"#@¦(´V¾¬N|«:'?Ó­8T4`u"Zx°¥Ë)C©ŒêדDM"…¾<óçîê?^ycÝéÔËJ2‘J;þ¾8ÜåÆ%nþ/.º–j2ênt?¥qûzšý©Ðñí:$»p¼^A‰¥Z„ƒpUÏȳ+1*¹ì®Ò¼UMipñ#;  Å#;)ðm–W¯5ýpZêìè^ïH„Èø-1Âæ ïòz™Ã‚ ‘iK‡&±t€Ÿôp½;®wy×DÃí®eO}ã~#;Vyö‡ƒ_Ñ–ø;¿¯¼Þ£F+eq-'”³¶6:©P­)B(÷ä1ø'?­Jm;ÊmÄÛ/4ôíÇG3ô‘à¤r¶Ë„ô¸·tšS$Ïæá=pðÅ%úýcoaÙ׿IjM¥;ŽJ´nû'gæµÚÇÕ¶Âf”ZÒ MBиwo×§ÒÒöÏí‡4á²ÈŒ|ÜG#;’>¼ä8é:ˆm#<“8Ü 1#õ(ˆ»bŒzªR¿Ãû4×ýmGAa=V €"5¯)|üÌŠ¼‚QhxA¤¨vòPƒ©ŒÄºsØw1ˆ>ÓǦcNò¶ß– p‹³Z,ª£Ž.OˆêºUo³‡Cïãž·c{*–puNÔö~~UÐE2t—á€?)ç±×r"P±‘18pÎÓM‡«óaç½q‚écm\p¼H6äSRt+åU0D}nZ<ûÙàî©ôíb6þÎd”‚¢?DvMx[ó³å#ó“Kœˆ6Ü|ä1çÑZhèÆÏ;ø+/ÚéèEé`ÛÌ´¾Þ•xæô™yªÓv6ý¹ñ¾ëCçwņ'“Ëœ¯ÍÜ„ªIk ½sÚ4›¹s¶"×›‡ÄFžJÊkAê›ôTŠNNœÒyn°y¢>©Õ^`¸Þä¶A(f]^IÙq!·3]õç#;³%ÙÛʼ‚0{šMxbÉZâÌ ìÍ—¯ñ}88>F´v³h*1b,þN™·±*ª'­µÅKø´[ª‚Ë¢¾Ô Rm-rhÌ"áŠxv‡×?d?zñõàÊüsÍ-dIH‰B‰õ&ìÆ'&ÞzûÉñæ.EÛîCá1áãg³Š\};vv”…gLm5n¼uÙØjÓ€øøÌJÕ<.jˆ¨`‘…¤èƒu™AîjÙ]Rše³–ÔqwéƒùwÖ­‹[#;‘”T'#àî„Ô"P8! X¬CùÓ1R®¥Ý‹qVË©G³5=)LÅ^pNn¬Ë#<ûšê?'N¹³É‡j``Úé$LêÑFŠÛHœIá#LÇn³1¡fªk»ì§hšèíanýœþ#ÿv—Ê‹tÛ–¶ÈU‚±4HÊ¿•¶éŒƒ­lœ|Ú(éÊFš•BéEÝ"!åÛæÑ*uîD„ µÃúÄ%GŒX¿¹m=æQu‹3¿6h˜tA×ùìnÇ´‘\ [ùZU{#<ü¬(3ªA _q®’þžËdã¹J!»™XŒCU‘ðüfv±ÀI ¹w¡Ï¿ósÎ]¹Ì@_ÙÛi¸â.ºSäãöbèÌì÷k7gÆSçÓ\úw•Ó#낤#;³ë³·9¾,ñu'µ)€¤ø°5BÐÞ¡Ñߌ¡c9ÔÃ|*Ã'¯^½ ÁÉœDRÙøÿϘXânÊRD-/:™ªñèYŠ `6oݾï,è ãQ³¤#;"ëUî™eW‰Áo@}q¦X˜¬mN½éN»ðe6ÈÆ×+;:îÙŒñmìaÃ7?¶í‹*Ù›kR?“+Ä,ŸŽíߤêùf¹Š>=b*<µ[5.#<Nèí•ήZäK³ƒèC˜Åû÷þ!ÁmZ’<8Ú\^ØèîÐ}£2§ü(ô𻟰×>_(ÇŽ'ì~#;]?5é¬ÔþO¦Þº%â²–ˆYAÃøF-á8ºjx"pg#<35K3Û'2ØÃƬh¨ðïl1¹z4^×gr³Ö…×é±³t›â|u¾ÀDyU¤º#<Š+È™ "§Æûß•håÇ\/Uæ¹µñß:ŒbªºžWÔƒÅȯŒÐÌÛq_9ï¿e˜°ñIÚÔ……Ì`@P(/h OðênÿOäOÑ_Xßà¹êÙ'ú¼Þé§ý Øÿ©ÈáŽI9Éþð%껓¡†ÞâŽL¶C ýÝoGÁ?WctM>~#–© H=…ùs<0yAn#;m(ÿ¥»GüÖêp²U®#<*èªÒÕ¤ ñÐé8s^èËgL,˜Pˆ9 dŠ@ˆÀúèÿîQÝðÎÊœã¢A÷ fJâAÌàXd¡8s¥Çàéïä×e5äJ½Ö„2u³Ûr#;mQ€c@¤ÛDÉà:±Óê˜òA &ñî¬à° ²ú@ßÇGMc*9i}È%|áÑ9›4ªÉKÏLWýl#<À¥yQÆwÚ_ú ž“¯71ÕÛhå}Di`çÆex¾Ý*Ïí`—zó}¸#<Âv-ÓJ—jñß5åQŸ¶¹Gm_Tbv¡ø9èt{Wl®B `æã~E®\¶€uŸt2‚>Œ…EÚ¨^£œ0»L]V³SŸ­_H2=1zm×DôÁí#)±š+EÕFñ.™T¼EÿW§å”ºž\äÌ}1›òòôÏ ½5IC«Ã¤pÌQ⦠ۗñž@.¨1P£×;!ªÖ¹.ÅXïVÀˆOU€â(q ‚djdû.Ièåï¿Öu|GÏËÇ©Eèç²;¢o§ï¨ù–ûx:Y`¸ˆXª kaô”ß j(ï5q>­‹s•üNŸE›9.`ýPÛËv_—†~"&l È•Øî(ûì¼,¼²?çÔ©Ã_=xýïñN|¼Š÷‚×ÂC ب -ÉÍaò²†H€¦iŠÝ×õ#€™C t27AýÓ|ªúˆsžn\G¢rãÓ]DÓÐî_°#;ýzÆM¹)Çáýû„ˆH’àfÏ÷¾£sΆ*p±Æ+ÃÅ]uGóÚQ’'a_˜;Šî%yfú!ä<¿;#)”Ä8ëÈQ|ÜŠ6뺉&âÒÒ<^Í~OW·eƒ£Ñ¬#;Åá@D¿*€¢;¹U|Ñ\"=Å:¾Å©'^Åcx´Èv>g²Wr3¸»\@$A !Ú# BªÐÈÄ«P*¯ýËXì¿ö‰¶øEÐJ¡(H1UŠ$€–Òÿñôéêÿjœ´·'gÞìã¢à÷S»5ãŽÙ¹´³2ª³3)*¥u%¨@}ê‘ñ>U¹'Õ’nWŸäI¶dˆC3û»tÁ̾ÿ‹çïŠtã«”ª”ž›»#ŠIELŠ1B3ÈúO¦Ë"@ .~Û#;šÓ¡O¿©¼éGô5¸þÞ³®Ff‰BÒ>=Xø]?OkñøbÜöí4ä(-·$,™$€ôF$o·¦?§¤ÚOxÿ"ÞŸ5òãyµjÝÝÿƒ¨r5&ˆ&À‰„CTtå¼£ø_1™æpy|nL}H¡·2D%ÇlUjô1†¤MªñŒŠ%O?Ïþ;æ!0õ’0_ý:O<˜Ÿ”Ûù„Õ…ÀB(åvpZy€–˜‰áTlÈŽü’ ^È$tOøbçŒÍ°s›äÚ>oâþWüë÷÷ÜoµîZ ”;u‰pAv/¡œ4Óu?EÔGÚ»ó¥ßÄþÝ#) ÔÊNƶkè|°ê\åWž9óáû¶à{v…ŽºÛt ±ª¦®²6áp ä.nçÛãs>õG–‘`$¡6ú_»@6Çò hƒužåQHé‰mÒµ²ݶã¢É×bà1g¸#ƒóTøÜ HÏàuŽS»ƒ¼üP #<9Wm>c«yð3¼#;ë½çà?Só4Ý4 f·‡PʘfwÞ¥æzsï˜]ƒ3ChZqø†“Ögë¬w•*mBò®›áàaÒ`Àž|òÌHðòêÉôß¡²r´Ñ×Ù›Ñ䔵ôwã¤it€,Q§£‰v?ÕünZC›”äÍ=ôÙÂOßåÇè­¨ :z®X»U ˆˆâNw>äØÄÇ„ÒÆóø=“~)œ4m™#)Ã驳;‡Ûäóŧ×è’ÁÊl6ÞÐU1ŸÆzbÂÆ†}dùü¼ñ„‡Ç󾦯ÅUˆ0ì”®øÜqââi_­Oò-`vHõ‰M™ÇHÞØ ¤­Î¯Â,§Ã´úbOti6,##;=ú~7ZóËk[ÁÊ'7#ê]1M?dZ­Èþî‡cMâÉ#;Úd$ŒÄ!MeÁ7ÔüöS-Ô¥$v^Þ¼?+ àl£¼ï´Œ†Ûƒ:F㉄ë ÄÜÃE¡$9M€NO"Á8" °¼€€#)á=’䕹ºE×{lz›y0 aJ~Ѐ £’;‹F„Ó15vR_[ýs{zsN0ʯª‹ Ï×sñzàfrfŒsôóñ¬bMúM7Á0oè²Õ[Û;/#<ª`(©rûã zå>ýRØþ¡§¿K­kájpu¤^Ï®Ëk-xAÒŠQéÚÇ!ÄJ$§ƒL‡aºqáH¶¦ ©mV’KDáUKtÒ”“ÑÊh}¸q±ùíøè¬îç¿$O®Þ¼V c‘0ÑbÁ\☬څ C(Uغ‚c}%-Zˆ*5g^d9WûÇ”nÞ£î¶^ætyõ–¬!*^…Òr)L]p©AÞQäš™ÑQAB4–{³‡‰“Nwkd´BÊ#0+C›9°Rr~¦V\¯…¨#dߢV”³}‰4G$ŠiMeA%±¼ ‘MëÚy~N¦ØYwTã‡éë=õØÄ¶ðFÊd#ËØÅ_(äõ&w¡¡6Ï. uÈe#;PnQ o!(Ä95špºI:µ’(¯ŽÓ0ä¡#¡±-¢çg¨JaÍÖ¡(yÖw†.¼?S˜RæSaHÍÊ Üs£ÝÌ|0–9¯{žŒnѤ$qJÇWaÑæÓì#§¡[y´qŽ8,á!’Þ#<ðÙûÎÃuòßÓsÍza¶íwL^¼Ž·UžqVY6ú²‹'Æ^ðë·gÔÑäYêgÄ32¥S>#4ˆ«»¾#¢D±#;:“HcÉc |!Y¦#;ÒϱOSµDëÙõ¬/óF|¶Oa¾~O±Vµio8Ç'g%È yÎ×Q€ùàôÞ‚.0ä()70 P_z>¤jŸs²»«Ù¯yÝ$aõÝ®ï<˵ť㷮wf1…©B(S%$‚áv¿GM´Øô´#.ýöi›û·Š×+ëR½o 7 }Ë„¤I¡64Ñ#8“Uyñ†YÊf›+жÃR"HÚ3¶vÅÁ·Ê£#½%¤ŸÎIÑc±Þ•íw~rìÕ«ë©V©vOjÃ#;ô^§ppˆw¸CŒUÆØ;í›dØ·µŽH©´¼b¨Ä?3…Þ¡Í-øsÍ (T)‘h7ÎxÉ«ª#Þµ±Kw`P)y÷À;MCÊ^éÍjû¬†Š 8Iú²â€±°œü« ÎGèÍšáˆ^d¿ŒÝ±˜"·þôZ°´œ`{U3``{:N¬ÍB\åjØíf)h¨n”Å6LÂVñB¼'œìT&ɾJÏ&€0Ôe¾ú-¤Òj#;[¶ŠÀ¬Q a‡ÎÑ¡+D@ñ˜¢´º°1âÆ#;!3e#;¦ê\Hp¨v @ĉ˜7®ùÛ‹À¬J?‰çEGèÖùôߨßXRACd’˜,P“Åtµé‹WÍ^{öÛ˜"Ö6RŠŒj4Ñ–½’îÕî¡"ÁdPÊBÚIªp’¦4ÀBfµn±´”ËE#< c „$#)Áݳ=_ÍÓ탌Îá$oùù|2q0Ëc2Àܵj qqÖÜ¥Ù‹#;'6©Úz%#;Tuà|V&ÿg^]Mv:BT——qƒŸì×Ð6í¾·–;óѽ“:Y‡{`0#<íÁ»~èÇÓàzë¼Ü2ìÛãOÝLñ­GVôáºÐ9ÿ7»|E*á“¥¼{#[†B䚙ݏµl³“ÀÑË,ué¥RÌà5|òìîþ.K3¨oò<ص³ï±xûó+³Ä©ÀW® âh{Úµ¸åìõyžÆoxò'vDš8AÙcÚŒÇ-Œ’8àóXnÊ0ùl<®xxþu.[Zœ9 úÂYjëT†‹[ZàÆ [g®.ü^wç‹on9øl ¾/PNs½è±ÊÓS¼Iœ™f×cUÔ0 œÂ£È$Dx<½Î|@žásQçFÇÍõ|ç;æ %¥¥añâo…ŒclJòC¡.K°.ιA5ª„ÔÝOLi•Ä×ZvàëlȦD/³*Ò¸Û[5¿ß’^¥ã¸8JNšá=‹–tž‹‘ÿ‡ƒÀJnÏÊmµï—]Ó0`#;_¿sYž³)$”Mtt¶‹úC5ZJŸ¤XBv1NcGç³Ö ~—foâYe¥QX*PdaÆÃ/#Ã;àœu#WTîcÆOoi„ÿ 6¶¦äpýÊ ‚hñwvôŸåx§é¶AŽ”íHFGs´Væd6ÓmÌ[.PÄ}$…¥;¥ˆ#ˆäpù4>™¨ ^Q@ÔüÅåð/:'$\ÿ/–KwZ(“chCiŒ¢iF€{³$Ë"c(Óte)­ÌŽhdLUŠ-¦#µ’Pȳ IÕÖ‹hN¬ƒikàªDŽQGÆ66¢f!†·ìº5"`†ÒŠ0å–º`S† ,¤&©HÜ ;0Û2QªQ:RÒ±dt(ÂhÜ’3#tT˜j jÃdÆ#<”è0¡@ÊRJ¥De‹l†‰+ ˆbÓJLื¢>žO{­ü5m˜hMìnOñ(¶Äsƒã4É!²ª‹A­’a˜3¼?M²¾:s¨ü^IAVQ¤`íBŒ`l.èÝìÏÞÈ¿#$®®Û>g*>T÷bP`‚ ùã〷¶ÃÿlÇÁn' ‡:JÊñ௶-&#)jÙÙ7û¸}fäJKŒB"È*Æù=ÖÄ]œ>Zí™zsë°Ü‡q¯…àÕð±×#;ŠòNMºV¢YsÒëKÚa©.&ÈZ– φ¦6ÑdÒ³8¥„EˆÉFnZ×M¤‹\Û¦Ô[s[tÕš[»^Îïi¯;Öé!%¥–”ñÖQ`"7Ò5e"ê¸0l#;²PÉkòÍIÛ9ß¶£ hŽžˆZèî R@áE¼ÃÞ2)B%ÛÒ¡â#<-²åQßõâÙéTŠÎ_m>]+wì8ÎuW>³ë> ~n z óŽ ýõ‹8­ß-áï/YÛÖ\ôÒ¼ÿG2•õÆÖG/² Æïsååêà}pË0çRìÆJ5ÙwÄeQjd]#<‡Sà¿ôn€êÝŠ¤‘̪'Ÿà?oÙ¿»k£¶©W¯ÕêQª<Ì SŠJ‚%5ÔòdÒ«‚c"¤)wDQ~ÕIòŸî§aÑ ç!­SœÜîæFæôKçQðöÃz¢Ï³èܘâ2ßg²á¥p;<#;;S{Na,1¨‹#ʸ5 _)Ÿ%8ùŸßâx]+ñ»I@º¼jâ.Ï:ŒRY—ËâýRéîîä #{r#)*‹sý¾ yþÇÏ ·o5¾ûCD#)çs_…ý\÷×ó[ÇÔ5usGëÑð˶¹]’sœö~K€åöG|7›ü<Ÿ‹ß•°ñà.“jí†(¼r·$‡!ñò&ç#)óåvBÑà X[)=ØŸœ3Ï™À6/#<â˜qåóxbn û6GXÆ’zå*ãx\ Q’]'M>äý·å0ùG—ÍðâllwZ)N=”z¬õÝÊli463n bm;–240e¤) 1B¤vú& âFAö‡GÑóç§nGŸÂ*2ÊJÂûXè(_Ê¢]¿à¨e+Æ­¡M4O¢e‡+úã•(ÿJŸ…ª®? ˜õ{Ë Ðë²êÁµoúô~ìÿ†~çý1´Ñþø¨þdŽFêÿ~9€q¦Žø#)ÉÏO©–œüÿÕà—„#) ΊQ@d¥ý:¼÷_ 1Ì;Xõ£àÇíOg/_˜DƒëÚƒÊKÈJ+¿Šß¼öÕëz툰¨QlFz­S6¤²h“²¥‰Eùœf&6*þ}z;nÔQ¬·s®Ý-r¼;+éo¹IJÓè¤OTJ€lûICÓ÷œ2¶²…°"à#;Þ´ÐPG/µ¿€#<$Ý]âë_Íüã`ÁÓgœ6›dR•뼨 ;%iÐú˜X@ãÒû~}~\öî®jæ‚.UÝvºˆ$¤”©÷UÝ Õšóýuúy~ý¹ôJÁE”Kª*§'øêR¤‚!ötøÛÔùñ¤ü+)¥mT0³–ƒ) ¢1BcJH°Xw½Àß奟ƓD¡¢0Gy\¾¥kðÿ'«ÙFÐVõ*âoAd&é#)±$‰’¨¡=ˆÃ÷fßQõh~^Q챪´ÉiÆ"Ù†Q(‰¢ª7R¨”¿}äþ×Í&{å©ç®À ®+ýEsp[زhoærYKCø2½9ŠGøáOãjÅ‹çƒà²Òì˜Âä…µ–K½ž­[?ä·3†å`ïPâ;à"«cã< +žŽneÄâQ=BõP¡ H¡Õд+v–œü종,`6SÒ÷St’#;uXƒmÆ6–}¢•ì‡èK\µ+«ËOª¹¼ž(ø ~©P¡‚Œ0?®äÓzÃ"Š„MD°íâ3-~S2œ¿× ­ø¼'ã”k×ðbÈŠÅt£{'ÞÂˆÉø´‘Cƒ»­„…CõTšL0U"$ü©L’ÐÛ§û͇…–¡ÁIË¹Ë ÃÑë`?a~¿Óäø¾3ñ]úб½â¯#)|¾O“åô󯋑8ó¯ óE$EÀ¼ÜSѦƒLg?Þ?LÇü·wÑ4³Ç˜ô¿ð]>{ãø‹è!Ó/Ñ?!þ@=Âô‘f>6â½usqHü•wÕ’¼?ûTtC“Îz@Óm[¦ñ³·ôÅÀL€ˆ9&HÈ:Ür³õ^/ul…+úù®uj: œl+Ÿ›\Óåü £öG>΋©r5ÝñüB–k6iíû|õãrî;\µÅßӻǸ¥êvêÊç~:Çg÷5ÀÆ·r¼gg,NÞA­ååîw†ifxk¼ñ=’uʨšbŸ«mÉÝ@T_K`Ç}ø}9dfñE¥/ìÄ«Kš5¿±ÀZ@žÔ`®rÅÁƒÏAÂôC9ô|›qàŸšîWâŠ{[îÑ–‡os×ÂøûµÑ  Âó¾õßéP\£¦Mé‚á–.òØ!UÞÝä#Àε,p_Í¡€²PNV@Ä1+Ú¬NåתA¢å‘ª²?>[²èþ^§£ìíU†S‹š"OˆFé:Ž•¥O:«ÇŠîìAŸe»o«ê=žn³§vŸ5öñ‡ù#)÷Q½’™÷³úìâöaq!ðöýœ||PM«t¸XÂ’Ø$¦r)È¿®ªb£”¢.P’1Jß¿9¾ÛCÍC!ij;f-Éûê„D’Eˆù‹©¢ª"׳!c¯o^\ûzœ÷|>£e<ÿGÎãè6pç낪_7Âtú<<ÖhØîøþ‹®·““ËöÙév™÷ÚÁ^¯ß¬kóZñ¬¥Öh~w.[lð;Òȵ=‹F‡vóú7í÷JéCEÚVŸgÙ#;tß·Ý¢ÞHÞ#'•燖Ì M\&µ{}Ðü9zÚÂáã>oìçãŸø;~,©mNç'-ú¼ú܇{¸óÕ>t‡„‹tØý¿ŸŠ_ÍËìÝèÏG˜Ø4hÀC-Ðmù[£â}˜³‚èÒó´âÝ}ë¬ÙÕck³›Ç³UEøÙ÷×îÂÏ4†É9;úv%Bzÿ¢_AøÈ@2Í 5Ÿ#;«ùß„W“_úyòæ²tâ;¤ëñ°YgÑ@8ò÷ï°qýÒÊ]`¦Ê£¿K‘‡“¶Páv e<¼"Ûqë{Zû1¹“ª“ÏÎù±Œ,®¡ù 1ÕÉGtzôýf×pãÅ90æþ‰ÛÃM[WÉÏ®g¦µäÛ»T_sæ5ÅÜ̯í†"Ë‚Ú\BrQ¨.옶ÓŒn¹­ê–±Û8º$\ÖòþŒ>Ĭ³\­³ª&·¨y^£C_vVVb^=L>XØ8OðÿI†ëÏ>f¼þeâ`|LGqût²¦gw˜×ølÊ<¸¬Ë-2wþ>ì·Ã¿þçvân–;Ýå"^¿G»U.¿²”߯ØîÙ¡¡áPØÅ“uü\êÒø©iÏÃd¹¹õ5JHï@oO•ŠPþ~5BÃOù]a·w§âõë|G!ð™Bkæ§DxO³íî#;‰Î:ÕßsÇ—òÚ=:»üýKÝ÷û¼þqìv†r§uôœGq‡!à)´gdFÛãn¨nÌ£4,W~{lñ{u-o³£Kûµ jØ~Y´úüÎ"SÕ¥ÎËÑ–×zHü‘ØG×µûÃK#;™|ã#OÊmPZ lz1¦^O£FÚ@`¾—qçŒûc¶’§½ƒ¤KE~-)ß¾Ü(£›ö¨‘JÑ4à²ùçñ¿G >?ë]n.ØqúûSÍü~(KUŸ§f’í9_ú"`W•ße}wõCW¸ %ÿYŽ#Úù#;8@2ôr/ƒ›öÙ”(<.´~H¯Dt¶ÀÁ°ñ|µú]€×¡—Ò¶‹V÷|Oä³àîW=Ù/¾*#ª®–*èw]îon•pO þFþ¯«þO{Cæ¶ß¸êd”FœŠ˜ò°">>À;ž/æñJ·SÛìþ5³é°sògz|§÷ò|Çí?¥Ú;s×·òÛ‡4“0àãgd4z½YÍAq*ªA=úáùÿ#)Н›ïæÌgÝ/dÒêñßù¯Êøyÿ­ú=«Ô¬¢Xø7†¢()¨ÕÑèþ¾F ôœxõ=—çé#<‘ImãFÌjäˆSqpSïtG9W‰¿Þ$P|öò°¼#)Ä2H‡|Uök§£Ð¶Ïˆp>•u~ë~n.µÕê úüc®¾#)×òiúô[ÕN“òü&éwg¼Oïfm~.ÁÛv>‰ê~£»ó࿃¤ú5jкµý¯åóšºô"qþeîAÜPhçé±Ãöñi/¾}3èý:î~N¿pøåC¤y1åñwÎ.yކÙõLø.wð )ú4îª#) þåú+úóJ?¸yÉbhæf¿ð}ÿ«#Ž]ü€|ê[}l¾´¹;‡‡ÉÆ9y Ê­£æ+ã{Ø#<~Ù#;›]#;ÜM@?#)„*ƒoÕLž»ò ½h” N¿ÃÇ3Æ‹Ÿ^ϳµ:{Wˆ_·Ÿ£÷HòïÆX¯ÜÜnÄ-¼‹£•ß®|+2d¿AðÎÚxæŠ>€Gwïý¸Y7L)ðªŒtáᘾï§`7_N.žDŽ§ïˆ»[‚LlßQOV‰aP€ŒÎò r0I#)Ïšû^ÃÛmÚ¹è±!š\M#<ë;R[Ç=Ã@çô®èO‘8>'C6…ü™™‘Yí®}ñSÕ5/>\Z§[tË¢ã÷^`ý)œørñ:î 50qܤHt³¯T-Ý,„^´Q>›~Þ*$aD–|jâ+rîÀ‹‡¢fp$–:–¥ïQÆ9Æá=@DCéM¿ vøç§äôà4i¾ÿœaëìÕ :‰ ð;ƒÁn+-ؾðÐB)Ö ¼.˜jð•+F¨ªIî œÛˆ«ž«Í^ûEH§3çIJYضH‡­H•Z TJQWtUæjƒýÍÁ»¹k¢Ù5¬oŒKÌPŠÔâÅøÆqƒHçL°hÞ·`怀êiŠò0TÔQ$5‡6…5Ke¼+ê¥t¸sKE™vÖ¥£MÄŸ·Ðñ¦xØ7&š¿D,°AJ#ÙmÈ.­H5C= 1¿x¶ã„ì#<3šãIpzE\4yp}|}¢g«ò¿ÅWcƒîö¦È$Rw‘BGf÷¸D‹õ…MŠ|±ÖŠ<ÇÕ.Bé/qg^ß<›a«K=êúx‘Òä(†¾AíýœPYêøøYÔûÓo Ú×ì¸XréOV—ŽèoÔ¶Ev·m—ÔÚÙW‰nHÀ*~IFnœœyÈçz‚#;Qæ 7üSìv3fŠ:õëÌ6uti³Àe…uÓüùËô~f•#—k¼»êš¸j qžÉ™û‹Ë. Øï=O‹«Ûòñ‡hTû¼mq&9 {†£ú<ûc!äw“÷n]ÁÆ+ú¹=-|>%øòM£O;ðåLJ~>jDõE1I&×MÒ'³=øt?²K‰ìŒo}·>úŒ1¼$ø¹~ÇÁþ|p“»+ìôÐgèî@£Œ ÐošôE‡y˜’Uºóí?ŸÑA`þ?®Ç!Jóh‘w ñ¨ƒÙ%<£ãù<ãeëÇ Àátv^Ó 㬠U»´g?ðüh>_•FàìŸ*?ò7¨ã‡ú…¨ÌË™U˜žYpÌ!aéL’ UƒpahÉ~;ËjA†ÕAŠ‘Äàš”T¢MÛ¢ÆâÂAUZPq¸TEŽ7wC³Ww)«2ÌÕGF„B!‚¤´ŽÅ.ey#;`‚óŒ#Q†™-f Õ‘447B‘K#;X­‚++¦© nŠô`ÍšVôà˜V#<ñ ‹]?KxºjšÈÎHHÛÚ-UßeVXÑŠ4§ùУåù¯œ×'(9¤œn?¿Åïû¤Óù'³ÑÜ›…Û9vOä–|Î8~dM˜ßøY÷l²bù,€˜_Â/´.)ì< v{e?½hÝ’~í¯ùVü‚Ïdþè>y[xéýù5“û¸¢òXqíçlbhÍ/3¹#·âEËlV„z;:¡§-âê3¹-m£–é×unÿ¤‘Ûp˜ŸëéÙ§’ä|qj¯TŒ»3åDÜÝ=2·áøT(ð”Ëd?ƒYUÅßòÖF+,¥¨§’"Ä5þÓ° ý¼ºo6Ñ/ŽÄªB‚|dbâÅ‘¬»ÍêŸãñgï·;}Ý#§ìõx]@«Žc¿‡Õõ&Ÿ†MÓ[X€ #‹NÖTõí›RfZ¥#<æ9”Yo±+!_Y ˜Ý0Æ44–‡ ¡ ÄÊÝ\ZÑòÔº7‚ØÍš©.pÃ,a ê}tÓOY]‘±Ýö¢Æ¡­A‘¥^HÁ†œÉ™†ZF L¢µWFhuë¦RòŽnvàDbk¬™ñM˜‰•.œ "8oôyÕ˜°h8Z°²Ò&â¬j”5ö#;˜fˆÖ¬D"}‹S"%›·86AUƒ!'fv|ƒ0oQʃª:U¡óª UŠ£8rÌ­’1ÂÈ™¶W Ôˆ’#<&ÆaZ9X[;:b˜(Ò #‡-‹Œ`½)Å Li´m»eƒ`Èv¦êÀ\†Ql˶–`Ôqå9þ?ðÝ›6Øúá˜ïmç·]†½;Ô¸]wBý^ºo¡%ÓJ(¼Z'6MØ$ë ôZ±øÝ 9á‹”N#ºô}Çï×\^ô*x~#<Dq1†)DKÆbý–}ÆSsc?a¢Pp™y×ôýwT}àÃëá^3Ž»<ç"_¢Þ!ry×x‘»¿Ô ôübzr×Ëñýž¿‚üÿþcÏ#g_Õ™¸‘»’<•¢²K Ž£,-*ªn•T@Á§w1‚ûõÆøðËÈ÷´@âGH¹¸ÂéÃJ1¤J>¹ðÐ`ªÉ.HŽ)FD‰©„8ÌùÙÚ,ob'h¨7™¡«ƒgêé#;hl“×w™ÞÆ-¨Bð눣ÒÔQÆ»ÜÜAl‘ê]/F°4¤´RéhƒDâdP)##;ÈÆÆë´ÄîsjéUZ*„B§CžþÈ`óOäü›ü8ݶ­q!å. h¼¼9Ë‹as>. €Äü›2þaûžWeû½]ý³—twŸ]áêÒ.ËghÉ>v ùô^Ùks@½¼]´ÂÏù‡!l‹Ä‘{m¯÷–gP<&>£Êà¦óÉ3Ï•‰"§äŽWîþŸ4¼FÈæÆ]¡û¹âï>ôÎ"ÎloØA'm®ûV#;TåžÇÉ\'JvÚ1‰Úî¶4Ûxâðöž—FÈ”Ùf9" f=ÎÛèW0šµUKEêÐ[¬4Š–OÍ‘êEõ;ƒ]®fš®½s÷3m±Œ~1A¾CyáÎú3RÈ÷–I*éòÃ3LcݪÑ&3Œ ›†ð†DÛ{1×$îtóZð€ÛqÓFñ3%ö^M»Ñ{ç´íÛ°v$¤F¯%ÍYÉѥȈ҆‘΋¤Ø†ÒÅ>jÀ°wÍUí«3{rÖÛй´ÇçûÆå˜ùŽk!„<Oå1mÈiø@Æù”F_ƒmŒÓ™¶¡0ÚÀã¤Û;ˆÂÛ]#;Àí0!2LRqYB'!Çbipî@Τ2ìl)ð_óTÑ©ðÀGmç.d–'O±DuF+|ÙÛq³ [‘ï#)‚<õFÜåç˜ÉŒÐÃ…bCj=S®gÜŽ6&D(l—ÐÔ(á¸4Ã7„›%ÞÜ¡{ußkÉ»F:Ƶ¾±„²| œI6{ðj]CKcnMµuÐÆÅV+U[ÞLª‚ÔÖêJ]([8n<.ÁNê7$í¦Žƒˆ84a÷äÃ>dg8rYÔ/€»K"š;cwrîsÆÓ]ÔéÓÆ:(ÒK­ q¡¸"Gw‚‹êÏÕp]óÎó¡.ç;ïgQ×gtAZ8˜–JÝ£ƒ‚Ž—–à× ¸Ì¢—\\hÐ^B¾fÃvcŎŒ{®‘Z±Ú5˜Á›nµtÜeó<œõ\¼ÆQ†&kÀX4#ÙJT¹Õí’µÖì6ÖÑdá"°Ë0Ylƒ<œšª6 ÍõrLѶBQHKz’´W[»`XVC69smŒœ’ ä›YÕÀ" 6žfÇ eö{ŽV6%iŽý¬i=tÑL2m!ôž‘Bß³Ò9…­Äìå­}þÞ¾÷gkÖÅ‹žãÕÒÒ—ˆxP®\Ã%bü®»òz}¸Ù·œ°lWr'Ì]F1„#*£ L™`3¿ž™2h›¦“§J¥tã·fpo1ŠN1ÁJn×¹²O?ñîe¥Ý=ÄÙMa^îxGkí]–Û”?XiibÛœ*¶/©ÏeÚuÙ6–Ó°Bæ2(¨6‡±o—·¦s¦k!Ž{km–#<â7+ý(Öe—âbiR4ñÆðWVÔãýdŠ,¤k†L’¾6Þ–|‹-’yœ¦Ò‚‚R1X½]­ºT-;·Š¨˜j&aXæQ”Ñcío•d*¸3o,1—™Z¬a*Òº–³oŸLº\B· rÞü²qÒ¡øq—sÖêóIȈ¡fŠ*#)_:µ*_—ƽƒÅõ~ÄpÐã€Ws‘é,Ëž\=/ÆÁßXw£Ñ_¼×Þ‚UIÒýâçí‹ôí#Z‚j‘ƒù—éþò.å#;Ä4}ŸØ9Ä&¬aºH¯ÁÝ¡žß¯'ÞÃs{þžöpÛÏò™ú9çò–ö/~2ƒ‰öµTPzÄ=ÎhÀ<ý>ï i\£Ã‹Ž¯+è®ޱ>SäÚì×€gÁ–ï4΃NÿCûÌkIW/EI²Þrg¨—,õþ3~^íÒ7‚yÐq’ù†hÈ]t1†"ÛFY·Ïïù>FŒ»pï_ ž8Ó¸+P2!FXbí‘%‘#)§‡@¼YŽ=¹AAýúpÙŽWªaŒi!ÿ“‘ãhxò”ô'›ìr8÷#) ÷ßëÉŸßÚ ®ÿ1,!š¹Súõ{6§£:rÔ)ˆ¨+Å¥[Ç$=¦ Š£“¶-Ì è¸uY”‡ó‹\2±;ŠXÒz º#;;‚ø!”Á@^:Xaæ#;jlB™¿Htm'AZÊFp]X;Í#)çg?´YѬ¡×ûäˆù¬6'£SƒáPU¢H83²¸ã5°"Ñ ¦î|ý=9¡þŸ—àrFS°«œ»«1Êp‘Þ`w±ã., MKÀ£¯Œ&b©ò˜Ý}2‡á¼sÀÙ¤«vK.Ë—!B#<6˜%Xq-¸Áµ²bØy‘ÚŠÔ°QËíiÇœ_t¶æKd{2Ã{×Ò;W”牄û2ôÇÖ>F%䈧øC­+7&"]áÒÛ8 ®1yFÂ](—YÄÝ7ÖÆbìBHº“û6k¢¬zæ¥Ý¦üd¤!Rë~”n Ÿ"ú­zÃx˜b}™“éº÷dÄËᎂ¯J¨æ½O|–J#pðëEQëz©êêÛÙ#<]Öae¼ÑÓM9Iø;½Ýs­/1i>J$P´Šd£PZ¦ßžaw>zd$+¥jPnô.­éºwn¬çßÍðw2CY§:wCê7Ùð¦O‡¬/}i"鉀„"n¸€ñ¼&Kµ·ž@§eÏ(¢áŽSpVH÷-÷Nòm-êû!;Ñ]'eê‚ÍÊ;3‹ D»°èI€˜v!•¤ÊôIõÇ•uæu»ö³†Ÿ‚°¡ºÙñ%ðoîG Pø¿à.'¼vœx¼ßI"fG‘CMM<» Ô¤k>·s|´dõMmdŸéÝÕtÐJhVϘ‚Å%2éD';1ûóK×£6±§_ˆš6piXñÞ Ù5ù»³¯Á}½s#;³Ð¢“Õ°CÈmݯ©M2qrsñ¬Xºøè¢?ÏÛâñlAŽ&]ÎcÂ!"Ï#ò‰ÁÌð õ©øÏ!ÀžæO×Ù¾Å3I`d”•ê0Bއ$d\P9‹78PÕNÕ„ã¦Ù”˜1š&‚ØG®°çÛÃ5!i‡E¡aä?vªì<Î#Žœü²ßÛ¯M,ï˜]¨ÁÏS&`6x¢±Ñ¬Ãÿ)S9l4WÞÕmôÅ/\Æd-¡GƒïÐðÚ1™Š3%Å'¼#;èq÷\3#e•B¾ÑO'xØDÐ#;×T³Ùà7fæ$"‹Há`…‰ˆa'#ùŽüf,B8&¼¤*D6äy´.—IjSÛzuÉéãÖ¼9Íí\š†id“þJÊÃy‚ݧ²èÐæ#;öeðSUxòvÆ0bÝõ’SûÑ~Äè-:¯«¶PÇZx×Àèöyã@ÆL÷Üs4ýÜVÝ9WW‹Ù1´$Â@lf'É;H˜\§wªzé+r`qÉLB¼@rJ¯÷$}8ø·àäåú®ÞHÂIOµ‘*áT\²Túññ?h‹ô³ÝšÈ¯ùæÍÂÝIÓL‡]L=<Ó¤%_7tJ-SNL½$výÕ9Øräô¬¨ñ9û_Çl–èÓ1UÉ$ß‘g*¼x’ç›e!9 V‚R\-µì‘åe¿éPªÔJ"ŠÌ‰#Í„R—Þ²|îç}~§Šn«Œ€ºr—(#<é!‡¥F詠ͽÈ좤îBÅš½Múlǰñó‡Ôé4ŠsñuyAÿxôëçñïDGƒ”é-µÃÑdYÉ£=½œüqôTÔàËu%Y=TŽdŠq8…Œ»N'TÊU_<èh±CŸŠ3©aôZn¸3Î͵Ҟ•²öXfÀoDA.ìnÄ]¤¦È§Éóânn<ðÏ=g/^ ëm2ÞŸbI5 Þþ¡ŽˆÚÚ…´s]‡áq9UVŒ¬jUá6¡ jïЉl¡Ï/?CbvÆûL‰¥“ÿiæwòŵ†›,ŸÂgÃ4×áwÉlžxdžýúç6Ú[±”@ýJ}+ÒÁ/D:)™¸äÞßåÞY¥KzØëÛ•þî°Úèä?ðQθ‰mÐlvjŒõϤ2s¡n„ñº³é !pÁ×#)Êw¦€‹u¢Úi¾á(}P//•˜TÏ-¯wo~JATgû4ÍfÀ¬Æ‚SBj—'»³¡¾]k&n¯_3únð_b÷­ÀT>#;Ä”Ã-Äütcx`óƒå{½ˆÄ<Ÿ3ŒN=‹)œðïí$à߉‘#)èð}”µ>¢Áj%§0mq-M9ýìÞܯÇïÄu¼úÖ%Ð}õÛµØoúÝçj̤äxm;® ^ˆ—ˆr7CÊrJb¡¼¿®Ž…v-•­bó¤AÏoý£nPû#;pÑYÓâ/.U¹ËiEú«XúõR#”,#;Ó{K'aèhK+p…–}€°š]µÓÕhŒ6Û?åv½ŸÍ-7U›š…Ù59•œå_Ö 8X$¶YÇ7…¤ž×#þ…ŸÝøÔ?½¾œîvðÏ„=TtႎQ09ãSU|[¦N¹ct¶Â·ÂçÉ/Ck·‰^Û/˜Ë `%ŠBzË?(k7äïK!"TÄ…z¹°|ú!7/ DÓéŠHchä–:+q£Î«‰¸-Ð0¶A‹Gƒ&È:R– ‹.‚šAòî}1zŽõQ¯ŠÆÔû´ ¯Âùç8I‚ß{^®$¹Å±3z¸¾Õ+cƒõÃô6Éôç+I—·Ÿ)³Žêu‚|oWfxëŸ ¼_–CíŒ`FñgÁÜç…Û¤f…´«:âéÒ2×ÓgÄ$&5òZÓª6w;-tzõõå^B/s5šùºƒÅÖÆç&±ù?^;3®c«hçÏy&ìp´ƒ'Xx˜ßž‘Tñ0q³Ð¸ÃºòiÂqÒ|nhͼCËd‘‡LkÇáÛ9­ g Ý€`úA…¹¯UÏ›„e©ûvù”dYJz즗ü\Æ/ÎRx±>ýÕ>>1ç´k¸~ÑÁѽè©äQæacûKâô\U·iVÛ£[`Ë2u4#W•c#)N®¨9Ê@†Ž#<÷+¤ÔÙ'nsJ^rwR·ÅÑŽs%âOZâ¹P肊QŸÝ¢¶`EŽ ,W6B‚Å„N9gZANìm±°5H„¥u‹ÉVžt{{"6G¶®_vO<]$ïQöby“ˆ¾#;6Kk¼~=±2ù¸sÁ듽ãç|_UʉÆ;É¥ msÅPïIü?wx½|y0gF7;ÏD£¥KÂñ•Ï$´R“Y>õØ}ãlm#;¼ü0G5¼,ü•D>R‰ª×BÒ¶TÐgzÿDÅ´„q^•Çk@Ç)øak´͸Ճ á—“ºi¨ïkÍÊ»—9M¹£8‡ÒŽOÓXÅ2Ùd±Ñ*:VR׸@ÁèŠÇTlt&PzÚFfç§]ðïzÜ—°¶€âQ¾ØjÜîH )Ý èôZí¼«P‡Ø¢¸ öïœÀ~µÉl{¨«xT5­GDL†“QùS žàê#)ë-ОÝo«q|ØQ û—3û&á{Ï–ÐÆÉíÖóÌ.Úߪ1ôÍq¬Ö Œ(X¨šòŽ‹yíá¶îr%õTvâ¤âDŽ Ç #‰©}·½æÒ÷Zl»E6ÕÄN«‰ÑsšZ9¦à\üéak°t]8r@P!ŸME”9“®êkŽ ½Ê%BVûý²Õ'ÜA…Ws«úž naÃqXÇF¥,«\d-†=”k#<’urNùÆSa½úê+\Ú¢­fµŠ¥q¶.hEËu¯ÈÅú•uØ·‚`qih™{÷,å[§~*uQŒäÏÚYëf‹ñœk6 =\‘ç6L<-sÉÐ}'\U#;ÂÖMYؘížÚwôìJ­8k6Ïl5cÎZÅ^yhb¼§Ëš¥‰Áƒ$hâx_($#£õwù°tØî؃bç^ó\ƒÜ’ sÕÎ8Ñ‘´±ƒ\D‹uÚfWÂ\$Š„•98¥f_xo@§ô½³åÒpçoY˜ÇƒÞ}æ­×SÌ…ž~ª!‰ÜsÀ^anë‚\µŽqØã·ã€Û°¤$¬óÖZ«ý9Y·D4ÒYPZC%(ÌT=erQDG9)-ƒ{âºM!kÜç(¨Q¹Ÿt8x×'ôˆ1ʪä°*nÍÌ.<óÀµìºÎ5zBîšÄCÁ ï~Ú¸JÁ#+¾­¥P/xsªs>ÿŽ©^¹ò çO}s‡ê`¼i𲸙º£âõ:|]/[,X{{ÅβÎ:óK!W¨}®[T-:‘ ñiãm¥íöÓ§#<á±ÊLRr‹•ÿ~³åAeÝ1ìÎÛ:Ù­¹:,’#0^í0WŽu†YÇ¢·ÊÐ:ËÈ1AÑ‹;P&0ÞØñ¥nrèvîzÂ*ÆÝŒÂÕîݦá#)ë­ «XÝ@;;@ÄX–ÑHP¥P—”?š/ýaÓ¾™<µ’¨µFiI·_É m#)!@³{‚ì°?|p›¤€/$vf£Da²`$ÎÁ¦ÿ'.#;b\PJaÚ#) XP\`ôOx ä7*(œ³ɲ,9yYܼà Æ6#w8¾ÛkÁ]5Z56—5y$UÊó|œÓ)-îãè~ˆ¬s«>L ñj†t‰ ¨Ã5’슠šü¦ iæÝò«—-°‘]vŽç¹âºœïD¤EHjµ¤9ÙGYtayç(ÐëV#QÕ«2ÙIp>“Sâ½ÛiøÌÏHÍ)·(â`„b6´‘\b:£Q/sRÑòÝCjS¦”Ó›[*loÅâØ#)¦%D4ëo§¥D2˜¸`+ñ”9Ì‘žêz†­îmV;[ÞCp¤˜D8ú›žœøÃ–µØéÔÝtXÄ>í„e±ÄO2_e„<#;äªhÛ;¢aâ"f_à/w¦ºø`ëG¼}“?OéíÊ-Ýïµæš×]Ü,BF|bûUÞÓÝc؇¨Y >û®O:o_¦ˆ¾7ñõmýS–ü,qÚ6òžÐ]Z86#<‰Llx¹7áŽÙíí–#;5±z,—Ù`í²ÖˆÚFM;œQMTY;Å•«r§FŦ°Á„òŒ€Ç¢ìB VQSD:Fp©ò[nc—ã!Bð;”LÝEä·êþ[ñú8ŒIë/ÿRZͶ.K¸xá|‘ <¡¶£91Š®ñ&Øæ#;za›H’ºûlÍ'GóŽáç8:u¸Q‡^Ïh‘µBfpŒîÒðíN”QÇf‡¬SSž)š¥ÓO>]§mÚÈŽ¤4åúC¥OåËÅaáîiàŒh¸œ¨FÙiñyóžÆRG|%'ÚŠÄ ÷‡¿ÙáØ+±ëáï ¯¿÷ÍcmëÞM#ŒkVOm‘™MÛ7›K#SR•9MyßÃÜ/+‡Cƒá÷D¦R9Ôƒä}»Á…êrg¶ÝíÒG“臷;(õPéJöî%[à ‡MµxÁãâj["^NhF„¡熑oûžxߤô'§#;¤Î"käõ9Ðìç‡mÚ\¦ê.^šô·ðÂ[y»0× Ìk§ª Ùç9n|·¥ì†rÇÊ " †…k«±3/:N®¢…gM!8a”ˆå0ý92YŽâÙÓ¤J:w¹~õqP%K³×ŠåØ,\Áøi ½Ï²!Ô= 9ãï¿Eˆ*@’ŒmnU#Þn»§É)Z½Ùrq£….TDåHªR ¤Žm/O˜ú#<pý©ûÿOÒÇärbŽ„QÑg'8ý|¨ÖÕÁÛÊ dÂiú¿7—MbÊrÏhc4Á?g×\J‰'X:ùK…ª-¼#†c×Ïמ˜guvZ?¡?…Y°þöÃhÖÃß­MÕÚ-ª>–ëžû†Mí1ú€ôyù4„~Eá¾ÈÚAúk‹Ãôj)-òÆ£ã©e¶Š%&Õe•~lþx,ª`ªQ)•#åMëQ PjAÓ$×÷<9í™0ÍGÕtC|Ký‚¦§óêQoéáÓàŽ ~_ÆH$S7­«#)€ª¬Ë‚³Î/âχä_·wÁú W3Áq*Ѓx.¸‹ßK;:Û[“}OÓªZtÅÆÂ…Úí¬î{E#)åzfM˜¹Ø½§ÓÌ!`˜yh´7ÃH~;Gó¿ÈÿlÔEõC*ij¡;O žhµ·ÓŒõó[ûÞgrÆi¶+s8QÒŠ#<‹ ;}u:!Õ#;P?[õ§à„õ&R˜î€7C§Ùü-¬#<ˆ@ERôˆì‚Ú ’/\ •”Å2HŠü^»j¿#ñ*ø¢&’€Ñ*ç"æ¨ïdS,òÒ°èå“É”¹u$À†É„AÉ5 åC¬JG29ÖÂËNÊ<ì¶Ä°ûâíå@>è†P#;±¿¯¦»pík9a?Å¡ÙàZ½ᵎô8ÄÈËå¼îß’ô‘á_âãÉêíB«yÏ»åŒ|Ü;åÎ>÷n˜ú(_Üã‚Î~Qw¶«‚¦|½{âÈÅ¢&šæÄ‘8þv0èÑ‹Z¥ÏEˆ\4U‘÷‚„ÝU3У µ¾!I@ÂE‰OhÞÕE•#)¦Ò§ š0‚ÒL•{é|´•¤i›†¶›{•qñßúñI޳ãf¹©»P+Ýåeäà]ýµ¦¸ÍòÚ¯ï1Š‹Â©#)Š|#<³¨`5Q"Q¨õNçZÉp`"YI˜Ûã¢"V·qô;õ@1É ÅÅÏwÚäàÂþ'%ïW¿»Þ"‡bOîdË6aÑìýjÔ2¾4™!æˆDí×e³M±’Š,=^I™oÃf{M•¶ÿ‡{n04# œéï|âàÐsU¿ì¤;5ÂJLãmÚøGï6ŒsC7Ü€Ýþòf×™^Ñ5MÒMRATšj„NI¢QÖ¹ðÅ”@q™ù†0-òãâß¶î|Rº“™OAan0aÐz’ïI-. ê”cÒ"=;*¼þ:óìRkTBà-Š®Û„ôN"#;àø ŒêïN#<ˆKƲB#;F¢öôóÐ~Ù<„ÆOAë¢Tìœz9=‹ÉæŸêÿ#)h`‹ñ¢™”Úåœ.{7\‚ãЋ|k¤¾*d-Wµ¼ÿy˃2|ý#<’sÇjû<‰Þ×Iåæþg×¼âû3¶†m5!ŠK›owœ¾÷“'H“Ë,ÝëªÁÿçûOáHò¡Ôºq#<“×¾îë%*ÎÛ57 ÂŒƒ¯ÎÐ…BI @¾ä­ýó"= Ö»¼'3\›˜~'†ðt†8r²}Ê)>ñ…Ù7)k“|Iæ'õ±”Ó÷~«s¡÷£W¦;ÁÆaš ÷K‰ÝáýlÙ–G>'xb.#;¯™4¢ªŠð®&"jY[u‡4Þü祕åÖLgEYÝ/­vçeéoTEE Y6ޙʡ. 8eÍj0\E£¸€äÎpê뤞"I™QáÒ0pp~žl\©º{eŽ(2´DVýÓzˆ¤ÿRã‹ø=jXÙ‰×0v%¾«Iñ8Ÿ¹pg(#;¾o£/ŠõD*\dW*×á+­æLñ€ñ0Ó¡TÆhB–ñM`X!èŠUÍÕË#^1ŠŠÁ—I¶zƒß”îÀSiM¶k 0Šs îšÜב×7$Àb"…îW(lP°ä—uDFù #Þ{±¬Y#<÷úb™ª»wDéŒûê#;"“6ÏQ,Cú~¼Ý#;«êe²Ì”H‰xÞ-@ ²Ò–#Æ<´ºÈ ¸‡œ@;K±ê#`;k k»•r~w)X·eÊÁ\­’Ýx,lˆðsGfÁÓéyâåCIÖwBà.7!GÎXÍB^­éÞDÈóÙú­.ÍŸ?ІêÀsœùi§kP=ƒ¤DoGÔˆŽ72F—}–æÍÙ÷˸'!òsRÇ-ý‡ÒƒÏ¯ÓM”û¹J ~dÓuãíxå÷ï™—ˆzíLĤ’YúäõëäáÔ+:IR¡Ë§L¸ž #<5ƒ*‰¸äèdÏ<·.R ûûï«çÊTæCÈ·Ò_Nü¼}£§zø¶JgÆÚœÇ‰Í3õœŒÃ4~Žâ~þ”À2$è"¥¨£K.ÇŽnJP0¥Ë}–ä*¢ºO¶£)êæ F);#;ä™ln}†Æ½km¸@:h3¥ënçS4—­:d6vvyóþG^éëÙÏö¡föÓ=õ>—¾Å6ñøËÞ®dww/wè¿£âw“öš³³¿'ÄJסLűÙ#;d)GŠÏ^ÜœUU˜~ ªmX›nk¯™MqCäc#)°‚Б4r v½æ¼8EӭÕ^, XQ¦ÅZêhA!+äŠQk–§UXC#$Z¬sºÊ Þ±0Êot“vR¤Ÿ6×'J‚Øwòã¦ß=ÁØ)Af§?s8‡aÅ–'ur»HnTN¹Á ”DUMÄó¸1I².”à$4¦[#|=®“<å©§_çÉaËZy¦ÍùE¥J4ïàqO¢d–ÜpºvOKi*‚œ#;eÙʲK²)kì±ÃŽÜS÷Œ1±#)§!§>/dÆZV¸i¤“_D €! ]Ñá¦á[lM ê#[Wf=ήõá#;àà¥Wɲa¯D@ÅV‰71(ª£edŒìª8F3°tíÜPãÝŠJÅMšQi"4ØÂ›NºüxŽœå0Õyó6ç#;ÀÑœ¬è ¦ùèžâ\Ý&â‚fœ¨áÝÉÄ­#ó¸Ì”jM§óíþ×Às{ †óvdþ¨µÔCs~NFËóPù¶%1®Æþ²Ì­á¡ñSgl#;Ц’ªeß¡ð°³P’ž”?¡P)B9ÎÜùÑÊ9µÐ3zJ!º»(ÝIü\û½‡qý™“û°¨¬ˆuœØ’±´È£»wnÔjPK» €Å‰HJU`‡÷ÿâ}èÅSL÷%@7ÿ‹Ó5ZÖ1ñªýè‚ >Þ¿ãEb*ƒ…øžB1_×íøÖ𣝯‚«÷\øîú('Ͼ âï÷õžÞ›%NÐü=> ¼öR{•'ÕŸ~X¨ª° iDö¡Ñ "‡ñ‘'÷Ð#’?ìŸ?ó}þŸèÑìâÿ1¹ý?—î¶`ÿ%©ê†šzt0oßßH›q”÷­ñÏç#<úü<9Š\ªç/5.¹À)KFS*ˆAè(镞(Bñ ì©‚ Á”‚,íy!Ü&ù1#;Ëq³ñãîtc«ËFÙŽ¾ýlO²½Äã Ï—ÛßÛXkç"ᛨîªQwMgÓî×QëÔæ—Ÿ¢‹½€àbqa.ùP©ÛúïÄÅ·©ºçMüÛ1êÙª•†D`Ë6ó%ã„åÝ©S¥ý-üökþ #;ê ó§°Gk!ˆl<½&™'®à|/Iïîµî7 Ô¹ëIƒ'’Ò…LjÁŽà©ÈŒ¨ˆ»ÏÞ8¶çí°Z„]mmÄñ°ÒèЖŠÀB{Øwü^xb~ð¨ÉpÈÞ#;\­Ò¨¨˜™õKë"­3óý´^ïH–ß·Úi‹Ÿ­AÙ#;A>P›#)µÈôæîöñ3ý~–HÍà×Áþƒx€A™0l#)zY#):Oá²åÓÇÅÌôªs=v¡öäÂZVùE³ÿ*o¯_ªA~HCª'¯²AuÓBþN}ÆÂMZˆÛ°•tD(MP0鳋#)Slðˆü7èå½ÁC©ªXU‘ uÝ ~÷ž`Å 0 F“ctlòÇ_„¡[„6¦â’ÆdKƒ¶å#±Fí@ŠDB’.#;j6AÙÒÂýnAlˆ jÙ5J#;ºð¨ä.·6žÁH°F%‡<’’ê&Y)º—[}åZÉ#;D” Äé¢ ¨!œUþt([CæÔ=„ŸW‘TîñðÀp)å)\9Y5”’pR!0NêS sÇÎdŽŽMé¦DˆCÛÇàIºøv2]…P½C(\ª)’¡ÚÝäAe´ÕÓAÉÂ#=zò°4deJ´a¥+ªNÛnë½6æ9ß¾ÜC´²!:„R\þŠ«šÏ`åþ¦±ï<.ÍÚ^©gÍZŒDSb6•2·(•¾õg áâvz­@ÆF¤‹‡§ìjÈŽ­/V¶æ¨ÕC(zSýDÃèiñi‹¨“Š_†,:ÎÊèzL6퇋@øì©¢[«ï—˜®eñC®ª˜´¤|#)!ÃÞ‡‚ ìN‰G ¥¤#<^³TÕ5€xæ'l°ý©ôX23hÇÀ1;Í©]~|å ýQn]97–< ßä­^÷\›4YÑLÓe•¦d¦,}ÇŸeú£=N»»’æ§S#<¥ð韖nÀì–ç4òx!¬3àp³G‹»kÙBœK#)TyX$^Cn¥ì-Ó6ܨ‹HY!HD(rõ—fP8dÃ1lÈEOdP/'€m¥1òÁËnþðEOYçz†jpIET=®ì ™¼Ž\Po‹óÿzUX¶ß¡‰–8ähÔêk>JûR#;¡6‰Èº&"I!²†`“Ñ†Öæ‹ˆòäÂ#;±èìH: Ú+f´ˆç#)ÈC7Ò-°¡ ï¡/-tƒ¶:„ Mí!pð¥$Ü@w*Q¥ž{Š£nûÜQI´AHq…»;±ÃF|èRíÃOÏÔí;&9ÉŸU½&è½Öw )¼ŒªmÙÕ¿n:ˆï: mêr$M¯8H>xua¬Ï|CHH…Ò¦ù¸Lº›hC¨°EêÐÎ ]Á üüÇtßÞàxцHù¹dg¾àœ§˜ÈÂY¼ä©”|jŠ‹#<û‹¥Vê›sU½–æ¶Hµcm|eWÞmÒÖÆÖ£l›Uã#)ƒ#; ƒe„>Nɘˆ”bKÁ`¤Ñ‚¼ÎŠM•”1J1fÚ1.nÍ^#)ä©$ááR’#G¤p>@óq 6¶xÝ*_ƒð䇾òÇDù’&±ŽóhìóÏ‘·+K$IsD‹¤êl|7È·ð®D|÷(y³¸´W—-!ïjýtöžÛy×.-zÜ$$6Õå-&‚ß~ë¶llÍXÕø5½G,@z¡Ž7èñêV8‘ÓiJg\xò`,ÜÙC&—™óè«â0È¥X¹{¹4bz¡Ê¼ µIK)2I#-늤”–¾5#qöõâˆPÍI™§Ð1±)ÎV4¼)fo«²W+ð“t’ù¹µñ«´¶ BP vÛ‰Žê¯?&ÔÉ€EÓ4¸@P"¥¶\Ig›cQ(}ßIiBˤ…A¶#)Xþqòónª¬fvpêMÈç_Qê‡Ãh ü.^p¸Æ$;°¼¡¿„îI«¹¥›'¯[C4°ëË1"H–S¾¼%×[v/¯Ö#<¯ŸmvÈæ¹-^+%ÔÓßéûÔ4vàÊ$U’)ç*œý=<5Ì Œ33ßéÛ;òÁX1p: CpÞì œI<¶ï¦R‚ü>{DÖ@#<6Çʃäßf¸õ渻>½Üü{N#<#;Ñ8*½I¯Áà…Ÿ#&#;7£AÈÂQ¶žJ5|ç—›ÍÚ×Ò‘4EÊ‚¤"1º¢˜T€éÕe¥Eò› çèDÔòù˦x¡í»^ý«ADtAÚŸ<É©2Að|D#;†ïÎÌ^NŸƒ`nŠ„é¨ׇn!ì#<tm áUäÍJ` ;ÏfÒÕ†zg¤ ¬2/['2,—[»ýþŠwç#)®tÅ é䡃™äwØRĦNaþˆ®³–;÷5ÍôÀY·§P‘›.{u;à¹MV»Ù9»¬˜Ëe$à\AT\È(A$ÐÉ ¬·ïøbg®-îIýn­Ã¬óó¦åcŸLôówM§wC§´ Øoc•Ùò÷fŒ91AV ’A´í†ÕG_Ý-aDö+žŽõà,5¶vü·)ô¥±¬ÍUv ‹~ñXd/´NõPHçHÒ)Tþ=îi×HBÓLCa#;CK­3Óœ!°×Ÿ&Ã#;‡Ýû|ÿ#<äÒã•™À×½™`ßa°H’7ìhoÌ#<(5§çzròáèŸ3#;9«í8q´Â…ØøNÍ#;P“)4z&”$B2>qaùûo–N»«Ågw,8¤ÈäŽ2FÇ£¬H{RÓÕM,§®×•âûþvb¼êãÕ¡ˆ·›¦Åˆ/$†A˜ÚƒÒ{ø›òÁ5»á½Q×iD4j=ˆP#y"_‹ÝB‡=¡û|s3„AÌ79ž§‡^cs³ï·;üžº0øÝ,‡k •AsÑå~Ý^Í1jÒèúRñŠax5àÂ-!xŠ‹*#<à#<È’kZ• oí9ÔÇ9·p|Ýyo äöHï(4ï8Jwî#)[ËA"ÇHDä[÷ ’I[1Òó±F+«t…ð³JD;ì7õ5l£¤ÄõSɽh†ð´7'¡¹Z‡Zêtá Él¸°¾ wľÜX±¶K««ÚîÆy¨ßK:Ü)>ŽÅŒàt×3§1\@iñm’Y!2#<€&$) ƒE°†(¤8×[a!WµÔµ®®Û­]Q¥/}^µui6£àÊí|o¬!æu˜kƒ§ŸÝH@Bûá3׈ÿlÅu­UuP¸ïúg×îú/jæ¡§•£oSØ&læt6ñÄ&"›o€‡Å‚ØÆ MV·>˾´/{@ æfù¶›#)9Їµ¤’èaãGx²#!éh†Éé¸y CÀË<åév~~÷Õq(˜O’BCNïp!hqðΪ®–_^S–š¤dìƒ(sC•«ZŠ#)œñ¼ Ï,áÈ®¹²L9U€2‹°&å>Û[‘-E¸¼6b#)¦0 ^¿#;ƒÔMÁU¢µK‘Á²ƒƒÚýhUUòjkG#;±¡LC.SÂèq‡)1"V(•¬ô“®ú‹[G‹é´;8yêuâ6ÌÆ.ü%ÖŸîl(M¢aXö„†jM7Ûb è2!Ûô^g,É#<Ãc·Ä8¥ ³‘·}y>cÔ;Vg£¼ç²­µnE:>Gȇ>†‚'£ª fù(Žª×ƒ â‘MÐêÎl)í:ÈT˜‘ ÷Rk]»é‚hÂq(Ãv™ŒêƒÔ†õØö¹*è0¹$˜ÚDÑRzúo<ͱ¤ÆSEŒ<ãcº± ¡@Æj“ÔÖ]$‡aÝ"pf`EÁçÞ[ƒò3Øà¤†;[rYsŠ ø4J@ès¡+R a°>}~œó·²a¨Í==´SkŽ”ëf^®~·9qyJo.v=£ã@O=JYœ9ÐöTíè ä´ï©â²‚¥=vsvªûÇm68÷T,rw¶|u·EàgP;¡K°ê H‰ß9zöÿ_¢¬Z×Àî!üyõ÷ô¬ÄWÑTrâuîØz·å¤hµ¢#)B=øH¾GÆ6ÒdÙ¢o•¾L%§ÊÓ ›«¨Fòz™‡æüáâyOվ߿ØZ<8><§½ä‹¦OÃú0j‡©†©èAbÀbl=F¡Þýœ5ðåêیռºnÛB¹©È>x§N³·÷tÏò+Ë߀çósáúæ˜^"±¿tŽCàÁéC<a󿉩‚U­¶%n>¬â̰’{Š«•9ÿìvv¦Ë“ì³y_-ÿ𧬠²ƒ{Ã6³·×]*A2>24g“ÉŠ¯¸5v»€Í#;¢4.Ã7ó† ‡¤uºï¸i·cäöIãˆ?t '™7(:F=>$M‰ŽY‰>ñq óŽî§¥FÕéOãT’ÃcÍïYè¹8®•±†²§ÚÇ8'÷a€0x‰¾äÉC»Óä±·Õ6¢,Ha»[#)ü?Éü>Vü³ÏûrÆ1}Ñ|u™ŽBkQæ(!õ¿ºŽ¬å¢0k"Èj®X[žØ~ßÝëÁ}6³'&n,žBW–x6}9™k%·:%‡òŒçû¡HoÖ¡€Â©T}õr’žX­1ááG§=ú‡¯}&`ÿs’"lë·(%ÿ“ü_XÂ'ôÏð[ç·mçgŽ2{ú["#< @öÿ7§õþ?§õü_¶C÷M‚ÿ—úÁÿÅñ­x5Ü+7‹ñäþ0ú¨¯¯#4ц¿ß|9á5ÎÕ|£Éæ”+¦÷Ù¶rxe°ù²K+Í—&îÙ1;çämókšÛùn–K^9¨ßÄW>…eŸÌ3üO<ƒÜdú?£=Ƴý¬]H|ÍoÇ_§\¢)À޵Gø²ºf‡¡Ž_·õ„+¿ÞL·’-ßÉÊ.úñAHC&ÿuÍùvó§8P †^:M¸„gˆ™ë‘#ÎÄ©+\SehŒ­tT×P‚~ÌÂ]Ô݈6 7A¦˜%ÁE†®cÑ.¾ŒÙsÛÂéWd;‚¿úê‰ßWS‘ô#)%h#)#ÃÃör%?0áD xµ,b¯â³¶»°}Ò¡ec—T+¿FCÌ /¦‰ÂHÕv&ÙéuhÞ”L!ŒÑʶ²h€ ¦‰ØÎµF¾_<ß#;”•^|$¡il™.ÕeDª{Xÿ¢J#Tà­ËH£—!Ïz\ z×.7†r+´3Ã’ ÔCD.° £f±é»^•¡>ïXI²wU"((³²QîöíøÕ>êçkà[¤´…b²Ç/«íáöcƒŒ¿¬æšúÛ‹#mNƒÚÇ…vÉrð˜Pëúµ«µD>3’áYfÔ|Fqkðõ7œÞÊM<â6-4ýØA$E…U•«Õ×3ƒ­Ÿ#;¬ÊWõ33‹r9›‹cºÈÄ*zÉàê#)u„èòÛP5–5¢jVs×õšg™ù8ßÍçûÌßzþ’ª*%¶yÿ?7ê}oõz»À¸.•zÛñãåó_ãñú$qÚç7e±ˆWÂ#<^!êüþ#;ó‘Nµä Ðò{úÈn]XM¾û.”G^ð~ë#Œôæ5¨#^¯RFþèö#)þ<¨eÑc=Ñ€rÒ|ðjñ™¦«òijWÍ+ õtÖÂ-¥#)~ÿ~@x¼>¾1ÂH;Ü:÷ùÇ*TÌ)#Ö!óéïÖ7NTºœâ¥ÐƒÀçÁñžáO«ÎÜä9ˆDÈ¿Z€;w¥üJCÓ?ŸÑ»~¹ˆcjZ<Úù¾`ð'»~ܸpêë„qã¥^$<°“Õ½pbgûµáSþG뜋[2cJ#<¡¿-î¬v¡íù¬X)y@¯ƒ!s2µ½ï²*‚ÅW݇“âø¹Ý 1Ä•åæêû°Úƒr|s`@ž¥š¯Ù¬²î*Ò.WJ${—Œâä»ÞÎ?qûI÷­Ècôm‡óî’¨šñžV ÜÌâ‚B–üý7áàãÂB¶‹ž„3繂=k£Í†f¬~0ÌÀg9«W#·ü³_Žó9UÆâ#)mÞYÙ÷‹üu;#;M„­Í”Å¥lóJÔ¡k‡Ì涆ÅTç9ïÛôù˜ôœ&b!æ)Eøýú”òáË.G+^ÒŒ(¹Àr,fõt"÷;KJOåò䈎r(Ø¢¾_<ÝK" —fܰÀ=g±d•ËbåV,ú²°|%ƒ5ü½zÐ{*u§¬yýÃ’1Om&›%ÖÛò+§g½ô#)ö Ë óF#)h~{vöév#)Þ1ïLÌAðYE) 3I|#òÓ·éÉÃ~\5@6½ƒ~lìý]ºÒwªæÞ®F`#;“²ª$¸ùäÇøíü›oªÏ;>f:~Er™ÜNã½£…ÕRë>>Qšßžc0ª*ÝX®n±.ùªÖrþS_1±¬™Î7FL30tÃ#)=?÷í9œïÈ ÇË÷r¿‡-_@ n´ù¾ŒÈ’ ¿IñÈoÜïG4†Rý-xÙÍÝhôqŽƒQVÕEm‰«Ú~#‚V›fBü·g³V<[Ž’éúH:n)¯YmïʧŸï¶ÖDÓnà"FÞLrÆV }Å@e™Ã÷ü¥'êQ{··ã‡´0ïädw8½=§ÛAò‰ó>¤àsÏ=Z=Ï«„fІâ|lúXg9 à´¢»ÏxÖ05„õ/«œ-×kŽ?æù–>(ÿ'‘|†d=\:8;_o›öïžw#¥t+D_-K‚J Áb¹³H®÷òþ;lÔç~ʼ_ˆd‘úâ!Ûœ»HHóSá¯öT@j¢Ô¤îR!Ó/›š5. 1lB˜iʉôÄí˜ønzó0nì&-ÄrçëùM7*hÿŸ´4Ê)9§–P’üäZïN¡s¹­JwóÑóâvÖ8Q¨MQD°ÜÊÈÅ_G,^^ÊOÙœÒáÉ™ÌDh†æýºàü,t—å I»eü·àÿW£Žy›5&&ý¯Ÿ¥™|ºsO‰elQúÛ5#ù>ø,]à~œó|s¾u:-Ž´ë8ÇoŽÚtm¯^¦qÌùÈ<á_¢Þ#;k™ˆ§¢~ÆX#e𪯽z«Â[ólj3iŸ¶ ;TB›’z-±9Ã{´c$ÛË,Éà¢é×jj?ÕµZ:wÚqô¶ãðž˜#ƒëu´ÄuÍö­S•ùÖQɵÅx¦¸qþ×|MZà·‰Pºú*ü<¯!¡àTëxŠÃëfÅ˦`×pÓDª€Â%‘Óž ß#<É@Œ2جû¯îÍç‘á?¹Æb3íP3ß0NìÁãœÑ×Óû*L»«6‡œm,vžcºÒ#<£’¿gã†=xãñ„oÛg bÄÕË™™hÍ'  9‚]à±Ùެ¼,‚æn¥4|ioé,ûâÆ|ZY­z?Ð4pÖŸhû¿éú\Ç©«$§eé‹jšŽßøÑÂàL%°°¾éƒñhvBâiÍHB8®!þØ\°"íSêaÉ#;xךIõ§Ðñd#;ØSÓá[õ£AìI))„˰üžŒ2':1MÛ (½e»ÃÚ$MgÓ1Êt{‰˜´lóóý…öpéžE—»F™ã×­Ly ÆŽe¦v|ùeðŒmŽÎ#)«Ir8üGÜœ–~k˜stvý½cñLkÛÍ<$léõaI3Ý?[ðé7þ ç6xj îÌêí‰Æú~ìaÆYõæŒiÇ1u_Ùñp)_„@ÞÿˆAÌ#;f¢*¹úv¾¸lë´~ÒÞ–ÅËAó$ùA9rPIÍ&ÛÆYðT¿UvJ‡FëªgC~NçWg´È{´k‹ÂëÃϮߠøuÓšþ›Ý #;¸W+åfZNŒ­( 3±‡ ŒR?VúJ½ªìˆuC®²,¡h««Ì„¿­áž“ŒˆŒMãl>Ü?v}!ô#;ë½JaÉ=Ì<Ùy’¡É7FªAÂtæ¤Dõ3 òIÞå4B['© Řá% ©y•iípV[näÈc(¯Ž‘ëÙÒý0|>™©Kæ†/1†ó)#gßïÍqtb½ô‰¼Â$°7 ÒCùZ!þˆl5kCif@#)Œ1€÷Œdª,‰x  Üøâ QÙUˆ©ˆuÕ!Û4µµçíÿ?Ÿy„ñüsªØã  õyckîåñh°\ø|µ¨R,+t, Î邱_Öùûäƒ~ûo+ÅçcâR&_¢1NUt)£ðfS#ËnÊOvÎC~[w’y###)fQu¨nmVøÍ,(s"7Нž9uïÞfpü¾oç$pÁ3?ßêÛ6ûjcH€Imýn_'c[ˆ?Ÿt`Èyà~ˆ…ÃúÊvŸ©:dR,v”ŸÎ@!dÞ5GßÓ¯›kE´ô5ÞÓæ)Pµò…wx%Q|ŠÅþ'3€#NH#‡úOæ?a™ÁÕéqÒÉä3«TøYßùñûƒ—ßò.x#; ¢ÚBÑ@4«È)òHšŒyÀ8®!Ù„O7˜O s&Ï#)¬Ÿ…ß·u/'0úâ˜EŠ~©GAåcŠP~Uĸ<Ia²C­]~¯³ã×[Tö9C):CÇ^‘ãŠ7æ8¿aÇ‘Zç–´(Ĺiþº2†1å·qNžòƒ5€tØŸÔžqY$N˜ í'8m†!ÕìÅ·g¹,ås¦6Iåƒç’$$Ÿé*Æ ˆŒ@r~à~“ð}á(?2õæÈ 2³Hí³_?#;´”„ä’œÀÍö)ÿœ«»õeÝ’!öà ÚüƒÃk>Ô8ŸÁ7Úu¬‡Ê\K% )æaž$>ï…êÏûW¸^ÔüòÒí–®Áß’é)òH”Ç%‰ò s•‘m ç¯{ÜìçÊ·Mܽ#)“L‹Ø  ƒÕ„ykéȸb:b÷g"¡Å×ß®#˜NS·5Í6Û˜üø0#<Ó]1ç@ù扜z²‹#)!,Ô#)tΛ…Ä=ÒëÔþ©…ÉwÇ¡¬UzÖ 3˜,¡† z‘ȱ]¼´Q%x^»]©d·Òi†Sâx4òã@n•¦ÝòWŽNrÄ;6„MíÈJÕVKILÃ)Øpãøë¿;y­RF^¦v:N>ÄÝÔuÑŽ2ð‘ÆK„@°†“ìºf^£Ñ*YUñe‡^¯SZÌBc˜$<[ŸÛ'ñôã6r9‹ H½'{>åv‰+&`Ã)U@¶çúH¶4#)äS¡K:èŽÙtné"ñ=bÖ¨|ÎÒéZõ÷ì`pºC‡\Åâ¸Ù§š@¬.s–*ÑÝ0ô\è[u¨"0ïÎ!æîÖL\œm6¦ï??[²_ÎE´Œó“y²šØ;Ÿ½0‘#;×¶.e·Yy8dr˜ß?g|{K4ç¥2{}®<Ö–›F ÛåK…”êkŒïy>§S°¢ä£êLCY£Ô$®ñ®s+§Êê#;&i¨¬u#S3þ_fë.Ϫ:«ø%Ó]} œŸ®‚ë_ëÃ0ÁðE·û3¿¡ÔÙ¦ 4¬Ok;IÞæ¦:Ï©µ#<¶}TÖ+'‰¯[-uÔïlû®ºyuÜÏÞ¹×{”ºùá#<&aVÊÄ:’”£:h*õœHrMIjÜR)ÔÇc}tiïêÁdš4Â3kÍêº_ªGZùéM‚Ë`N{tëž|ÿ¦¸=Э„‹öÅ’´ì˜VÞY÷¿–‡=½»ÉUŠè›ëóŽw³¾K‰òðƒ‚Iš».¨¹Î{LÆ6òÇowÛƒíÆs¢VxöçNLvê$Dâï¡ÛV¶É xËœÔзª‡°ˆg –)Òê1‚_t3)Bš2¸^«˜tÓÍ£¤œEüT•ÔTj¥sMó+­x‡yÏ^ª÷#<Ó}­¤éág±ÛŒÓ\ÿýW`ì]C’#

Æ•F^뽅ɹ,AžtP>¢HmK‚‚ª=Ë¢¦>Óÿ£Tº#<‘òÿ…9ì¦~çÌÍL+@ÿåRUÚ¥ µR`j&hhø½ŒÆí)::‰ë÷½­!µ§l ´¸rTÎ9“Â\èô¹?ã«”4ÍÂ=µá×)êãe“¤8üqúÎs¯½ÏX.éÇcûH@L\½dȼ³ÄßÁ‡n³´ŠÚ’HæK·f¡gÞŠ‘ ‡üW/¯¼°‰j‰ÜYÓÏ2Àg#;dêÏ+å³×Æ ´ñÝg›a\€M¥j#9éÛsKƒïªßj¶/åÞçä”Ï8oÊgˆÅá#\ ›P5£ªÚÜtœ$™#)â5‘[|ã¤ã5ÉÝ]w•@¼Jä`êÞ;AK7,<#;Œ H7IË$²#;‘C1œ¥\l¼‡,i×U4;]ëMÔ_ÐýÆ|-¦Wá§sIb0Ï™ãÅg òõ·‹›lxdí^&ŽÀï{[™NaêÉœçR#)¦ ’÷³óµÚ žÑÍòëUË å¬3”MͧmáÂd|ì“¡qÜÆtgÏŽÎøõуVw:ÃoX¦á´0"Û4혂û#x“^ù¿·çGÏB0‘Ó”šˆˆÆ#)n†ñ‹¬F<Òkj•~ا^Yå4JÙy‰£‘vm ,7,î@y+ÆOOj•ã÷øpÄv¯ß†q7T#¤#;åèògNÿdm=‹b×tÊ]¶à’Ž(¨–G›øI-ó@þ”brÂþ‘ŸUlj¬¯ÜKw›¢[iT~—E|¢üÔhÃÑ3Œ‰oª¿VTÂÓáAàur qÐwn’ìo‡0 Û—Ç‹IÚfQÜœµntbW¦jxôDã;ù½/¤Õ7‚0Ö¨¡vA„Ä:ôÈLj„㽃å~(-}@ŠFÊ;Ðɤ”¹‚vЗª!ÍÛnç¡ÁÁðòðÌõC¸i„47N‘¼‡ÓeÁØf‚¡ØÍD }™j:{gH­õë±ÈíÐVÛ?•Òê™»nЋ0NX^ÔÂ[KÓ5ðEuòº!¼³ëg+šsPüQ b4aÐ Ê¿Ãææë´×ÍÌùo¬¼Á»ŽëØì΃Ĉ\Ï}®°îQòûàÚý'"=¬ðOŸAÎÞ;ruUBàðÖ»&#;mA@þ©ô@.^ (ŽzÝ‹a'9úÊ«`Žr9ÐMÊ;”Å#‘t#–a~ÚpööççáËoà~n¡A²L„ŠhpÉójÉɼT`${aA &ÛÃ%gؾuœ¹¾¤CmŸl=Ñhƒáïàx"L|8K.û÷ 0MœYÕŒxƒ¸û‘œíöŸ_ãyá°ÛrÛÔƒÔdGGcìFÞµí²[¤-6I~žçQÞ«†jÁêŠ-åºã|áë¢û¥äT‚âôp”ÝŽÙÊpÜ¢1\[Më0o›…Ì„BŒ÷êxЇÏ‰OàÍ”%µûñàv°Ðd£'go¯ÃÅŸ›‘IbÅñ^€Ëä¾.°½4H,û/ÂtQ äáºu½Ào¢¢t”è[À6EtïsÝ\µ tñe g˜£ˆÈÉ[üpÃR·Ž®L'F.üãàÁé}“-¶/'~Íó¥5§:F<áÚ•»ïM÷Õ>×z<٥ѵ¾;!j:ª§ñ¦0ý>X뛲\îÌ£Oht”ÊàŠ|0êãEö냖uÑ¡I?-û‡|ë­O„ÝÍ«¤csèƒZ™hÞðún’^üÐh­ãHìm»|¹Š*v…ÁºýߨÇÊwÊÄR8jÙ§åGPÀî(“k„¬ÁÒZ·2­mLõr£P`0×1Y‹‡-ϹÕKG(„]¹ î+òF®½zÞý@ë®#<£ðy¼°7‡ªc¦McŒhî×-ÅgáMGR@ÿ/îgâõùËü$åR+jä5 Æ"xPbHá ð¤-1âÖ„ 8¸¹/v4P1=Š‹PŽ"cଣpægbÏÌ""Îüüüýoιò눾@Ø#<…™öDOŠwR1Ó\ì=6;‚ëõ­¦ªƒ20N ¤%Á#;3XÉÌMåa•{!KÜ›0TƯ`€ÞVÌV‡‚¾zKÕ•]sÜ8iP§_ó~œŸ£ßê”{?'Ð5ƒ»‰MÃîWéãg´ž$ÿ‡ÃõÙì¯Ùè”o¦Ôï–+ úŒ1´#hÄ¡Á¤ç$ó輇B¦Š‰Rƒqó_çZÝ%-Ó£IÑ0ðdÈN“äaHãyBr*E(Să„eþð¾¯ÂŽ %éÂìX#™B'=6„´ ˜©$ä`W«âìø?Ãî襪#;º".}§”sbíVoÓñ] ò½ïÁ­8ø#)>ùgÑÍÆÜ»BÐ#;NÙ¤\¼1”뛃géÕû_Ó⿱Á#ŸéûŒyÆ^if4eàÛÆ uÛ Cy×K<á(,D?€Ð€ªr\§o##<Œ5Ô6í,Ýþ?ˆZüÎίÙüW•Ê«;ß÷îtáü#)Cø}~ì|ØTg#<¸ª Ô$ˆÁe#<Ýä¥éûö/‹×ýÔŒ#<7Úž?–ÏÇv¯`ËõþùГüHˆ(Z,åñý>ôC6›+Yÿ‡¬4¿Õ*©)þÙû‡€{?£U~x{ ¢òíÄÊÅÀ0š©`ñÚÀð6ê-ßëgøK{tÚøwØÔûáváá¡a·ù§ &Űm@£øƒ' ;ÑÌpsìFÝÕÞo6*šNÖàD'CÀŽ\įO3jOWŠ#“^;‚ÃÃ…S>ð˜Å ±+ØÉùä(ÈR8¡0–º ¼Ãý®aeTíMuöx›@Ô.L=éŸ#;N>ÀQçöy¥:L>µgëPÀ$/úMÍ<áÀÒãû·~eÌúö”#<; CCéG²#P>½_ö=7óî¿ú,ªh+ðŒ º¿1=¢oK-#£ ܹú.î ÓS0ô®ÞŸ/. ™†ùª§Ôn(#;ÒO¿×½¹’vÔ½cªnBpý~=Gó®ã׿٧§GcÎý›Ý]µ_½¾^ï8nÃYÝnðJròûÁîåh.wkì=§&ä™rIâ4Ôªõ{ ²AåþLÄG"\@²#)#¨HäC ç×#I~A #<Åb›ï¢›­Cá<ŠÇƒ*CÙÞ²QâFOÛ¿oe5[lÚ¨¹'»åà }}ˆòǹ;Ž ;ŽkÄëðmà ½h#;>òM†áÛ¹%Ó1òëé7ÓÞp½§…±²*#;Ô°l1‹¬dI$ZÌ8ŽZ½ï. ¹à´¥²@÷{‹fÈ@Št—2›½ …òÝóBBJV|óIù?#)Êä"zÿÍÔ<åãð#<« ý_Í—]mN²æ#;Dà.ÆÎØý޲”^´„ÿF¾<÷ß#;iv#0ze†ó#)ömo<É!”—¦pÏ·*ºÇú1¾?ÛCóþú±÷“°’irêep5H[=fîÞ8ëÏÞä;žhƒÖ‡meÖAõU¡J— Q‘ÑcÍ=ÄÙëëÇ …87>|Ò›Ù—#;¹ýðè–"²H‡RJrÅ“éËË©4-IJÿãé =²«ãošÇÔW絞۶ÑÏ®}óý'Á •› Ë#w¨jwënÃ~ås9u‰û†:Š…#)‡…¤0ù8‡ËíÝòJ(.6*¢wš›“v‡Þ¿Í«,þ=C;e!¯àjBß!O RQŸvKà#)‡ …µ"¿Y*W¸éÛ79ˆ@4üÿÜ*©!è?3ÌK)Ÿêtñ* *˜D¨ uésÞoos¸!µM"hYNó»d7›j\²qsÎÄXôƬ—kçÔÀeî ;ZŸƒÝŽRËŽ‘lYñ‹vI,…UœÕ*ªŽšÕi ?w—4„îãœôèAÔ*X¿^ì=Æ\”Ï€jõs‡a7æõà8de#;§C¨Ítƒ½ßñÓc0íK6Ð3t;K˜`(¢FŸ¶Ì,IïþýI§ÑŸ¸ÀaeÔÛúb»Ïâ1i‚‘šÚ £¯3ó#Ù>¼U^ ƒaá÷ÿdµ*Ç|RÇÛ9_Âèw~x–ª{oÓèú¾ßhP[­Gy@ãÖ4²€’„`"0‚'¡)›ù' #<»<-¸Ýiå9›¤#<¦ß×(hCöI³g°Þ±«Hˆ ÐÆ&‡eÍe—9Í`>™æ*--bÁÂ(4#)D œCkA£øøíÜÞD‹‘ÁÍàQ÷z$Јh#°ÈCP‡ã ÏãùÿätCoÚþn¾ò ŠdW«±MÆôüY+c ³³Ÿ¨‰s•,Þ)G´|0Æb‡Ç$î#;3P}¼›f*ÂB‰ú’®ç&ƒºMוöÒ!þ†FÙƒ'\¡ú5’è^à …°ñ\9™fèSM&Æ¡L AÄRG-…4f¦õ336"ÓaÈbú{…ô÷•õ†¦±ØOd ,<¥0*Ï;?ãÖI<7a¬8ö¢ò –F=éלèõQ…Ï-LÞ°¶ŒË©†!#;RIÁAp3!ùŸ¯¯ò~Ü'Oä V;‡ýo¼Ð˜Å0F/AP¤þôýuZ? -]Òo±ÉÀÎfÌÕ°K©Š¡~Ë¥…ªNŸâ¸1MâŒ+c¦1¦%°¡Êh±F#»;2Ç›ÄãœÿŸ¦q¶µÆÙ˜Ñ²!ö£brûÆ?8“½&mlçIÞgïÎÖ\îé8í,¦t%u‘µš5šzÔƒL³LmS¬#;5$ K¬ÇYfW)H†‹&íuä’IšíˆÛZRiSWzš’ÒÖÛMæk3VZõ˜ôÊÌ #¯u uë:ï½tò䈋@üïõÚ6hD:vÔ²]êNb(¤&}o!ùwéŠÏØ~ËôþÓf—4>°¼ÛæZú- £#<5Ž>¢Ù¡UÞ¬³X0IA#)ä)l¢“húA“$éÿžÜkÉ¡d­î.I&؆‡…\²’šÎÝ+ß3/al(R'Z{γÕMølrÊ{ß#â™1ßsÕ‘Ý~íŽÈ¾&e`öK—¥ðÉMÂzC7÷¬Y±ÀÃÖùÑ¢ÉÏu)s»W~ôŒ #ç¶þʬG³í)´ü¦¿V€Q÷Ñe½ç©±ôë‘[‡ÔÆÇ?ËùÍ2q»Â"zü|ZDWñNTþð°³³¶‚Kì_º}Ûr?ìjoÛž ^¦žQ~@2þ£Ìü5è©ïü‰"Q²2Bí26)š®®leºÓ[ˆ[5ß[Ýš¥• Å#)Á‰JÉ,éJ‰‘û{î/¨á³­®Å¥xmèmÇ’ú"s,#)¾i>¢¾x~!åùþgÈ~ºÜ‡I3‰œ¥ª~'¨{ç„WÒ@,@KFB@å1Ú·¤´?Á´dÔ¥#;è ºÈHFᯇ_9ì èºRuìÀ.çavñÃì¸ò@£P]Oèˆ`GñW~ͼðËñfXs"4#)#)A#)ïnÎðY8Ü}ŽàJ…þ[¸ÆïÞ=¸ä ÓÝßû2¹¾)>Šû­R}ؼZø$·¹ñK>ÊZ"ˆ—ÞnDõ–; ü‹…ž‚zàúÿýüƒkŸÊO™Ç@9ýKɳïe‡OŸbnà ¤! þc¬!ö4<¿k™Ï{æ8‡á>0ì„XÆœ€ñïý¯ØwÀ†Ú§8»XÀþ¸~;Ý»|Sòœ:ˆ{U;ˆ©A7G€Ÿ.å@&&iF£øÃÍ`Š@=\:ºë¿ÉàyÓ½£#)Øô³N{‚VÀÎ<@éê}=…KZ´åØânP‡ {{RPy‹€’!FB>pšzÂÎÕë «âªÓ"€M¾}—Õëñ­{ܨƒc]#Cë#;ðÏ«=O¸ô~_m¡ïÚUöKÉ-)ª3P,Ä$=øŒ’I>t Ч¸~gà¡¿ùvUŠÈGD,#;#)Y »Óúüé“ïíQ;cìôz¸7¼»HZ|ž¿ÂQßb£{”×ÏGHÔ[†¶¶2Ô÷ùñxÒƒxsFÝ*Bäóè'œU½?‡»‚66‹‘Jå_Œ÷Ô•€ÀlŸ`iÁñÈ,BumcâU~¨Ï©Œ’~*#)¡ƒÁw‡U„>0;3MÎ,'Ì©¨µ}w‰ËóÜÌŠ:å Y4Ô½BðyiH}¦J²!z«Q¥GÊJ8: €Õ>Ô2¦s$q±zòp‰`±'¦_ë¥Dã³±­B‡”úýl†b'š¨ŒdEQ*BÎ:½yóvÚhÌsx’-ÈÛG¢-Ž %Ϭ® ÞúøúÓoW72)˜E^#í<¶Lñ†DY>~°¡$´Z•{9>Pë;Î#;¬BĤJ(~ÞgQvÆ#;W#< ±FÊ¥`×=½åN½ä{ 3D>žÏS¡ëDóÔ¾c@ús{;C¸„G#¿Ÿ)ØH+æAØn5Ú}0òûÒÛa 9”ÚªQ{ž`=ÿKûG›ûßÚ…¾ýâY¯Æ:Þwä_ØÑ…ç\ Ûߢ±MåT"JýkÿUPê0Œš“ú!QGþj¤¡_ùÜØr ¶ðÒˆ80UÛ@¾¹#q›RÕ DÙfé¯ÛßáæžÓÍ'”õZØ;¢ŽÄq¡–4(‹ø3Uí|O½žußÕð’^¨kl1W0QþTÏàð^&ÇÀˆmøÊ>#)Pä±÷¤'`'.'¿ò¾Ç‘Àðc*”T¦-O¥O$ä~ 2ûòEÌ"†ûÜ5ª Óæ(8Ê;)˜]Ós=S²c“- ô¹=®¼ïÈBÈÀ§Í°ßÊpëæ8>!cÞ\°X¥y…#<…£‡ÐEþ#3ê• kE´,ó{–P;:‚U ?«ís:':(>I4—ýN~‡óNYõ@'½ž«0Âæ~(6R?¶kxXj—råxoQoZQ‰qûˆÄ©œËe#) $ˆuT-Á#;CÌÕ H#` 0Ð{è J.Í‹t„„ƒ%Âì"^‚Ê•;¢. ’R#)Â#<x”U Ds]Š€fzƒõdb*Àƒ¢j65åú$žURzŸh’\˜vó w #<$#<0€âçzŽsÉýJÑU5îÀ…Šˆ ‘9#)ÚÂ’E;¯¹#;»S¨?4Jwí7š©¡´(úö–%‚ƒ¼`ùBPX‘F5û?›õŸ?9T·°ýV^®z¯¾B#;ûß’VûTÎ xò¨CV¯• abÀû,ã± A‚Œcâ¸*"lX²Je4i¼ú|õ[ä±¹£Ùª+›¤kmÙ#;H4& ¬¶j»j.âPHÙAl)@,˜2Ÿigd;ÃÙöû¾í%þ 9'éYøú§¸=Añ}¼ ‚PPp_EÐ4ùÙƒ!~$€K^“ç sOw¥èŒ5v¤r#;#)°÷;MÏáÐÔ 7i7Ô#;ÐMz ™U,Œb2#…#)8Å34”QÙÒ,‘¬iÀä éG%,-f'™KÉè”mxóvðHIG£.£çne=Bñ!oãE¥¥æ;†6hÝÁ;7§éžÚ)'*ãÜ`fcª‡¤@#;§‡ŽÂ=#)¾ÝÅ»¢¼y´:µ×aö|Ÿ;ï¿Ï|MI}°“‘ u%X—²R~£²@Ÿ¥ÏvJ7=¿šÏ¸<0H~!Hú~;°ÿ {x³{[eûsÓÎ8ô¹Š"uŸ’°RC3½=¹{¼\ÛÏq@ é©%xº^78§×¹øÝ>èþåFe-¶m÷‡Sîõ6±ñ©Pó¿6ÏåDÙø‹B­-<0Ó3òÈkp ó9Z"*ßÏ™d•d’Gçé`ü¸Éœ;ö[xZÀ51 ÐfT´}sW²¾=êû;?É ©q¥<‚ÈF¸~‰Y7™Œ2EŒT–x3Ô†KhªTÁS³³KÈ´††§(Cì1—ûÇì‚ÿukõOö#<;×çý Õ]-ì~ Ÿ‰þ§´Ÿ¢×Õk¯d?Žø^ø©Ù™¹þ¤?½ºÐMþ³ŸñQáñÈv.ß¿¥D¥_N^r ÿ:iÊo`Ü’S—Ô}f.w'«ÏQDAøª´UÌc8Pd',)ÊŽëøl È¿P¿JŠ ìÁ_0bŠÆ#<„ÒŠX~L)5 íNÎdåêVµ¯‹¶R@„H?¥_Ò‘Ø@ƒó8ì‰óœB¼;ß½-ÏíR@tè«H¥,A ‹€ƒŠÎð»cÇCy°.=GP†iæ@4ô~cºæß¶2Óø¡’tËÀÝÅp#)ð3â¯x~W¨2 ³Š÷|?“ÅTÉP#;6"nvžžãžjv&o³~£Ø§Xu‡`qz¢s“–ù D,*Ü?mp\ˆšWcÄ=6){.o°®dáÄ <@ò=%ÔEaÞd`ÐË(7ø}‡Ó O—Ëé<òý\ð*ˆA)-m5ÛÆ'ÑæHÊJ=È-OŸÅGóÀzG®ec½žáîs‘Á;1“>ST|£ Ð’J£ÃšžMV £âÀxæîú£Èò9c+=^Ït=Þàj¢Ä„øþ_#;ßîúïCÊ $é=;Ã1`üJN(½¤{ŒÈCãÄñG¢vƒÉ„㤤#;Šñ{~ÿ7i&Ûfâ#GT†$M¦¶(ÄK×^¹-º¨bžZØ·ýTv_ß{Ÿäæs¦qàÉ-«:ëT9TªMäÙQ\§%é,èÔ±2íaH5‡Ë¸[mŒL²»lëPåÚ¿éö!Ê'Þ›;‰ÇŸ—~óŒŸ#;ÿbuU*ò-]‰ø6pK)ÑäÕ˜åêññ›Méw<Å ¯ÎPz‹¯n]¨±Yv¦3íÓ…¤R¼Öâ¡=´ÿ—ÈË꜀Ü~,¶¡ËxcÌ30¹çm¼í3Gg³¼ÞÀv*Ðþ2*nSÉ:ÎMÆ‘‰è@æA«ÀFÑ,1@)HG+×eº(Opz^‚>\ÉÖ…ž ðɳƒ‚UdUQ-"ÙÈ8;û8œxΆӴ،"î)…„zÏâ6ãmPmÀRyÀ_T(Îy³¯#<™Cü;Ï‹æ5(n:XxzÆT<&…sðF·O LSà€¾œL:œLøE#)úìáðí=Ï“õkýLÏ@§ï#),"=#)ó÷WѽŸÜ’h[wR•ézs꜀ƒŒW©‚M >1È4’‘Ä0Qèlï„qA†¾×êàÏz¿3_œ*~Z•ÙEç-¬ h¿Î“\“ù¹úŒ§?CõÐs9ªÞ$Î/ùWûðlìë+ìãXÎ#)sáöª»gð÷ãö^÷¦¼v#;Ú]¿ÌÃ2sœøSŠš ’"7dizT@@$Æè#;°æa¯D€¨ý¡Ì³¿p…‚ƒLH4‡Ó€-ØÇ‹ƒAY تœKâ¿ÀÈ«ÜáÔÌèûÐÞ‚Çï‚ÈgÊ#<„‹,ˆÿs°ðÐ.¼ƒpóN§Ÿze0D†@X:ÄÉ A^á"©`Ùç:Î.Λ3h*;¾ŸÏµ¸úô'©vs«žR‰Æ-Ž…yîK8'RªdÏ¡‘?2>¢¼ÛŠØèwä]kê¦ì¡89˜#Œ6v·ò#ªÞ´ö|ß/³|ûuûPGS÷ÕðùHa‘@#<¼ "±RÚ!Šn´V¢‚H}O½èÞ!“ùZÛ\N"ŒGòó¯Ã/%d#çg#2„#;ÁõFM«ôø|~³ó=DÌ÷ïù%܃ùý”yiȤQyÄ*##)t¹ï]áhDƒ,#<ñÝ}ðzVaµãù±‚\ýûzƒ©:Gâ ¡æý¢~,‰Í°Wà‡Ø£ûEËéÉ}#)‰í”;Ü¨Ø 2ñ™‚Wöõ¡ìêŽp„FÂÐALDho\ë¦e²yJwžm»ayx•`”Ã#<–È}âD%‚/ÆêyI˜àÓmLT"ÊV Õƒ¤‘‰Ž§\jÅLÆ&ÔÿÒÎÃ5?¾©K“ý9ãÄ4)Fùf^ZBèK’ÉyùÃtݰb M’û¯C¥e±a¤Å¨w…¸¦ÅF¢FË&ù@ãmœK&‰h^¹à ˆF4nIò2Ñqckhïèw07 ¸Æ¯4RJs‹1ý©™­ ljC‰#YW¹a·Ò|¬úh?ÚþåÐ( j€paÞ†¦Kd -¨|94†S8‚Ôd"H}úÌ^êÂZ«ÏôáÙº?wu?.²Çãô_4rý4\Xß¾}¯¶¨X|~¦oŒÀVò”M3úØ.ý–ï;KÜ Ðzä5–¥ÚP3a€èíh¢ñº8v‘8mدBü„ƒ€Ö.òúÕQN›î .j7—ûÆ&ŒÝ,“+æ!IŒx~ê÷+ ›ðN»ªO#³†ÇŸìƒ_/«Ž9Ÿ?Ú2/£c¸¿Ù¼ÚÈ$ÞñfØ- ÙþÜg†§C?±¶+««ÛÙÎ!ä4™ñžÒHɆz%\ØõLE3g;fÉ+Ã’ c.l†3fgÎFdRýÇX6³0äx𥓓²†÷Ó‘Kz¨£a²̃"Æ£ùJÌW“‚ñ~Oƒ=þÕçaa"±e6Ä=Ü•{¹e#;£ôH?ƃ~xïù»Y»ð˜ÿ–»è{&­Ö¶ª`m¬¢wµG#<(ø¼ðòÅðè¬é`éÙxUâ_ªÿëþ÷#é—¥R[76Ÿ’”.Ä8¤CÕ•!ú x ýßmº¾ÎöÈ’åý\L¡Còr#;dh ªŽ€P ÃÖÈ4¹®<ŽȾ ÑÎÈ«ÐúîÔS†Nósök÷o4ä®è+Q0)ÌH²ÿ`v.Í·Ó©—Œâ÷kŒ Q6]®e {=»;Ÿ‚câÄn5».²öIâ#)I%&Vv'nðO“¶{>Úf1ä'a!мŸós¬ mÊ#)#;2Á@Ã7]Wý2QýúãÞý&—#;ñ·U˜iç¸dñH}. ðd5#<‚çUpsçàz#;4žƒå.Aú]·ÙÙðµêípÐeæ÷‚À<ÎÓ#<"¡Â;Ä·º?2ü”køÌN°ž2Q1K”MßIN'×ï_7 ¶#<æÊàÖ Ä #;¬¡ù_G#;SPÁíóH4•ïT9TéX_5‚MÏVRoïO_ó¯N›-HL]•¬Åi=¹­üp¤=9rïÍ?EœHè9Èâ,;¶ø |7¿N½¬¢ (+„›b«ºK¶â!ñÉðʧÆB‹× /œ\³wtž^Pu¿#<´¨”Xª¤Iµ;©ûõ²’•ÿÉ霿}^i^o?®îs®ŸP¨ÏY³0õ÷\(ëNɲº{)òYçÇÊN’8á¹ïNï8ÆîSG$,Å£?tïèí„Zsô[m;_^ì>Ü!ao\6ZÁÈ Rž2Òˆ0^+kñQ­ËásœMïœ þG)‡=>.}ž¡ø¶†àšëÿZ;K€ùò#÷‹‰²4®”Ôé¤_¢¿NGõMð·‰â)aË?@#!ù‘ø#;Àkäª G 3iç•61"4{Cº MBR#;é¥ÛÆbžðvöûn°,a·˜ß•R>r5¨qí~´:%r/‚2{ݯ'Q¶H L…ð¿wŽo†Ãph]{ ɘLƒd6fª¯søHwöó³k˜ìD~¯ñÈëd´@›Ï3ð ¨pôõuËT!)‰DI5Êçi±RR\¤yXÉœCù{YMÖ\(Îc9vóDeðšÚn‚bØGÉÂŽþÌùÎrè]d ¬A"«#<}Ó‚mݳ)+ÀãIc.$¨M‘AæaI#;üñ†„Œ•H°­ÈÂê²G“˜F ½yùæ*à›õÎcbÖ“4Ät¸S#;PáÉ 9‹‘€Å.¿›¾å~œè™ î,¢ã=3Ú ,Yû'Éø”à›ŸÓŧôþå–6€ABPÉñö Á×ÿqoG „Òóûñ¦–#< }QìÁý‡¯°å®›!°æŽËB~ ~ƒ>•iü°Üô›è~³_Lá£@5eLËû~¾€þ9sø~ñâ“üÌúFâå<é¨iëKÁËüØÈ·®ªªÃþCØÀº˜0͘ÃjÜP6mÎ$‡ç °w?Ä#;·±Ãh·Ì?`QvæãDi¤$¤HÅŒÌñOñ\¡‹Ð<¶yM𣇉¦¶*ã'‹ƒs¡Ó6`p¼Ä#;#<#;öêínÜx‡ƒ´;TÁÛ¡t4ŽÀ2-ï1¬žDâ'ù2!Ó…r*Н?­8dNž/ùÎmÃÈÔHO!åÖvq ÚÙ•øx^ƒ¥ëYRRï”6UE…JIX|ÆÌÄ–1ù£_ë4ÙDb •JÕR¨ÕØvI¨u;27»ÖiöàÔ ¡ÏyË­†¸ÁØð„¡:¸ûý‰_ßo#<þT,äd‚Å7‚¹@1B¬é9qôÅX‡Ÿè=!¡ :´5ÛE¾2ŠeµŒ4ž>…4À¸=Ÿ¾">ýÈcíߣ ø,Mj÷ÄÝi£#;¾ÂjjÍÆÌ¦õRÐÖsÌî$à Æ#;@ cXb ‰¡ŒD€¸RAšZ,#;7ØQÿ› èü¶\¾:ö°ƒ˜gHA˜ŠÀ”fXWyà.§¢Tì—ÏÅw3xòp™~–Jhw¤’tŽÜFrÜ mÏ‹yð''!;ÁB¥B}[}^Mš>²ByøyÉØ¹}Þþ«Ý-Éû!{8jˆÔ8¾hýníõÔYž‹;7¨0#)iĪAG^waúô$‡£K†ˆUQ[4DeˆHÒ‚Ì‘1Œ#mÄ,„KYX»-„%PÐȰAÂ]Âqð¢ÙýÅþ…#€D’F@’­+ÆÜ¦F<¯ë¿•ýïµ·´Ô­)c2ÈÍljÆ¿ƒ®ïygu×rÛ›j~מO#®¶vn#uÕö%¨´ªU?õ“àŒE¬]·f „#;M²ýÊøÏÂ#){ü?3á^ûmLÅéœ0dâöø5€I¶¯÷ˆÂAâ¤:z³º¶•±Ùåàr·ùæÛ¦Flj¡ÄCåâ>>'5ЪŠÄDv½]ãáüSË‘“Û†Al†‹K¡c`ýÆ#<"†®E#;MRhHØÛ›Xh22}€B‹ºiñø¯•YmFÁ{ ÈzÍJF«ÿ ]аcòdgÒÔ¹^ópÀp´‰X9¬4H`ÁpÞRíXã¿ÅN凔d^ C3¤“‘ú|ÛÍá5*¨¤²¢àCcdøÑN '§#<†Ç`rY7,žoO<óÀÌ㢲<)#¦0­äv};aÚN½\ ò9¶îf+©…‘mK0›|^n‰#r×S’Ùf­»¤x¯LÃP›èqÙED£àq¢Xƒ‰xm&µ«ŽN#;÷ØNî(x°¢O5;—nú#;ª¼—°;>§©´ŽæqÁð5#<Óa2&ÈÿÁ¢ͺädÊ#'±t#@€ @9r!µjØÅæÍyêœë·}wÙ«0ÌÀ°¡…ìɬ ñ3 L3 -Â’!Ýbsàå50t7q—n$Ûc+¹[ie™Ž¯ͪ+‘ì«u#Q#Æž&Ø·²üàÄ%ÁfÌ8IÞþÔ~µ“É‹ÛÙ„wF {`±QÜíȸ#Ãv0HpÐ9ÛigE¹n¸QÆ™åZÝNÐîï:sè„dÙjJqU„ÝF#»o¾­ÊôÙÄ)É€:1;_Œ6)Dälé™8”{´±¸Ç`–¢ÂÜ¿fòE¢øÅSc«vnKjøÈI ñGÈ«¦†i8XÀð÷™3ı‰I±±cÃ×ÀkŽTSj‚Î}ÝRή˜lÇ¡¬\Ý(ˆ¤¦É4ôçRõÓ°…Q”qè«7t¸z¦í8Î8Â'¨ÁA-ª‡×ÐŒÁÈÌóî ±‰W讲9l£Ó^zÖß+ÕkšÑÉ¢(W¨¼e͈pwöA$C~ÇF¢mÒNLæt”]8í5Ls*5¦åfÑ C´ƒßuÜÜn0îx)vMQ‘¨…J¨€ÈÂ`´¤’rùµÞ¾»¯Ã}f1Û–9ÊZ:ä@&à+Ë×­LÁ(ˆv…37fdË™™™clƲ¬Ë3$•c¶gš;•ócŠ®{óÖ#<ä…VÔTÒW)EB  ²A`€Ã¶©EŠ…¼;ÂñvT£1.ýo»Ô<)³·°¨†É㣣-ÍÎ *–éå[‘¾÷g4Î$!3¸í¶î #<ëg¿‰­nª¢³;R(3—›Ò.™\Ï‚uºyÆ% Äˆ2OZ–eÇU‚ȘB¢’l³ÎÄj©¶D˜ bvB>g3™1¡“¦èÝ`E,ó#<Û-%l;Ú: Çðó7 ¡˜šmtld Å©#ÜÅ ¬eÜ ‘GÃn×µ1æ°§!”æòk Vm;¤¸'hÚ#;‘·2.8}o§u¬Ã¨r×Yîg­TU²&XÝx==a¬šrHrÚ0²ÂT µh¨™ÿ\ Hs›w"¢­j#)¼ËóŽÁ š©Í€WiÝå·2uQòï¸NuˆȇÑ/ð7*õêZJ#;­ã„pu–áЪ†#"#;aÀاšsK h†hRSàú9ú¥ô“f’n­„©w”#bÓ¾ræÈêŒddö&2––mI ‰,ù RÓ¤–.a;Ìuñ= )ãbr¬[ÖhήšHóÔÃàé+›ö…¹Œ§§#g‘`ÍšE£ƒ¶nó.¢¡ŒöÓ_MBÎi$—Lj®‰øœR½sôµ)³ŒÏ²Q…ð»ÒÌ8dv™Ä¸<«–¶¹D8²µÀxC\e#<ˆÑÚ¢up^ÀEîhP`K4lÊS‡È'‚ûÎImòsv#Y~m¦QÖv;>Z±ˆ„)mßyâ˜v#;Þ&¹í«Ë¸]ì-0æÞ%ZBF 's)¥0£ZŽ¥Á"KË#ª¢^Á:YC£4õN;@ÕD†¸â“»ãUåÐûŽ ‡nÀÀ#‰À“"RÉchšlÕ3›Þß#<øˆ]äYQÐt2¬Ôz:Ž€¾gˆmÕ\Êh~ïT–ì»55é '[Øsí{ŒèîøP;·´Å:dc2ŸwÛPŽàòKä0i[Éàæx‰sÐ0óuC¨ %$ØÉLšÃ|ñ)h.9í è°5¿„6$“áUžâc)#Ùzô]yûö”’£Á¨¿9íb$R0§w€w„¼¾>7,QîƒW5l ¨t›V¦„›D§_Ì·£„¡ÅS¨ùkW.µ&oHcHhbHÀù!œx09+80|}Ã`–ìc¬Sd›¥•d’\¦‘ö é:  Xžx†Ëì6PÐü˜ÉÃ#ð0Lö[Ÿú¼Ÿ¸0•>¶¾öý¯Íû i¤“2–Ëi™¶L±#4E)”iîê$ÍüfÝjTÊdQ«6ûu7ÚùûÓ #<}œútw È: 5MQ#)ªÖ^L  ÁD}Ÿ#<¹!B;—}×T–¨»%ª¨¯÷š4 F+81áß¹à"Qï‰$$$¥¯p¸Îô4z,ƒûÏÈ'øä€]ªAB#;A@)Q$ÛJ ™½$9¤_.G;#)ã¥âv¾l”ÿŽ£”#;mU~yÔ<*•bĪ(zJ¦‹»ß#( lüd¿¬R0V0Ú6–jŠyï¤/õR! º€ìåòݬjØ¢)¨Šˆ)I¥ˆ¬E3&šY–¯×ëõ$T’_Ž»oêóU`„ä~Ìà©_ˆª0@X‘# )€ÙT#;¨ðR(ÑC˜w3<â ï¤,¸¦`D„P¨)À6iÆ—µNÙûéUªiÌÝôÅÇéãÍòA‡C[n¼:J._6¸ÌîÖ:$ÃÓ–vííµð»Æ5é¶#%¯W­È0JÕaœAHDGqU£?ÎÙrFã·‡|ò$=«fgd<÷úl?Œ:aÉ$“‰Ï ¢ùû0V˜÷•åŠ  d/ó°bQam~à¹%Šà”G¨RÃC°%‘ÅO¶fÊŸ"#)›~ªÙPµ‰@Ž ¤¶zka#;AŒ+ëŠVçÝ¥x„±º‹àðíÓÈîÇb‡`}zŽ]¾­=!#)#)ݰsbá#)|БUBBEuåß0ö€0"»}JÆÕËERÒY´©&Öö.CÀŽCÊ ?Þ€*Š#)Ucm«(&Ú±¶ÔŠšÚ#< b±‰Žúd(zº$žÎÏdª¢ƒXDáõ<Ñ“D럌–#;¥ñ…em®lNµƒXáLâ#"&A`b!ó–*ØWÊö¯miêúM$ŸX¦ns5é‹Ò½)$‹z»™^=*ÜÞ¥½MÍv&ÅÝÚ /.®UËwìyæ¯9&9ÒwW7K%eqPDÍДJìl”#Z1l$Rˆ #3*?é¤1Ü#<fƒ¸¡X1¼­ß×ß96A M[µ·Ù·àüR/!0‚zfH\4ùJfX²ëذí`ª‚±íkæÉíÂüÿDþ¬¿i\š¢™&Úp•!äÊKª,ißêÆÃ ÙÜyž8ôsñé¯{LX1-¨\$|K|Ï,Ü/£ó]2¶ ·¡ôvËö÷lõ½Ç·-ÛŽ‡cÁàC䪒ÊéY7¾+]Þ¯ùp›7e6½\¸Cí„$¸¨Ks¡<8ÚHK=^-¬?Hjè!é€#)H€È!3{‡½‡N³îBˆ‰ïÉÌéöéQ–¡Z§¨.f&ä-]ÝPÀãÛ!rdý…÷¶É¢¨Ô+¨T/j½þIããTž£§[!™4Øhlb9Ôœþ¾š¸#1w§³¡ ÖÝ¥ú¾Û–]õC¡×h•SIÜ ûÏÇ~¾¾ÂˆBUJ©E'.ÿ¤8rN'™¢Š G`®ÔöPl¥ðé=B|YF€¨"|Ìb6*#;Uó¬¼Ï£l[Y¢«êTçÑe+è(z£`/TI ‚ÄÃÀòȾ#<#Ž xàöJ:˜YƒsÕ¶–ÄFØ›ØÝ•~XÉ#;âQ«ì£©FΖl9EG‡£†\ÞZ|Œ—àî®;™´ÒnË\­?‰ÛƼóv…à$¶ð|ÌÑ„ˆ”DÉ2Ž5ˆFšÅZ6Û%j6ÉÍXÒM”IQ’±Yi¶¬M*’ÒM±h­3U‘•¦eV•µ5[3Tž£ŠIÆ}3•¾™Rs†ô#;¢'™E€"H" t¯JŸ!ö¬7õŒ(GG¨¡(#<Xa8ˆv9l}ÝÃ÷Þü·î¼[ªšhç-!‘‡›þH®G9¡a=ÃÖAå^ƒÌ'ÀñS͆"²½_@}!ÒÓÙ!ð?üŸÉöÿ'çõÆþݾØ!ó@Pó±’,=sŸ*-EЧµˆH®¿µE¯A 7LUIiT—d0“½¡$–¨Of´Aƒ>—U ”P@¨„D}UU ˜ðy™à|w»ÙM¢³‘£:Ga)…H6Ô6Ïöb¢Þ. ~“ôƒ»ç»rÖ!y^Ì]¹ʱ®´/@úÙöeÐ'º™ܨWõ{ÝÒ€….k1ø¼– ÛÓÖj ÷Œ·õRuuÎýçö‡HÜëW¢„^ #;X!“M"Aœ¾˜`EH;yàò8fè'’v5ÚB úï / 5ó5éeÉN#<~|[F•ã\”ÑM¿“à쉺šIˆ«ùœ³Ëƒ¤å0êBƒ!&#ª1SHþËèåÛ÷õ;Ï«ÀîøÑä°^<ÊŒ¨Ñ 'y;Rƒ>Óf+gc÷çA”?rŠÙ„kg¥Øøy óð15ã–|@yQÚëævopt}Ô<íŠ>‹¶§󸫔Jœ¥çt×Zé¨ù}9L’ª„šÁâLÃ, R0úžOiôÕÖžrå#ôO(hmß‹\ý+ÃN$îyÈlxé˜òßê^Œe×þ›†÷(?ëÅÛÛØUÐN¸#«Èˆd4BÌçT)D ÛsL޲5ül ÞAœ~WxÖúô›ãbr!hêူo#;±¦,ªjçZÉeÏŠÿ »³YÖqvC$3èCƒe&S˜€J#<‰.ššíXRkaP„¯ÄF(’ˆ°ªzÎû%ª¬ XùƒÍG Cà#)é¢SX€m;÷œ-:è{'j”p$—çû}úi‚%Ïé?«Ó”4¤(5e5TTQ¨ˆ,ΟWiéËœR#cðmÝÛw(Q²X}ÿÓþ&é½´#ÊŽÌcBûa»ñ³½Àãh¬–ÈÕ#Ùò1ˆpÏæåÏL¯uyØ¢ˆÌ}=Õ4ß2j°šL•’“‰T–(î`ŽF;öƒE(‰ÙIܰŒ‹¤\Ɉ9ƒ.ÆÁòéí¯"@ÇRùYHƒÃkšÜã¢-ºïx=Šd”1BåCÂØo´-¬Ø¡ôA`eëÇ+î€#%KMà‚åɇ½ùq%ò)­ô8+Â#<‰íÐ벺:& &ÄÉ¡MìÕ‚ÌNPWQÙ6‘x( Òð#¡sÅC² .!¬1|⌕âË –]ƒð7eeí„í´Ø§qF;jÌt,½ùQ»PÜ-ôT&ʲŽÜ>Ùã¤r¶§œÝ6öf‡£•ÊŒ#;†ëÒú‹¯nxõÆT†žqÌ=p&ò–ÁÁ}Üëg^M³µ\9pß‚½ÁNÍŒ¿L[›wϼZ¨ÄÓ<€X>8>¨YJ:.çŽ[ZÌ5©Þ‹`Ñ0¤õ39Øä]ÍúÌDy^Þ­š›¥ÔTø9S #<<Ë> Nl:ôÞ}88a®ÄÈn§”Éà7صOfN×ݨ©f…#< ã…ê6Ò:K‡¸à‚¾ÇÜÅÇöˆ£=Á ý·7o|†òTÒX€G)²oÙÙzðG¥ÉßâoC]øjqÉùã DɈ”zßo›#;ûNè)’ÏŸ™Ô9WÈÎû|#;suô‚êÎìJ,ô¨r•¡Õ!Ö¦i©©GíðôdG‰®¤‰8¡¾Ý/„•?R:q1æ+ ÎKŸ-teV ., ´ÚrdÍêÛ¸BˆB#lŸW^«Óu@‚ù\ :ý™5Ù½@›Ø>?#ÔF@â#)OGKaÍD£î­4¶šÛ'[rŠY²k]76S;«©³r.MA„§Ŷ†l¢”]B(£F¬AFRŽ6"±Àq0PAi)¤‹©Ç±»‘Âä6E’Ƥí´dá,˜EAÄ„"±AÌ£zª÷_ ½ï«F¤jk%3 )"$`»¬CôCµNÈ<¹naHÉ#;%‹8ôÐU9¸€‡Eë*n˜Ä) hpOBQåºío:.´íÖì²_ƒË¼d´0¹Í‰µ~ ®Þ#;„SÙHÊ› @*Hˆ ’iݰÉ@¨E7ÏÈ\‡ÆÁí{“ÁÕ]"¿„‘EMÚ,/¶¬VQƒh­%ié0÷†ôSÔbÛ§¤.?7XÎkE1pT(Tæ…1x•ï£óž$Ñ¥Ë(×SÙúg\dF×q±R4ËQ÷eæåm3°T—ˆ[“CïÓnÞÇŠB½ê­4,µâÔ6íS´ ¹¢§Ù²´À#\{ÞYu•^8v$§sz(ŸÍ×oŸ¦Ì†‰µ‹ˆD‹­1£ ãQøxÁ¦rü%Žß#<¬‘®Y²©¾]Qk7Ú[¾³'Ÿ!¡0Ãä%q¢ÿC°¼7kÄßn5³4@pœÕåhP*àvëÉVˆ°àQ]ø> Ô3§®øZñ8Âî…$¨)º–Rç‰Ãl<ëEpR÷ó9w.pŽ<“Èèâ‡ãÊ5ÑF/Õ±™š¤n/RôîÊ8Ÿ¿[ÚIƒÝ®øfi‘̬ uƒËÆ/y$’' =;ËÈÝÚü½Lð÷óÕ.¨#wvA"Öá°„èˆl¢íÄ$ Í#;†¼õ*Ú­FÃ`Ü ¯«J@È"‘„ÉØh]¼2 H#²‘¸pK5Qz 5ÕZt%Î^E†A‘ K dN/ñs{²»¾f" 3Á#;@˜Jë?¸Ø G);TV¢ @+^ÂÚ()¨Ž6;˜q^{ƒm«Ñb‹AТ ªª•Tb,ê_¹„&$ê3æõÛ†ö#)!Üh©Ú‰[Žž«kÏÓŽ"—Ìy9bT²syEît Ô¥G ÞȨª«ø·š_7¸¼Âdˆ3àˆF‰Ì™(Å&l¦9ƒêÔÜ;ü?$DâRB1“{"±Æ †G uBlT*ꪤ’˜üj˪TŠH£YœD k #<ÇŠcE­ý…P†6–ft Bè7dd"E GÝ×^]]Rlh²›o:êI ÷ÚºL@˜Ÿ¦ÂÏ¥4T¢§nº»Ü Ù«*îNrHÜ ÆžwÓEˆÄc&ì…1l"GˆQÖ™î¿.¾Ò¥›ZÉDÈdDd@ã`œjF›=¸"°­.¤Âˆ).ª›#;©šÁ>ç‹4Em$L©% ˆ²w/{ùók7Ì z i²B6Æ‹ïúhÃ#)ˆž6˜ÿ(÷ÓÞMEÀpÊyxh©¾u|”Aeà“­Òß3èÄuŽ¢ŽßÒ`‚ “Ãîjªl(™àòÜÞ #)gåH°< ¦tüî¶2HnzœYb9è‹ÒÍsùPªdP’Äñ֎8r(‰V1(i#;Ó74ƒ.Û&Ë;ºV™‚L|³’Nm7Ä¥Nox–Ô„jƒÛÍï¿oosºÌ0ˆ't—+d(Â`¤j D[uК‘ wùP·Ä¥6“@ˆì‰Êa1N"³*×ÑZœͦí¢‚Q¸›ÄÈæj÷~ù‡t|g`oöëÔÙ:ËfÜmmÍz­\LÍy<ã«êÏ2#u4Å|¶ß–­=ÎúÁHÕíF¢r¶@¥IÓI¶úÉC¹O›M#<ÈMoY%øi¸ã>#;"çó«°Ï¥Qy¥I.bWœ"+–:DUsPÊSƒÉfeÅebÜæ³äì}þ¸ƒ~âêô )xx8„qrõAÕ'iºÞ#<¹T‹ˆ#)þä Ôè%àgìg [¿Y>‡æFÒU­}åd¥ÊdÉL³TÍ2e¦¤Ó‘TZfmIWîݦa6´!!ʉ¥L°SJh¢aEX’_B鯇[˜dÖ“IDÓ+fÑ“&)šf’2ch‘Q¶,¡Ø”/ŸuE4bÌ…&H%™=–®ˆLËE b²¢Š$™”Â(¥’в6˜¡5¦)ll†R‰I’™M@e*LÄ i ¦VH##;dó'*¦ósŒ0õè'ÊB¶œZmi[Æ?#;¸‘GX„R0ðe÷?ª®w"&"gÂw)¸§ÎMQñg„–vŒè÷‡ÓÏø…Z­6hÄ<æÕHçñ|!pÓ4ÓbÆšes-§§#ÑQÿu™Á”&½h•áRºW¾¹cš¥v¡J; ¥H[6Õa}l¶ÂsµÊqA[Ehæ]#)%O²aÍiÍä ÙlI5óXѪù5¾LZ‚]&P/”ÇMµ;r½æ2å¿R%ãyv|éÃÂi…”;RÜÓN⨼ٮéthc ³k¹žJÂjáøM*UB—Ü÷yÚœ·„‘"Ö£;I†£Ç\än2Ð<áßëʾUbÞ©ï¾ÉjÐ]‡SÔtAžŠ |4FbÛ>Ló'RÙ—#;vGQðÄ vÔGÑ”la) #›0L£-Ô‘P;Ä=o‘ÚåžO§®kaõuë²z·ñònÏ7@†ÚHCžÿœ¥[ìØìÝ^߀vvØú½ª¥‡ÐÝõ×+ßõV“­!RCÚM‰¹’ë´‡/~]ÏÔ$ª vÖ­ÀNµûg¹FwÚë#)篈+Ê’A@ª±ísœ¸jX™Æ7MÄM²$ò}mÑ‘¯ÖÕzkr1ÆÍVe¼šÓllɪ(=E6æ±ÂŒtÇ 2yu÷èl‡ËXÌž5ŽqFÝÕVeÓ¤Î2ÍLšzfd53lve9¤m3N>¡"‹-)1ŒÏÜœôœާ4³ZUœÙšš£Íd[´Å×[Ö"e‹ †ff"éëÃ$Õ{z#;WVL\J¯gO¤ñy{•j%8òÐÔΟªª™rq†#XéN,*œö? ®6Æm•/ÆaÓ`À?#;{˜˜²‘Y »UUÄq4î–!(µP¦¤‰Ò„R%Žf¥¤²Ô hIÓê·=1ˆÚÍ â‘BÔëŒëä4°ŠœA…¡cN8Ž'Hyz—ƒ;FŒ—y§†Óêe#mÃHÚÝÉ ˆa˜Ý@oµI#u#M`BeÒßOõ#;väÐÓº³óŠwyR9i×/x^ûÁøÆv¹¾*$Ú±5-ާS6ïʱQ˜E²2t%éÇ.Ã)2’<¸áäéëècŸè^xçˆ7¼Y8áE¹e1gMÜÆÙŒI³-tŽ/+1$VföÖùß;co¾Ùƒ5ÃæŸ;Þñ<#ÈÜ{—"£Oê-ómâ¹ÌôõßRŽ™Û2.›t55}h|âÑ-xïˆté'ÆD ÊÎ4µ#Ú˜ÕV{c‰ÎÙÂH‹K4ñ®ü=pue©FÑ #[ÎøÆk-‘KƒžwÆŸ˜Ì::EÚVBözŒ;ׂ†âŠÙ÷ŒînFdlB'­åm˜¢ÇÓô©Þ¶j‡¢‰#J”¿A™!1 ^MêÌaI§£^¦uÖÎr“Tc’Ç“cOÆ"|Ln˜·bóªbfÎË!¤FØûÄ€½%(kPÅMqdIkZ2q¨¢^÷¢ÀPÔ"aËDa†5«€ƒ1Šq/˜(ªa…À7DŠÔ•ÆåN%Ú -R]:$éÁUâñXNéT#*|^q6Ϋ¾<6ÌÎ0’ÉQÑù#•fë#H™L³‡¸Á¼ÞÛpqÂ6iÍs&û?/ìB½Þmåái”ÃÆ„ew˜k‡ð‡íÓcˆ<ŒYz$—‹vç#¶]1 ¦PUßKp::™œg%šZZk¼aubcœ&y¢1Vîg#<…‰IùÓIÎåÒ2€ƒ‚ ™®é¶“êU߬ìqÎMAt†bÂÅÑ&6’¡èX‚ÂëúMTà~$ãï£z7Á¬§¶I—N~Y.ÈAí-º¼Õ–î’A—xqı$µŒÌã4áµAúc´§ÅD©iNÛgcˆ‰Ø%ó&s0ùM ç74]žº´â±HšsvÞsÄ\n!ƒY#<ËÄÂ\dÄ$*„•Î7UN#æ ‚Á4<áÚ µ3 +w⊙„¡R“S3(d2S;ÓÕóŠU¶\·—#h •M‰y©€êç ¼KaÃDã¾–Í{®a*y±Ýiá§`Üœì7§dt3mîãš3+Z­æ3<$Þ¬´J# „aêO‰‰œ#<ÇåŽ+Ò#+SÍ5tì*Ä,ÁÞs:Ëã¥UWㇼ<ÌP¼¨~i÷Ø£•”‚ùทw2¸*#;ç>2è—lg(wœ•¶Ü&R4ÊÛƒ2Fm·§d´^Œ+ÓA´ÖÓì…ÐÍÙ%#ûkZçTmÖàٲ٬¼G¬tsI“l…tlgˆ1œb³mR=LÞj8¨OÇ”Qƒ¬53]OC7Â{ev=¸fW,|ê¼XËÍÖî¦+ªo%vJ'K£&¸Ù³í6ÙdrrÁViú©”S«I±ªWœÄ†ã§as¸ÌíŦÓB桺¤;ªÙ:§ ø¼³Vïa;;SatrËUW8ºE¬Wd7e߆ÌÞuò©HîßFkŒ<å²’kšZ¸RiœÔ›6ض€Á¦É^²”apà¥K&úò´jHß/:Z—«AÚ ¼ªPŽ"Œ9k¨¨“RÍ»Õ@Ò PÓE`Ž0¨å5ÎüVŽINôlÎ3œ$9\“r¾-Df4iáU¡4ÒB˜D¢æIŠBG{Ëb–è¡ULÓ­MCœªc³e×oÚ“ºmGZÞ7j»_v ‰y¡^¼²nNd€§ ·¨&0©597W¢ñ±7ÌûàÏy¼€nsÀI2¥œs ¶KÕÈðú`Л ¦á}[T(ÈyîÜ•§0°1‰'LIF#<èn˜ÕFEšÎM§5p&è+ò©#µ‚:°Æ-õ[鑊°oG ²ã"é#8×’È8†9‘!#;ØÊgV ææ‰!šÃ¡øŽ&WºÏC£ÏÓq·dŽ«[e¡ÀCÇõ'1$Ñ⑼IºÐ/:6ÉÑ&*¸°´2ìÀ±žpÍH£Ž¢ðbÆ•­qQÙ“^êˆÅÃK³9h#92rÐq¨Ú.§f±šnQ3Ɖ»8mu&ŽŽÑLPa†ŒˆÉ ÓJ$Àá%%¸G{µ"á˜Ú´KuM,IQtfF#;P‚W#<›¹L3ÖÆ+d2Ú¤D2†™h˜B9â1AÁ#Œ²2pj'©˜-ˆH Åî˜Û;§¢,&ÎR ¢Ez¬<†o\{TxŸïÅÁØ| h\ô:’ràr„ަ•QŒÐZC )#;RP¦C €3#<(ÆÁÑÄ:Å™R9\u<#<¨ð Ð]•4‚¼ëV*3ZMu"Äm=©Cüç@zS”¶%ÔäðGhAÅš²‘eQL")¬œyÞßnv(mÜqŠÍÊÌÕƒzŽÿJ¤c5'6“#J#;¨Àë}u ã©*W:ÚuÒà,æ Ã¨ho%AÖ˜+RÌóX4A¢£7Ôb†Ö6©±Bª—Ê[#)£2ÍWI$ÎÃ¥Šé¡¹mI©Ç|H¡a,@ÆÆDÉPÎÐY#;VS^hÓl #¼‡³YŽp%%o% 8›±Å“¤DC–rL@l™lŽÐÍ‘§)c*ÆlÏ-Éaƒs1a±Š2:b7BciRŠn”ÑHt4hAkDÈ ´Þls£=9ud˜#ÄÃHf7t‡$2fŒã%Æm#;ãÄ °È‚â[кa8&Eˆj4f­ ލh—ÄÈ{&œf8ÛANA2‚ضk;é#;]©œ$Ó"F7.….‡\‰‘°h³—CQÈ46ä&NÅÁ““ &Ĥt#)3³¹´Ì#<68j»Àß^¥VÀI¬ÝQ™¦k®¥3#võˆf[=™Ùs#6{ï^ZóJf–“M¼—lÑ­¸¤’(RânuVƒ£±™&t9~ÌÌ L0‘oJV"´r˜Š©M•˜§1D%ÁŒfØŒ 0#)åùÇ´é’™À'‚j£Ñ±à~#;h‹ü¤‘Ú˜ñ¯§›ä.X¼¸h"ÀõÃ(ÞIc"hªƒ½÷žê¿aWákjm 4–¨¶,jCkEX“F¢&ŒÓ2Ѩ5ØØ¯£ø_/>¯§kF®Ê }ÊðÄO’\øS/FêØ£â¥w©Õ»¿Ì¹&A’ô.ŽÃÇé¥Gi¯ŠÏFJ‹¥'äÅXÄfÕÅ \ETAÑ”3KQtÊŠ‚AFÁµQÄ0¥8€"A¸#¿¸œ(Ú>µìå;§ªææðÜ*¤(®‡œð¢×Ò迲Ö^²äõÍ#;㾫býÖ¢Ðìùdl¸âI펚„vR¼ìZ%CyÇfÅ\…µÖG‘¹²Lq[ƒæûä6²ÈŒ˜m’TRGZ$ZL2Õʺ[|›z—¦Þ„U3[ů6LbÙ”¥4ÑX&ã#<Ò噢‹ Ž!ÆR&lcƒ8®åV–ì«Þùà©ùN6]ˆñªbF0DŠªƒ‰di0`_ª–øxf0Þ£æ*]øå2w¸æºKJ¹Îj†Mí4[ŽóHLU1¦4ÇÏ~½µ±r”¦A„$(.×{à ¨÷¢®´¤qŽ”4Ìè-‘Nf­i5 ¢ßr0 ´¦  !Kε5Ä7¸Œ!²#)°&ˆMMØY—@]dfÌï:±løÐ™<¢wþb›”]’1$ØÆV‚R$˜ˆ ±¦Šsõ„$.¸.4¨þ&$‰# ¨„T‚-¬(•f0”! ¥â+ñ²îŠ"ƒ ‚Œ(;‰™Wcõ$D$t ÷ñõdœf F†ŽžNiêœU·˜þu C¼EXi­´*ÂÈÚÛ”w÷hb(ÝŸ­ª¹eß#<³–ä1¤ÒaAzU˜º 4gJÿÇÐÓh¬Õ ¨¹û35¡Þdë§V¬×Þa†Ü‘jê;Aá‹´‡/¼Ú+{:uÓDwÓÇšÃvZ.hhE7JÀͬ¬PåÊ4¤pç4‰˜6fm#;ÍÙ97ImuLòâØE_÷ ’p‡C™5XBd[³#)­ä¥ª{eN0EÆÑl„RŸ+6;'ØÔLK¥¨,L’+|Hb E’‡PXÓÜ£aá­b#Ëô ÌÎŒ§6ß°{ŽNÐß^#<ÛãDp-r%Ðg6nC‚xþzN¶ ý:à™ÊBcÀH;’vX"Ø¡:§*¦Ð`ù[Ý4sc1£¸^Ž˜ l¶ ‚¬ÌQ圆ö#£ò4ÀèpºÂº1Jê°­t<2˜ÓiBu”ã-X&#€’—Ši£­ˆñ[ãfQŒ9gFFžÌvŲl­f³£]ž!#\EpwÅ50)¾ÕÃðøO¥¯¨‘#<ÿ6dÃðW*ÓŠE'²ƒÔ‚¥Ôc}äÂP)‡Ö?çâ#<•óQǬRâ.Lw575}ô!fçí.oˆŸ½Ñ„âgGO¿„ \JøÈsžs*®›µ£ U¤½ËGÏ/%2škJ–3&R2«í7«à¼àþÅ –Ë8%g울š7Ç6ÒºK—/´e¸1Í×\<Ã;ª:×3Õ²åá %r'mŽoZvYâ©-”ËÉ•j¦aÓeJ%Å,µ™NµÍ9yÅÚèë9bé axëTé)Š7¾ö£šV©DMN: Ã[H›ƒ$8EÛ+5Ò­qŒeáÃqcF˜Â‘¥hÔ‹Q´Õb䢔š¢­:P,цŒF!Ü™L÷9²µÓkÎÜ’J¾)§"$Ïá¬;2æó#;ó²¥mª=rœ8íÏ3(4N%Š‚BZ1)C=¦Ä4lô­ÔàÛ~fŽÌbãÑ–©×‡aÍp29aßs¶m®"ïQxg…„-U&/¥tÎW¦iLÌcô´NéÝ‘˜yœob”Šv––%šßOÅ:,›LÉ­õ NšŸy‚ ɽO+•‡Û˜Ò!æ¥j+¥–ÕóŽlކöÅÏ#<àô$'K9W/fÂl›PÖ`Ós_“KuÖæÂa±"H€‚„mJmdÆÔ›Rլ͵‹äbàn@w£VéJ¤‹$”Á`‹LÁG%Nå”§_{¶ÿ4pàª1U2*–”‡XA…:šXÂ0x 4- ´0i¶#;‰´PÔ'¼,6P$‰,Ž_æ¤s7Fá[`¥‘]üÄ¥7d! à´m´@®¥™õœiëZ íÖ Øm¶¯?‚\ä®î¶Ý îëeºlwW¤œôòñ»—ÞeÜu×]çuç'žqKÖ&·K4Ü«¡WkuWNíÙ7Šå¯]®L´í<Ýw™W“bBÛ©[†ÅvY‚ %Æ„h層ƒ\Ì…»Îݸ¯m¸¶«ÆÅJV™¶I Mf¨Ûi²¢™®¥®•e¦É­,©²Ûe–};}^h¶ Áci”Z²UQªÐ•P4Ô–c C™o_ é£‘ÁbÏš“X6"¬Pšiw0™„A¢(¢_żH)“ˆ” €÷@Ed^Á`‡/¦#ÀˆzÏÛ`ög‰ì!\Gô°¢PÅ ïã€BÊf›>&ß«4óy »XL`’Làˆ‚0æu‰éP_P'ÉÙ„ÒlÀËÂsaážÌÌl#;ekà]#)‘ˆÛûéê1i(¸(“ͪÓm8昪yžÞrfnOâLÚÑšAèŸcøì¨Ì÷¡ªQ¢"$h*CACúð@V&Њ³(£3" hº›©­»jbÛ塳I½£²)¬”~žœ8YN³a 9Ôû8o¥«p-"n˜om.#q‘Ý#;@°(‘å[ZÊ CdëC¤ …./^ùõ]S)h"¹˜m寷÷¼¸uj»^,ßê$úûm#”“ͳ•Ô~˜*ìe¡F~ÏÔd_çû©Ë?¢å$’"-㘠7‚Bò˜"´m¤wÎÔ[I2óµÒÊ&¦{;R¨4’oVécfD¬Ô›d˜Ö7›µk™¥SXÈ¥´›(ØÔ¥…,ÍIR™)Zb–m¢%#F[ò.ØSZÊf6†b¦$Ú(Ö´Ö©¢*õÝÚ»¨ÚI¶WuÔµ }{]ªë^Ýv4TÄh$ŒÌ¶ÚQ-JkRVÀjj1©L*¾J«µ¾N˜%‚£{÷l™²(µ‹%±¶­R$˜Z›kEªåu³%¢e&[M%¥lÈÙ^yæÞ¨•›Mš4ÈÛik#;j-–ËI½œZ­+c´^*è¬ÙgŠëÎä”eT›LšÆk¶–¤Þ ¬µã’Å4É"ÛBQªð» Å‹ 0’ ÞÌzx?Voƒn§¾÷3²ZË«|:K(~â´²®|J:f|ß7}z»{u%±§~å @?”Kv"©Ýè|"#;õéÎãFd(öç«ì‹ùOš'žeJŒˆáØ•Z'Y„ ÙnX±Da!”×jÛWÂÛBSáÝ3RÓifiˆ(@A#)… ì£}p±#;F³JÙ¥mìª×KjŒ L$,ÒT¨TÖ¨¢ ŠTY$Q‡‚¶R*fZ¢S`fâ•%" °[nZÞËZÜ©5¶[66b–’˜›[ôUÑBQ©IZûý]¶ÛfÙfjÛ%¶SE*So’Ü¡†$ËQY¶% ”eiI#<46›e)¤MIš6˜Ê6 †Œ­”ScdÈ–F…“V-R•AQR›*RšKi-E%R²EiDÚŠÙ´Ò¤(II‹&Ra4É4Éif¬ÛÕVD¨¥‰šÒd‘e­©e²d±¥2m)UK5µ%B˜D©U 1ŠBD 4›|fµ®š›-ZR­d°‹!"q ,\@ ÅKA˜¦Å[cVRØ/4Ú®km"€ #)ŠŠH#)eíì(ÙT¸0#)í÷Ð<?güÎÄíÄ€yq²L­ìt"~ãs.9»÷fg³f<ûWõ0htþ;Gû¸®™Ê&…ß<)âZèD8 »º[ „F1q‚žšR’¤Áçu=p¤.Fð:n©j‰! 8óßoo9Üj[̸¡ñ‰ýÝ2Âz!Æ<#Û Yíx†EØb¿_V5Õ³®»±lж²‚f=%ÑÙ´ÐÉrd¥´”;¹T¥v˜—UóK_ðíÀUYþ¶¡£n—ÙŽg\¡Ð§ï6$¼"Á„†ò<€§3™¬$ŒiŒÉÆµŠ¢2Œ~°ÐG´¯Ì.?˜ ·¤¥ºP\‡ù ƒè7mÁ¶Ž¹µ7@zt# z;RÁz/B$Ù^$@Â÷¬@üUs–xW*IRI!%H=]À#)XªU6ß9WuÛr§I2”@+@æXÙž ‚-dNtº/3[X®Ú«J+©ŠOtñ¾þ© "°ÉÓyâZ ¡ŠÑEIUƒa86(‘*R°…†jUQ©²k0'Ü$.!!0“Õlø#<‚z¶"鯡ðî]‡ÏK´o:yÛzìÝãJQe×â€ñI #)×€?¥>¦&XÜzÉ« £ä‚ðd‡éI¤ÿW÷E°*å§T*†R((a”Å>X»ˆþÆòÔ¦C"Á ¡@¡ƒû¿†íÝ–™]*ÙlQ´2²’!/5Q4 kMd Ù¨V¶‘ÇÆ"UEß5ºö„س!â¸{úëyëµÒÑ‹c@4¦XoµŒÎ–À3uIDh½XEXÙÃAƒmÛ*‘Fsba’#<#C"’ÓyŠÎjÄsBÅF[ªÃPª¨#.xµIã.i9ó&ý#)}hkåHH2!F’ˆD`ó]µ žÍˆâ€äãçCŽú Ð‹D1+a2¦ª&,U¡*ác3Ë¢¹(©¾ ¤Z×Ê=°í?Ãñèc%.çàF“Ä+‘ 0Þ&¤áÉÅ=¯{ºë°Åž#X…X¼´A°(Z­@7)P!x*ctHÁ¦¶÷âØéÕ[EîÕ®©b$-œùÁû>¤È²:޵¢Æ|6ìqõ˜áÓëô‡Pe°®Ý3´ š¦?tÚ¹—í ˜Åâ>ÊðüŽïª·"„©á$ík1U#;-z½972Í |%%æ…Áƒ¡ØbúF·ÑÀ[eG+6,hÝ0B&¿]¤³(¨Ó¾ ©P!+~Ïåë?N†í6ÃãXÝYx¯\S´!žh<;f!–ÜgôÔ.iôt;06ôÅÆà-±V$©ûÐÁˆÖT–Öòbþí1æD34t„δCª#<°-¤Êoƒ ¿–`›¦y±ºu~ [™m‘¶¹ÓR' GÕôäÈ0bBí4ЏHøhâj&’“Ìh÷ü7Qƒ!±¸MÄŠ922á@Ò…ÆŒ%åHZp2;ôÙáZ« È¦qÖ+²-Á¤A— B¡ÕîXL›~IÐuG"×Á’fk\M#;HâŠTß•0ëaŒ’c#<àæN¯芩! ºâ+ŒnPn55kS¥d¼:éVx©ÄÏÕ|UA°õq‡yP¢©z2ŠÚ‚Á µEÙ•5|dzíoÅLûBˆ˜#;#)Ài b86Ay½‚bœ%CŽ“C1€/dpÀ,Ñ k -å~ž?N=G_͡¥(rÎ6ƒ¨¸]Uy)t/Èšpò t˜` bdÀuÈ‚2šs’êÚÙÄXšÇíñ„ç“Àå…ªžüÖ²%ºØìš%Û§M(j™ŠhÐ6¸#;@f#)#;*@ÙòËÓDÂî(¼?>+;.J2xÁ.7M®ne‡BBo¹®´`sšd0´ä£Ý=&ç`í/|öíR0$ Í26ÐÜŠ TyPt)Üx#Þ«Ü.AÂÆC#%È>ÇÂóBÐÔ¸h%C)3d%Ñ0M…wçëÌ×”4œ¶P AbA=Ü aÁâo48;o\“ß%‚&°ò„ ö‹JRò†³8Ql‘[¢.Ã(¤ó ÌÆ©`,4Ô.h·kÚc µDÐK%³©rœG!¦\˜°b(x›PtÕÀéáè£!#;±ýµPª¢£Q#<ŠD‹ULdªåÙ5sI´r­ÍI@ªÀ@5Ýò¼Ã `aã#d#;õ+^çƒz¢8¬ùFyNt)èÊO%숰*±P€, Ô‘ݘ™ÝA¿¦ß4Þ|¹¾Ðœ“^Esȳh¨ÈºBW^à’)2B‰@ ˆ(¼D21eÌ ]ܧåÓj®q4Ì}ßíà‹¸4‰¨\ ‘- ª‰:{¾cn2ÈQ±@ˆTD쉰¡Ûl•Èßø¹ò·w_V‹¨{òòœs9‡ˆôª¤e†CÈQ„£`ÙÖ#¢ ºšýÔ[©£/5ìÄ@&¦ò@à#lî+ûôe$=Iôìœtåo¿ø^ˆìùÏkìD($o(–ÑH¦Î!ºß6üí;ï‹0íìà=Uòx(«Cš~/iá@TT³ä´8àÄ+«îÍì>~I>^FNͤ8wõ…æÝ›þŸ€uíì#)†‚"ýa:¡ ]#; çÂÕ؃åæ¡â¯Õèx „ôI’—¼ h¸`#<¡î ¢J¥*#<‰DRA*"TRE©À#±f…â•‘ ‚È.`"SPÆP;ñ~…Mæ#;eÇqÿ?GŒ©]+EnÜk2$(·žýi°Ì"dwIÔ{ì}/j™þÀ#;ÃÎêÉ|Ÿ`ôH17'¥=©GXrxíY±•ÜŠËúD„‡Dç$$|b¥DNA¢*œíÚZ#Ô^óÅ´‘T- H®^e~~ö®¼•’ï­Ñ2—_m>~Ö a¦ŒdªÀ‰öš”þB' ÓÑM¢ŸÏ³Ö\ÉIª@†Ÿ­úºÌ{zû~æ >£Ò]ú-¯µÇ• |ÃNYx帷§}‹'äR'‰C¯·8FLCK]Êî—quÝ]®ôÝBÉÐWµY°5î%{vYdûX‰o•#<©M°‹¥T¯ïQ–3)4ÐgÉåÕu¢kub¢¨UBiü´Zâš(š SWŒö»›\®5Ky>M¹­èj¹ 2Сݒ˜`(ëGö¥íûl-ÐD@?¾#;%#;¡#r øÆ#<Y~Cu¤²–ÛdõT˜¹¢j³ÝÖ‘àQPŸ`{½Ì”1üYD"¢J’³3yÜÛWy×^L·VikT1¦Ë#)4Ÿ?êËú)Jaì È4Gõl½¸œ¹|¯¡¼<4tçÍT­-#;¡TÜÓ÷Â~h…˜A`TõîÈ$‘”Ô–­6•´­/—òéµNèÔn^­¼j¹iSIrλMâÔÚ£%*)yÕÂÜÛ¥–¤ÃclX¹j¢¶êk´XÖ§Wr[d´©´šl%‹BAP6E¸ª° j‘üÅ“xý‹üÁäXw*l  ¤-‚ ¡¶ÀÿWÄüOoBÀj#)brÎ<~Ï.ï–VžÁÔ¢Æ#;î_o ÑrCCѱT D)…$A Å@±jU ûe1 U1Qú,ÔŠâájDZ±¡xÂ`BA=ahœÃ²ò ™Dˆ‰ºë¬î¬ŸHõ§hå#hãä@‚dK¡ôÇ(…à^ª¢$Š¿¬Ð%âÔn_æ–M_m«t×®º™RËW6åoM¯­VòËÓ¢(¶KVbµGMbÅu]Õ½04+(˜©* |él#)âÉ F›D¥«Í×S,Ĥ’ÛF«I4‹%ïðíªø#;W³m5Q@\Â##†pΪұ‡Ã—Q¤K1(ÿ!N2n,,6ÜXt‚m€5—R¿2FU¹$AõC@aˆ˜Œ 1\AÂKLD)cÝÄ#)m·22UUz=»?–ó<Þ­¢õìÏë>Φ-`“tª¨qG:?Óçó|{v¼DcúÑb¡hí*`¸]—=´µƒ…#<yïÉîGï4»`¥DCgȨb€@4£l ,žpúO²¹Òqp5ƒ!í¨l㯂.à;ÑJý弯¿wå–¼s«¶î)#JZR–¬k_ÅÝÅu›v¾»cMVi_™«©ï+¨Ú"4S‚Óÿ œŽó‰€8ý^]¹ ÂC½ ÂiŽšo«MJãÞÙ€±þʼÝl™ê6ºYÐ ¤2Ó`û}wŒ~I|Ï·‰M*³Ëùût&óïµÔn¡‹1¦×ñê÷3#)èÐèqoWµ,&1„ɵ ªDâáÿ[ÃW1Sœæ¢¯ ;1 ì8+p­QH¡G#;ôÎL×K ‹u´‘"0J)aAu`ˆ†P€[Å¡ƒ4:ØP‹;(ñM³A¥‚ƒ}oU€³È}"pô#<èO ë:ÎÚB@TUU‚¢7€—ƒÓüG¨÷¼ ±Q_T6ì6ºš›#;…­èáÓLŒ½}ötT¾ºÍG-¢c½[w®Uô¬Y[Â¥9DdvBñ-!%J-ÂÿGÇpˆníu7ÉšœH5lu×^€VÛUJ$Ý͘ãcE#;LrIÎÜHSHovÕ2’J?i½‰jR­€,éÁ˜·Ñ¶»›aŠ»>ˆÒºiHDÀ˜0 D¢¥Ø#P³N6X©Õ4OB¢ûž8êûþžóvõ&H‰¬Ð8 zé ÒJ>-xF©Ïý0°LÀþ\òýx,iœ³LöS LÜÓ[|bƒØÑ³Ú­W#èq×|Ÿ)Äo÷a±“¯¬O’y(‡©ö¦ÍNuÇS%­~˜ Ù#)ÊaØò˜Úˆ$A­ÊHµªR’Pµ&Š)-4¦LkSU–¦fQ4-I˜Ka¯Ë+´I!¤,jR¥FY¦ÒÏÎæÚÍ"Õ3-i³Ce+ 0ÖÓER„)dÖij[lÓJÓmJÊÓV2Ú[f¢¤(ÕˆFYEX©k ¤Ô°Ö³D´Õ*µ¨ÕŒ…-1"U›5¯z#;OŸò|ìKªü©„ó«EÀÃ.½8a7BÙlHDØ‚àW!S¶!Ë÷%‚5d Q¢"%EFE( ¨SmÙKÎ*ó#<ŒþzÛM^–Á›)œPƒÜ=‡g8ª®ê6Ê®ms‘krÆ2u/ÉkûMío4dvÈÄ?)wD1zª+}ÞÙ`Œ‘W—;‘fsšòî¿*¹//ÔÝHÀÐ}x£ß0?nƒ´Ú‘’(²ýû#ÙÁGu³#h x°£NÅ#)|° z|ÉÜéÍ Ê H DF¢î€âÃÚœÅç`þF*f{BÿiJe!#)¢h-¢¢”D¨TÅT· ‰V[¤ q¥Ob™TÌ¸Š–¼U‹c8ó[£ Gê㮃?ð¹²uоlÞƒW Àö8¨â»±Ò@š‘ñEÂxyÔÃ4FÚÙ}E°èÌmð 0•±«‚BÎP24ÜöšäV@‰¥ËJ°×5ë§&ðOæyh‹Ðý1BÙ¢©`d h‘K‰v#;ÂÊÁ…#< 2,"F*F[™òŽgÆ#)¼àž#)PÒ3Öz2dƒÐC¤ö¢:BôÛt*ÖfcÂ-®b6uGÄÜ`x‚‰²sKNÊì1DD#)€@™3¯©aÌűH7`œaàI·—µí¯_.¤ÌQlî¢Þ[¢j°ˆTas€v?@tipÏäz°ž0%ÛϺöíxµ¦%wtt¦o[ ~·}@Á²UQfâÁÔºÔòxý]ÇGëo ûš‘A³çú?YŒ’(ˆ¦›UëõofrBŽ•ª¨§ª#;ÏÂO¯¡t0û ¶£a4÷ œq”NÑþlI`^­£ ‰":bØÆ?‡<¤ÊhÒÕ“U%5­öòËM«¬¶ÒiزõüºmÝÖ´ú§®N§#<G΂osÀìÜÓÝ©ÎRŠˆ™ž L+•Líú>”‡D òlï )&©#<§Y b²’¥% nT#<=ÿLuÚµÚ2¾TX])™^ˆÌ¥•ˆeF65÷T0o#ô~Ô|x`=è#£¤äí:#®‘QPØß!o<Ò„÷!zÉ#)ï@.±ÛºlÑPç™°€|cÝNÞá|ã˜;yСÀMC] ܵ-#;I„ŠÉ²UÍr뺳*¥T*JUßpšËû²^Û¾°yÉߦ×)çs~é¶òï[ËFÖckô<¯3Ö÷¡Kž:MåÊd¤#<»¸¬œÚŽ*¢©mA#)…dƦ¨}|l¢È¡ïk‰ >P¤D:›ˆ—¥Û,Mò]˜qK³™“T#<Œ:65²þf=SE#;Ñ’6Å3Á†V¶ÂÂÆfFÖÐ!¥‘“%›¼»ƒ¤ OkÚÌ0ÂeDŽkãÀ®.å%}RšJ bÕ"û`L#"Çúòo4A¶bK2åK+cˆN5,§kIü.MÉpúä#<©A¥®˜?•9?[Äxµµ“Œ/Ó¾mÉCá¿ãs“XnûŸî£Y™\ˆI#q1{wh1¡$Ú@šcݘÙfá¬Â 9¦cÅC¨<¬šk‡p–FlÓ¬+.°k.aŒ~²c2¬¡GIõÔiŸ¸þzÇAA¨çINö£=p龦#)¼¡åÅLèxC“©#;”ë[CéQóÐtEµad)ÄùN±%† vbô”±)úó§Ua§·ÕšW¯[‰"[4Q±í"n B/­÷ú]vÌòN¯0$‡•4@ÚÅ#£H!IAzä0aÚLëµâ»Î•×yyÎÚí ¶jÆ“UbÔkIZJM´©ËºêîîZ$Àh  #ƒ–Pd°£ìûü‘Eê~ö#)rç[‘'D“‰!,!ÈSìéke%4Â#)/â¸eþojÄVÆÆ€ŒÅ`m€Ï°•«©ŽABÙ„…2(f¨`»ÑXhð|xQfä»ÛyÖZB9ªÞºfR˜± :zîÕÍ‚¼ÏRñb£Z它K†`H’1#;…m˜ôП{ùoíÝ{+52äñÎltÙžÄ1¼©B„Ìb½(¹Ä) 9rÍ(j¼¦,J±é„k¶8(Û™\ciäRÝÓ¶Þ4›dmj„‹MÛ[5%g…8kBþæÕ M*TV£Ñˆ[å͇¯±F"ÅÃdr¡‡T 2òb¶qv”È#)€]ÁF›Ôõ„@8ú‰Â¨¸æ¦~d °!¼)ãa,g"ª#<g ;vëî§Ùîü‹#;²¨ÿ#)qO¯¾––.±G£Ì.Š£û–EïPÓj¡›þª­ºëFDL#;±¨’¼'1|<  {(J¸böÀ2’ "¢÷E<ÊÍ%•É[-“VÆ©¦µúK_µÍ]Çn®µÕR“ô{ÎíãE¼Ur›»\ª4ƒò>õ"HöOÕÔŒûâx!g»‰EÍݪ`Ëзú§ }ö³V£ð›ƒ"…9v: Èî=øÇDȧÏå>c­‡êIéƒL&Z‚ÆDùÜÛ]u«íãŠõíçü»]u®g*¿²üVE´¬¼Þéð°ÔžÅ­õ3[2w» @â]ÔÁ0 …ïöÛºéô:7M¹¾Ä0°ÄÔ ¡/O“ ”ˆÂFÆE3³G–)1{h\qÆ8âàadBdÉ·Æ'Ó¯@s°,QžªÍæðì&Ù3‘°kŒÔ˜ÜÁFAÂÉZæ[÷¼ð3´S-AïÈ¥ò—{}[bÜù›H$/çãŽæûÛuýÜíGž¾µ]†N‘vò8ÖÐBLóû%ï…pŠÞàfë"¶Šû?m7ë²Ù-)…ˆ€±G€Áµ%ÄS#bñªËePñgjØbÐR¥ÂXÆÖpM1¼\l¼N#;4úo<›ãˆ`C…Âáe€¼l0¢HHTÝP:)<”#•Ä‘m æØf©T; L]A8Fî.G’ÕlEû´MùǬ"6}0 5×ZÙé·TÔ{ÑÇ¥õ>ކ Êy{Dù·€7†:^$a|‰¡°ÝØKñOlçPˆÄBˆ£:<ÃbDqÉ *¬Ž4×µuËwnk­Öjš[JV¹kwnÕu3amlÍjº­$Ò»¯™¼»µ+ym·ÊYE‰KKm³mµcmkÊ‘åƒ`ÚSÃÂQ @ú|½ÉJÓ^¢”cY›¤ÊÝDÃRZA#–¾,#Ä| ýdD#;Mï>«r' Ê28hƒ•è$8g^oœà+“©r@·Ä@æ{§…x¶¡Pˆ¬õ6ƒíoë÷!c%ìQ8œýÚ…½Ö«åÕGªæ açˆ뇓Àó:å]kéúyÝÿÀÓbÕb0V^B­Í;4¤¨ý˜Ï%ÏÂt¬v»m‰#;ŒC Ì~äÙeG£ˆÇ>´ùÀD3f·¿ôÿÈÿ‡ú?Ãü÷Ø£ñ P¥‰Ê©k®{µæÁ”Œ¤Å‚F#çé{Bè2óíc< ít©ÆØà:#<$kp°:ˆËæŸômÃŒwËs{P5u¶ã³ðëŽ:ʪ÷ɸhAÔì×Ñ'×·]^Î [å~šƒ‘­âá0Ù‘›ª mŒ…;4ãz Jаd‚[Ê;@8tô[ЂË2ZM¶†ñûXŒ"0ïø3¹zt¶Ûì錢&à(y—Bb=0,”˜gÄ(™Ã¤Âé[èÊX2êÍm’5{ÀÜTÎ۟П‰ÌðÖ.”"0€¤5³ux#œ(§ù+…6S3Êy¼²‘Á#)õ@#€¾C±¶6‘{LKplŠëÙ†i1¤lˆF~•¦­Î¼T®…€ìñÇð}ØÒÓ\°È923ÇN/‰6¾‘ËÜcËw .ì#)wf)QY2Kß1ijr†Ö æy¬áß·7X)̉ÕSËz§¿“/ô& 5žûµC’°: æ»J‡{ §¶O(¬öu,¿GáÆ{úëñ€e'à$F$;b¢T$! <‰Å0y¸©+üAwÛŽ@=" #$µ#)5¢s›EâØµcZŠ­Eµ¨Ú*ª*5A£m&Ú+%Š¨Ú­ãZ×+&*®m[}9†Ž#<¢q0nk'oâÓÜûÊî %DL C ‘aŒD™AýÖ”‚‘²d*DdF È R¤Iðñär=ùâcÓ˜ëÅMÄUL'9rÁ„ï«|ÕݰÌ7•"«”Îæòò²F{7(fˆçŽ&3~ú8íÝ ­OÙ}þ͹Þ®bÞ@uq›©ÍI i‡òÚÌ&Û$†ŠØÓAß¡+Zå°šݤS¡(…þŽàù/7žáoÊÐ@"¡ Ôó¨¥?#;Ô€»ÍÔŠ•œÄ8˜ä•à7ßSÃÃ4ðF®Õ ›°§ðb„Ýšþª“T´8š•8ñÓ^54ˆÉï8»óÑKäHyˆCÓCÙ[ŠœT$™—\æÚ_bˆ>åîÁ´#;•€eO¤é%³1Œ\Œâ$ϾÌ6‰ã°C‰æP,`˾ÛŒÌÈß—¸¸û"!ˆ˜‚NŸ­¹$$‹ÉmþOêrEõ_¦š—ëS٥ɣ†nG‚G2$C¬Q‹£lÛTȼZ)õBððÐ`i¢d‹(i…} @Ûpƒ@*0WW2Cý‚à”‹tH–¨©b‚Å@ È?Š‹G~‡e¯ÆfrŠÓÐdEIŒu8 1Àdcá›Ã²DŒ…V6a¨…@qß÷ˆ~¯·àë÷CìÉòú{Ëc׉†!½f×WSŠîw]ÛÖß#,¶M¬¼F°(Åј5é"M¢>v6Y½F¦CŒðÀ¼¨´öÍ´5cca÷È-=ÍïâÈs³~“2a 0çÖ°“Ö—ŠN"5V%˜Z8jÍÍ­£K)QY:À-qΖ&‘“kh#;ëL\5íéÔÆ©¶? JX!ó1âké$޳ŒÄ]Eú‹ð:M"ýGÔmêî‚˺¶ 7-Î #uñ@À§ðö4ÖDîé¬c„13Ù3¼òu—àÅÏ2{Ý<}¸éi7¢¡V”[o™ËEw!_F+!a (hm|£Ç-¯Szd=2TšÕíâæ•M„føš]SÕ8ÝÈLJnoY!¡&s•\"ѳa†•Z¸ETrØ©(?YhÃÔ¸hΓ`˜bðÃÜDy{mã3¤^‘œwÕ#båþ†Ãt,Ü‘O¢ô ]"§6hFv0ՠɪDAÖ¤h²]àºe‚bÂÕ»´\‘kZEXõËPÌ%&a©&å˜!ˆDavYB H…`l6"¤[tR) E•¨4&#;`a° ¡,¢Ë €Â$K…„è1 eÃ×TTZDÛpC[þ„Ðz€ò;;}dnÐuOvú !( Ô7Œ"¬®Ø?Û´>]Ÿ¨žˆNèý¨‘’jà®rbé`>ènýñD?|â†×Ì„(W#)m¦ço¿å5ÓVvÖÛ¬QtëVýM(ɇó¼2m%oÓû·<]c»ñ««ÎÍ ¼çÜúùÃͽâ¦{4dÔ$ržåtö8j«32æ¤0&‡—#¿ƒîS­Åˆ„’„ÈÙ¯#<ÌVÑ»xÕuÖûp“ÆÎ3¸ãGhÂ,CíD¥™•ðlmUQTe¬Ì˜dÎÁm#€ù{VÚP›) #UÈœ¬èpø?4ø;ngO‚!È7ç–ê;¢4‘Cšíµƒ™wúA Î˜Ó&?ÖQ\MÐÉÌÀZ#‚·L9)7Ž}µ_ŽYÙÑ~N×±ºBÆ*“cYj{æÑÙÍýlñ ¢y¨Õ+衪,ˆ™JD²êXÚÁl°$Hnz¢`c¿*¿Q&Ÿ}¸ÔoŒ~”_Ûßãèûh08±³˜(y­-Ãn1LU£«<š`ÿX#ïP`Å„OWÙ3ݰô9^¡ǘúã }ÑTmä-Ä×’¾m¼«ëÖú¸$X9™”XÅ ¢"`C«W•_¯(´£JÚ-«ç¼µCè¤@ot • €ÉœjU›Q*”¥*–Aƒw,nÚ#-“D–”ÛoÚºV¯5:V¼zî¢ÆÖ-y×.šºÛsW;&C—mxµðÊ’ÆÒ,ÌŒT©nqÝ·wU‹Rm,¬Ú²@-¶„‚2™Œ”]ܤb±‹UxÚ·«_™ééä·…1Ô~[5}Zn°£÷[#<äµ[ŸSSÂåbôj–Cê#<¶€#;â#)BDb*0…ÓX¥(Z ³Û$Bº-Ñ)¨'ÙR‰•‹^qhjñ Ík­{my3D–àtBÉjÛ8mË3d€Ô…ÛY·VöZ±ŠÆµ)©ªFû[$R¥h*Q3Jm¢¨6-3EFFª-FÖR°XÑ£i•¤Ô•22bZ–(вËHɲÍM•¬Õçh(YÛHÀ¢"¤§Ùç™ yhhoÝS"gÓµmy^ ÒU£hH!$I,¿9Ù¶þÙöz/ãêzžKzôû¬Xÿ'¢‹¯bà6櫈xˆá1*Ð$ÔpWïs©âÕkm,m­ùZšUd³5¨¥;¡P87 ’ FAÄŠ÷E¤Ž¬—ïúº‚Ѧõ¢ÆÖwvuÖ»´RÖÜŠµ¨¤Úkºº–Šjµ5¬¯]»5–ˆ,XR •(ù\þå5‡,ÐdHƒ>E'”„žp @0™7ÊغPÄ$@HÄÞDt,„ Þ›²r­kÙ¢÷î"®îŪí-j»|4‘J8µBÀô#À§áv§Äu}<šõM z=8,k&Ÿ1ûkÐQ@…ÏÍy fïT…¨‹ éÍ6#)„ }§œ*Øzj£ùYÇj˜Ñ¤#IPYÈ¢§ÊïW÷ÛzÛÚlk^J¤ÛfMOn¢¢ÛbŠMjé¶6¼[É«UàífˆLiT¬2% E#;hU,p O}fgY¡H~ Ì/¶³oU¶õjÛ늶ْc%­«ëQæ³åv{îã«^wW,)î»Å]dD›JæÞ^]wu_zôKB¬E,+ "‰ÞEÅÓÝÅXÙƒ4¤U¦ØÈá‘P5ƒl –µ©Z%AÄ"´ˆ‡§ZUkºj¯M–•ë+ÊóVëo7Z¢ÈÁ¢£C ÝHµÈ ¤Ù#)LÁ+±•aûÑf„ &‚¨†…Ð-žÐmÜ¡°ˆÔ@#‚À`ûIÄ.BœŸÕÉ×Ñ*œÞ#)e!yYìè‡ý§î’}˜v¶ƒ SŸò#;â¬cNKllÁÝ»Stô\‡8Æ3-¤µ÷ìøñφˆæC¡ÛÔUèAD +UE­_‚ð˜#)1*Þ­]‚]Dp N_ËÐñ;о€aò!ï/Áaò&¿œ1Fçðo¬þïâbY¬Ã‡?6L–%söÊg™¯Ÿû#)(‹Ìë?N6HHÚ‘£Ù!c'íóßcòɧÇHR=N#)3i\s¡œ9hTO!`ñh|N—¨º²¨iP²©ýl»…²åã2HÄQfÛïgGÒDÐxÅW§MgK#)ñ,2ü"šd0ݼBS´C®T>Tw›.i¤ùÃ\Fiu¨W¦>*0±…XÐV‘ÞBÄ@ÉHÁ#‹ôfaD˜0Eh‘Æ…†€ÐuK,r!Ä Ð— 2H"´±ìª%#<™&FÁ‚Á˜ú¾‘þaX°Ê ù †n½}{¼gŸÃçïråI!.Vsñ?A¢kS†qÂ> ZJkS§52œSÔ‹ûò0vp íâÒxˆ‡\Õ](Uݬ·Wv3D¡€öÅ]#)K·.ž0#<H)QiB!Ž]À„‰á–œÿ]Ÿ+¯[êzQÜ•wUÇVÙ͡٨‹ó˸ÅdOáäO‹‰4g ÛAË%hƒi•ˆ#m „﨔£/+”Š5J–DÒ"6R¥¨¼n–¦ËRV))60o†Ä`.mÊå·3‡tÞo.®:ë¨Ìs¦.Wwn”jåâÞ<›c”Ùo'“n®]W5™b÷yj›Z2<í¶ºnZé«dÕJXÚëæÆìÒlšÞ;£tæ®®Ìé®HÒ§vè9WYs¬´m9¶¢×Zm¹Q#)]6A^#;ß4TLa)ê€dm™¤Ü @#)woôñ€qÚǦ bx#)<?Çàž0/#) ‚PÄ#<„ N¤¡,'{.-ˆ?¸Ì*ì3GÍùg)í#)C¼ÞÒÕ¼­ù®Õ½Uõ’4L‰(#;D¥Kb4Z9fw×<¡<ƒP×ê«g–B|ÐS×´CÑ·ÀR~*(?7¿Š~}{ƒ©·Å;rD^èÖfFEEU¦iŠÃqØÀUˆ"öÁ¢@€TÎP~ø(‘ŠNÏwK#<åD!DXD‚±@`h0øŸ&Lp(±_šµRéi¢_hÆMÚ[¡wvÚqÛ»©¾#;ÕK\ÐYh-‚4Ñ¡|(Q=Å$I¶†1‰D5ðFø™!,9#<À^ϼˆ|e^›mÍ®”x­|yq;¯]]åÜ®ë©"“n\.kº©¨”´ÖÃGÆÔ«p/^y©»»UÛßw¦‹&½.Q$Q®^•µ¼—wUÔ«Åmeâ4¤Y¶-€žo¯2Ér.`ZQ³ƒC`jTC@µ¿£#)ß(„¿#/A}­üQ6REä ¼ï¶ÖUd–ÿ/Yî ïˆØ q @aŒŠQ aNïýÓaffiP8w]}7¥¢ý­µU_bª‹J©¶Ô¦¥‘ƒ)KKMµ˜²VšÔÚX$8#)ðÁò<µÚxUWz#;o–Û*›Ë¶Ýl³fÉ‹*™2¬Z©–¬²Ö-µ»l¯¨ô7 9¡´ð~‚vÝUFRZf&nš´­FkCf"±al\0†ñ@õÒ™­Lñ£ž´»P=°RFŒà§Ø`v຦Áfóìƒ=ç´(¦ ÃL©Ì9y}øÐâ8½dzî©áômÝæ?N>˜EûÙ'¨Hƒÿ@Öf{9Ÿ·=8]#; ¼˜Øƒ„f ør”iEÙX.¬‚YP‚µÆ•·‘ì³OL×:Ží…nxE?²kŽÂaÙ T-¬‰B×*) ¦d¬´’ZΊ-ŽµÑ­3¯Ahò]ü–ÌK…ïØíˆuŒ;NÝ)>Â*tï@´•'…`l?jSO’hа&(CB’FƒdŠÑD4¨œx@€§cvÚ÷îÔW•ÙŽ¸–³Ú«Òñ‰œ†Rä„FGö¶–2À¡4uƒbCikq#<Öµ!*!X0U¨1Ìf+vÊ#;46Ì1¹rвKA`J@¦ÞölS ”þm Æ ¥àŠØŠÈɤÇõ¾”ˆI$Ž(™&&Lô½M`Ñ$×ÍÄžß‹Åì쪰kB@˜%yFîPȺAjLâH¦†¥êÎ\!ˆÔc vJõûâ´c>½a±oo‚.hS%$ÅȰG l©l.È”žÄî‹…K"©†º+’餈2 ’xˆÔEG9’©hQLP baÂ~< á \ÛGìUÍ5,›÷ï RáC=ˆŸ}€`Ÿ²mZ†ãC¸>ÆðR¨'ëÐêŽÔn8Šy·Uè¤OÏTC@˜H í:pDÕ|ö±¿jÑmjª‹©VØžºb 6€©O²Tˆ #)ŠES¬!Sì ÷„ º&ˆN…Í”Ø;Ÿ¾#)âÛ”HA#K›Ãéñþôå~\³8"¸ùÀÿ/gé‡úµ=$ó‡è(*Á$IR|À?<a7·{ ƒš¢» ]öýLJ>é èŸ^_ÏãW½åÊqñ6æ›&­ Ã|Å?iµSl“#;¤Ú>®Ë:¿Žè٢ȸâ%fg›Ç¬ey¨j(MóBBrÄlÚúUìaŽA–Èh•K«ê>¼hfÚ;wŠ¿¯Ú¡:­HpçQ©|–—chŒÛƒMïÂ*ØÕ{r!Pœ#–CëgA]:Gri…í¦nŒéT£;äÓ+à Íþd‰“2†6QÓ ¡©45 E˜",Ihòˆ`®£\¨lh­–úf`ïüÞIäªNYfM-¡l4èfŠ$îת!ø¾õµÏhqSâLÁÆJ¨,Xý Qa2‡Á#;8¤=~¾É¯K÷Fž;a£ó¥px(‘ϺbØßÂ.½«=bóŸ»_<ÐaÆÁelVlÉ÷QPŒ¿ Î$Žr=ݰDHwVTKóXÄüaîsOƲõüàû&(oÀE–q\p–øiB{ôyë¡ð4'™vú&…ýÞ- œ¿jš)©M7ÊnwN%Ýr»@¥1„|ä,„’2êѲH +–Ƥ‘oŒÊÍr!°Vl’]å{- Ä™y¿¼;ˆ’ü ÐÆÛ‰Ym­ÉwÝÌtXDcEPh©ô Èìëu–&]óˆ¦!VRÂFC)ÿ»0m±¶2È›#;6éñmÐ2#R»Ë”9©å¯>ÖY[e5ÝŠ›‹±P2CµûY‚™ûÈÆÍ1ó#)«è-uöóÞñ¤(DæÐålcy^-¼AØE>hê1Òœè(=œãŒɈ¦PA[L¥Ed.ÙeÑiv$ZJPDCj5JëÏçÆ­iÛÅT´ÏG ¥‚„ @CïpúO×lŠB&ØÐ€a8\˜ú…+È$Ìï™mµëcy ÁggQFƈiŠd„pÞpB:ü¾íNùyÓ»&vŸߊ‘²I j LßÉ1°¬ï¨—C°âaüŠ­¤Õ–ÈëôàÖ‘“Ã#;JšžP¬T/È ½“|†Ä™QRI5|¸z¹_bC—-n_ò.®~L¥›ÜP„Ð Ý—R#)‰öß@5õàÀhŒÄ5ðíþ-‚²ñ§ô4t‡Y÷~#)f$`Â)ø $XP„#)|@<’Ôj_è†Æµükl·ªü“Z¶‹QEli©”;÷—«ˆíE0}9äTmxêôž”ÈffÊOVYYº¡ÈîŠ)äÀŽÇ¨>aÔv_IïNùtWè=GÛCÍ"Ý™–ਣ¡ë:¹«£ÝÙ#Ô/›ÝÀþchXÒs1à#)só˜’¾8$³£éÀ7ŠÖ­«oœ }àN;EW9kðxß0ÆÞËB1´Gí"œEÞÓÐ) i@=£:ת‰!$uÇʾ;a]9õŽp™÷ó#)ÛÐJ=Ž`J=¶Mž¾œíÊÖÚ-ísêæw·<«aõ‹Í#<U o'Í܉”#)¼RDïˆ$PˆP$0òæz#;—m7U¦ºhô º/.žƒk!x=‡šýæ‘¿hy¯©%V…`5øØ\a>zÕ9Ña 8ƒ¢Ì\kׯø×òfVREëWì)¤I¤lüaG>ö¯Í…ª@3;G–ýÞ„É#)F„h©ÒDŠ„v`ÉÛÒwaÎ|r°š( »¼Ôƒ Èx~Ó°ÏIÀo6K•Mžjýwë³&³3ðufSíܧ;W§òCqš×µEú]/žd!N g)Œzîki¬jÃåíÐöfÉþŒwø@ï?69Ö¯x¨è€ý…ÄH`Îéføú[ž¤R?«¡¡¬=-Dc¨bt%þ]wŽÙ Á<žë·KTmmEVvl@×¾,:d9à#A‹"Ne¢MZeÎ,ö\«L0Š‘Ì¡ªj‡÷¾º­Èlôü¹ Aµöl[?SGøxúsóûÈ¿†*˜ä\¤‘̉}¹u‰Lw'·~ÁÝ7¬eU8ЄÁÓ§óŸ]Vôùúž1lÔò`p:P“)RO[ðZ…US—w¡DÞýnÔüŒMìÐ3D1ÖH¢Ü“µ9àhPýÏmBÙ‰#”=‡@:$K­¿²TŠuŒãð¨Æ“bµH°‚¸D4¯Cgw ä¦;.ÐEf2–³³òAW#iµ“Çê^¦ºÆ90UCŠê0»õBÖDt‘~Ø!:åŸZéIÑÕªw®íbj!g„<2õà:t£$#)éB•­ªÀ8W6ºaéD‘Œlýüº$t^‹jýîK‰í…Ã:Ô“Ü{ôÔ>Ú3#)Qn­[†žNlÓšå·uâïàk­iKM”jƒPšÑkMe¬Všm¥dÕ©lV²¥}ÿŸÓåï:Ñdˆ cxnoRÝbéÝÝ‘‘E4ƒ¤}þ‚Fè¢(f zªU¤H-Wï¿óÞ½'゚26ŒÊ3dˆ£0h ”Ú&&¢)1 1£(ÄF”Ù6&…&M•&Š"Æ*Cð“—Ùž0€3&Ìž¨t!ê‹ð°âνTºú´ÇYì¶%Ë›]Ž—©žy¹¶:‡)°á¦5œ,k7ŽyP€(Ñû©ªTþ|#\šæÇmé;j¤ÑÔ0dÿž†uÞŒÜà#–é/*ûõ‰ôx_E¬…Ó•CC£2ügì 1Ÿ¥æø¦ÞôƒMm‹±77kj`sÅ)$?­²±fÇôÿ‹BNzfHòÆÎõFq¡dX,ŠMYË _±$#;²Ø•Ld¥(ÌæÜíFÞ¬Ý6xSQ¶e™½F4Œph$‚hŒ€Øvw\åÔܶ-»]Ùsuv®‘Þ×ä+fkn1–55‰{V jåò\DXVÙ‰¶ŒdàaKž(#<€âàp‰@—b#d"† D*!í¨Û¨FÚ ÆáV¤J¹¯®ôÚ¤´k“ZfÞ6àUË¥rÎë.ëUW4Ñœ¹ÏÕ­¤êÅÝ™Q–˜ˆ_{Ä#ô—WW(ñ‰ºT²—!KN4î*&H )GY)LƒJ³#)ñQ‚‘— QÁXâ:£ÌCb9N XÕ¤ãU04Æ#;D17EºÍ 5ìu#;D((&lhbmêF(Ј3Å33ŠFMƒ$6Å@i3Gä@À§¥Ã`±5¢ÍLD´“„e,e2¶«BE±ŠDJ`oTgh”Ë•δ#&C#;‘&™ÍÂAV(a€°)…$c ]H¡1Æ“TŠ´•L"1A¤cF÷ôªTû¬ÔÒà¶*ùH š˜T6ùªÄ¢%ÄÐR ÙˆPÄlP”Â!¥–8ÉfñJ6Þ˜¢ù4g:£›­Ò&ÉQ#¥ØP!õ¡#xòEƒ‰Ò}Úô2T72à¼0Õ!æD¤¹ÖÇ7a%iÌðlsBlÐhjü¾ÿñåúú0d ,‚hxC •d öky¾q¦»ŽªŸGnÞõ>ƒè)Ð3×¶ïçGì<{³ ãÂ×Ñòv°@2;$“Ã_CmNÊÌÅhoذDM*9†›^•U¢ÉŒÔ˜¼ š2¬5„xÌïb›ÇÑ{(™«O ÃMøIí_G]ðÚ$HŒKKÊ„G‹É~©êõÏRŒý'°AˆˆNzc¨d!àÄVFš5$[j+!¨•i›J¯¥;ÀÈ3Ð*jž2Qw⡃Ü…Š/еå‰h”¶¨*° D¾{ iF¢BÐ,S%=UÚ¶Ü´Óġ쀅DÂT*=/—ŒÒâª;€PX¢È);ám^Îvl6ÍDü·i¶š(ú‚Üšé­”S³H´šÝE=9M¬¹¨±ŠlÍbÖ¶°åƒnšb@qPQ6ßBíà{¦n»¹.®»uV¼ç1L#;ä,DC>i¤¹lÃ2ÀÈeA™q#<K–Z\˜ððÏaùû¾’ÀñĪuÏfÝÍÌr‘Šw¦‘r¡„Þnm²=T_ •Š(#¦ôàГ: 7øbXÖ˜.¤Œ0À—mE„üÍg‘¾#;Α9`8™¹[­¢Æµ}R·¸þ;^žw›êºÛtyÛ\‹ÅµÒ6îÛ¦m6¼nmF©5±¯ÕØš¹»»Zå¬kclZ5ŽEsVî릫ºm²ÅE[¦­ËR[›šÚ5r´îök‘±ª“Q´b(Q\Þ,îÚAüƒxŦOFUKC ³¬Æ p™›ó ´Àæ õÏY¦X}dÞNà߬Aa#)‡RØÀ°qˆjE¹¥#„2!Q!”A@>’#)6"â%T°Sù°¹‘%Ø<·ñåUÕ€üy¾Fýb­‡‹TT>˜@Ü´«ðAsûÀ÷‘EÝ€¦«ð“÷‘%™L  DßýÆáÍ×-ºI¤®îÆí®ºÐ,€º#)Év<½vZkL) Q‚#Ùh›KÜÄadn" bNSÞ,ù#<úhT ºŒV0DòA²–#)Þ*b"ˆ¤‚‚°ˆ`YªQ²H$‰ÝÁ[È–nT(3íî…yÄŠ‚öÅ Òk$hÖÛ&ÛEcY˜£Q¶Ò”ÖUQ±Zþžü·ïö©Šƒ° û €{A#)ç¯äˆz­AÝ}„5c=ûÆl¤k"FÄ| /¶1‡š¶ËJqvÐxtw0‹’'R"  vLÂ!øÀÿ¬!A$ ¿ÖnJÉ7$ !Ðé‘_MH(zmC6ë‚Õ@Y2šíùmUÚ¶þšŠ¢™­+HI²M%a…ŒZYH%±²Z,Ò6¤£j6&ml’’š5™l55¶‹j5lVÔ[jmf´I¶Q£S*V±lm&Ôja_¬ÏyGdH+uÁ¬È,"Á¹é«£xîÚï§×—ž£#•Ì«)ÈÜhÆ é\ÆV8d•7b4À¸ÔyRBAŒ²$Pd¶ £U¸6eÇHLèÄÚ#m1‘¥IvÕZ$ABåŠ#;ˆL e‘€¥¤¤Ù[¥²å»]4Ò­Ömªè-#)A!‚-v˜_$$PY#)*%,,B}łꙩbÚ¢#)"¨½¿mxT¼lº*éi®ë§vbáڗ髱z[«ÕÝKmãUÔÖ¥ªj[¢ÐF©°¤;€ãPÝ]U܇ÊÌ!0•Ÿ¼tî’'uOO[o»¿•Ÿ-R¨¥{#)ù‰*@Hõ…íÃôll„-|“ºü#<˜SGm‡ên°œÄ~i¤(b}öÎ4ß~*inD˜ÆÑ•¾ÐÁïŠhKh`FèŒÜži*!HÝ9?{§5/´â‘+f=1ý{I­ºˆrG 5s¾¢÷’×´%Ûù4~„z#âƒñí¥uËî”t…¹®˜ÜêîïyƺÖòË©SÎêsdíÙ·QuÜmt˜îíºeúΣm­ä1[š«•kš“[bÑ­Zé¶Öeªí·*빢ə§à=½M¯UxÞ]µÐ¡1î— ?–‰4iO )'³û•#)³5=¸ªgš~¬W&h„#¿×­ïu”ݔӅ¼eÌÓ¡þ éJ B[ˆ…F5?Ÿú‹3×÷íx¤†#<óB‰3©±^p•÷F·=§ƒAÏ`gØÈ¥0ªÜñþ ÓÞöƺ¡žt’®†{Å.Ðæ)cOÌRÆu÷Š–ÒäE‹õÌÕ.ÓÔ#)dÞäY-–qUa9à6k!vÐÚåµöÖˆOÇ{»ðk„#¾ X~”8Zá:P~8ýȾ|]Ÿœ‘͸(m ¶vá“¢¿ù)ËHã ¤#; ÌèRÄW+^”Ät])åÊéÉTOŸ³´l›Åg¢â¡×‚ßÁžÕ¹kCÅ¢‹Ç·N,~ze­f`Ö¯N|%™°Á×Ý;GåÚ÷2h֌♸¹}Ž®j3;)s€ÊfŠN½Ãž½öÁ´÷ß:•§§¹•|Ê̦¾y$WL3!3?ƒÎù9s®+ ¶ZL’8œ9vÙOmÞÙ0Ÿ/¶ïÒå1ð÷8«ì9ÖxmVŸåCÇ4@$C4#<~‘Ò•…]=3ˆLŠ­ê˜íD©(wtÚ˜¾Ý=§%¶WFå¨BÿÇ89ƒå°pÅóÜ,CSÇ`ñµjIèU׆˜ò¾ žZô¶#)Ùò<õk#)¹ 8ykRXuQrBå(§|e¨©PÚE‘Bnȸ9Ô'D„³¿ZµóÿEùi”<<èê‡L¶tä¯Ûr™èyønmw_ ¤ºmZ–¡Ô=Œ~¬,(`õ’(P/Á’]¶«§¸?tƒ‰)ŸoÇ¡Uºo„÷9ßFqÞ£¿G•jó‚ø•¦êrÉ®¥k#;OÎÝ´’1$(!NJƒ#°§ôÈÉIk]϶³ô¶#öÇh|c2ÍiŸECvguáCç‰~S¥¦×z¸GÓȃöxvs‹ëåÒB0› †,¢˜âºILu;½jÎ#;Ƈmá#ƒx  ’L#<÷]ü'<ž+%Þˆ‡Jßl×|UÈ¡B]W‚BB å¥Æ•µ¤çf7Ý«Î{íaâ.¸ÜFîæ¸¾9"—ë^°œ¥OxÍxCæñ2A‚=D‰¿K³%‰ <’/íµ^tzè“>ý£ÊÏXU| EË‹ƒ‹¾,Œ!ùaûu“w|Ѳh]7üòIK™úÅ"'Û»ùú硜­aû<®æ)]G/á>}‰ÔŠàjÁ0sVB±­u_g]Ís`öáÅÒéúÎÓàb‡uÔ¦“,Q°Ý oQdMÎÏ*tÃ×~Iñk¶$píB‰CKq¿­ÃMA#;®ò ³]#.2ß'9Ruè–Âvš© ¡ŸVb  UDP‰E `ç-sLñÃ~ŽVQ•¢è\æfZ½&ÛsyC^[,Xü•WÆÂIÊH!iheGX#)Çho÷wm{Ž´®NÒrÙxÎÒI%M»U„ £PÇ*8>‹F XIè,h C‘˜&¤á_“9$«åJ™Â'ï¨÷}gBm ¥îç~7hç[ÎôéeùBvòŸZæÞ\·ž“Õ#<sËΘè‡Â6U+΄<õŽj(Eªm)Š©¢iÈã“4Î ×#<] PiE£°mÇ*Ë;î}„S&©“Œz”Yíàw\bÙ00ÇbJ–6…²G|Xã«–3ï£L§^½¸ 7BgVHPiJ1/ìläBŸzê3²9]úxÛfº;Žl:½'wQUSVÖºa²WŸ’&>9Ó»ª}ûž=JkZkNÚ21D‘ª!‚±×ꤤÆ;ô}âEXªz¸ìï¶a#<Iç~%‰@½6#.éi<=fuSEPæø}åÌÛ˜âtÍqÅì^[)XÐ7ÙÚȧ¢ÇÄQŽiÎт2]›Ó#<ï‡OLMðƒã~«~äìºx¹ÝؽÌ÷x;c~á‡]òͺ4­òM’·UœŽ®cÅõ‚–#;@×òÆè¤%°^FˆÞ—‰ åÃïcžq¿iŽsÚ§ªñw6œs tŽ"ôú¶Çºl]¨¿½)Ý.+‡‘ÞîO'#;AÍÌÊ^/DBƼ0N4¹öÜpNíí ¹QRâ3a߯)ž‡eî·‹Æå«.åKi“™Š…™<²xœ¨¹”dF­KZUâQ#)Êô¹D=DÝ2 Oˆ´D4·–bÛay£ ‡yiæù0ظ$|:óœªgpw3…BÄH\‘#;ÖC1i¡6c å™#;î¡u 'ƒwèl=n>',Õ“¦:Kn8jtçA„aá˜Ö‹ko;iú¢n:á¸,õîNöEÈwzÄ,—ÄkŸCTˆr¡Î®›0‡™Ì‘º¼ÁCöË£1ÍÉäï²=x5Ò†ac©Þ× ôi¤å$ÍÏD²â1œf¨¢sõî[*‘ªº…CC^*<ËiíâgHuȳÝJ´›nvçECA™í.$Á¸Œ³8!u0Î%Ö)a»À #;«*T’s@àö&†)S…3£LÎRMnõ‡CAë8š­êƒ#)Ü:î³£i‘¸xz‰ñ˜åŸMm²û¹Ù,¨ïPÆÙ´5û¾Ìzcïë7j+3#"—ñ-ÄB Ö]˜ß‰A¼cR™/aåGFd¸°ÇèáusÐA¡<K7›ðe™,4kìê²Ûv5ý1;ÌcoÝü×ÙÒMS  ß!ããó‡Í%—“¶¾Òº”Õ*Óå×ü¬õuÕö-#<9qõ‰Çµ9Þ’DXD÷0®Àí9¯{ƒŸ ·Í.ŠŒEX³æª‚ÄIl#<@j‰>B4×ɪ(q#)Ø&Ĥ/önÏ«ßÆù7Î"Â. ¬a&£sIÅVYh̸ÀÊ6[1Š¥HÚ1ªnØ!§X·*ØÈ^0ÊCT 0’ÒÁd-!l /Rwk–#;²Òío7Wzí•© ˜‚hD@;æQ±$ÅmsVôÖkxÛmͬ®…£C»2‘d!Á#;è m63¨48-1 ˜E‰Ud(©a€S 'o!å–¸ç}™ëç5Ḵ›sJ"-¹°~Ž\Óž,»iLb#< }ž¼{ê½ü‘­"Fo}ñŒ/ÉÎà˜BØÚFèRÑ‘d „OzD$AR¢¢ÞT@¨Ô#)ÁÑa˜þ—fbÑ‚ãîÑ™¥U#ƒDÚ‹2}ôºû®±ãŽÛJ™AJ¯õ|ªZ""ƒ&%·#<±aµ0ÚƒFÛmæW3 Ü«º(°ƒD4ÿptmb›-WaF ®&A~J§Å›È”:fœhÀU˜†xeum”³Ÿ Æ0‰iÆa½aÆ›tªµÙ!ÆÞ'ؤØ'–‘ºrѾڎ=j몋‘5JG(ÞÆ#<”™?—½tÓÖ“cSˆ¯,PŽ‚%QÎZ—t!½#CLÙÑ •Ž5„[Fᔤ(U†A£pSC‹Hi4Ø15Z6óqmäÊ@,¶WoNvµ°›—O2­CdT¨­ux1ˆÐ× Z„Z›iر 4“Q†F¨g62í» ¨ÇâøÍT£äfڦ෿æ< o¯FqZé‘cS¿J„F&ˆêjŸÍ”e‚nq ÕoÊKŠœÊïWH˜K4ØÙÖaºa½ëÌÆ™#ˆF"ÆÜÈ­ŒhMêE†:Ý”peÄœ\÷ŒÒhQîÀÜ€ù<²˜õS$´Øñ¤›š$‰ËÓoÌ¥0ÙsavH#;5«+ƒOL׊m£EÐ>Ì+WYÇcLñw =‰fÇ›®üL›5ºœÃÆ—F,*g‹5`®fV&·xÑ3’¦³$*.ÉPÄ`Í5ŸPÆ–ž3`C hdj1H7×Yˆk,à/#`Μqœ’#;Œa·ç#l3#;êß#=}¸•Ê|©LÐÔN$„+ƒÖøýNO½ª”ÈÁR#(Ð²î¡Ø\»Ã=pc#)Ç©3ô’B OU4ŒHÀ‹!" #H¶)lNЇ°`Â%r©P¨”45¡'E¶Ÿ#XRðP:øO5©!”ÊCè;à\îU;êÆh6#;±‹MØREUD™'íõ ¾öJ{ ®ÕÈÞÊÊÅ—Nq˜&E^î!èó }DµŸJ¨'ðbnûµS(N4H0#$„!<Ý1RJ`¤˜Ž&×P5°Pœæ=¾ÁõèO 3óu%Xõ“w*¬p¤U)Âk}Ç&°`Ciq @¤ü|cëCM³*¥pyr™Ÿ.3{ž{s‘#;i½1´ÝÒww&Ó“»v‹ª™n žøÂK'lï[Þ\‹°:'š>‰càtžqóõ#;Ò*•œoÊŠÊjŽ…>Û€ýÇo—†FpîÞ3ÏHH5‡º&ç©Q ë$X’5  Œe[¥%]ªe+i†¶ËË®mbÚ5±bÛ¥ãZòkfV̉#A(ª  $_‚à#;ýÿ¾ÃZ'¡W¡¨£õo¯#<Ýñï¶1Ñò$‰"IÕI6“ZØlcHFHÄL”i¢i(Z¦VÂ[ɰm«”UVJ0Å6Tlb‘–…4Ù&”›%(Ó(„•¡-ˆ”‰hÌS4YM"•J0´“#;°Ãf)BI¡R’EDü†Æg‘o\2TÌ‹{áÖMÇ Ú†Á}ñ2:P|zçc6o? ëw‹ºæ€Ÿ ¯ÈÐõ˜ÚÛ¦Ù#;¡µæX¼T{¥1HÐ,~ò$‘‚Ci'£ÙMï‡‘Ææ9|0T£¤’ÒZ*EÂhìª#)b ;ï¹¹‡ÔfaªäHÓ7Ö&Ü8ذÜŽD2¤k¶ÿ?ß Õ &-¹#;¸û)) Ü”äLš2oZŸF‡+Ó}Û‚Àœ1#¸õÕHXÀ¢#;[¸f¿#Ï s8…GÅ;xx wcr*¦LEž{±MTÁ!âq˳ð>Ï—Ãt>}½ç7—8À½Tjš7ÚSȼ…}•kN—)½ÊÄÎùÙ˜mù#)õˆ=PB@B)5PV+HÊ#U&ªŠ?A­k¥iÊšˆ£±Œû.ÕÌÕKRòíU»°÷%›Eø"ÿµÇ7Ü+Û”\âT$Y R É#<=¾Ì‹fKQB˜>ê£úK‰°ø‡ R¢'aE¨kpÒ4X~Œ.‡ªÆŠ}..™WLE+Ü‘ÊOZíÛØ½Nî’œç0jxç³nöé²@´2À…°4f#„¤“ [Õ7¥·¯o·ŒtÞ—“!%ÙȽ¼íyš(¥5Ù·´¯ ”aFA%´ÁK÷PlÌf‰Mä¼#Æ#lŸeäfz À{ŒL´árRÍJ-Î*#ƒÛM ¨gh*1‚š0„¦¢Óp„ˆ/Æ[ŸEÖ¼Èô´æl´"û}‡ºÁ²'_œ=\ƒŒÊP{%;ñ¯rtN]yå™)áç>EuàÊsS!"U‘|K+Gž´Óõ0ö”ù.Á~fu#; b>#;ãðð„$/×ešó«Xç#,¢vˆ´-?«å1 žuxƒšKMgÑÓï„Ä>H4a~”:IDú~sÁPˆEA@‘‚¬L™ù}º˜áµ”YQdø°ñ#0x}/£3/‘¥û°œ,oeÒ1ryŽa¿ÑÛ/‹Ó4œ$AÐéaÓ1Ü)aþ?‡ñVa˜ý1Y¨L6O¢‚pwîéÍf%Ê®š,0!’c­ÙSTw¼ó@'“¼éhçA³8I#< m  *É#;1F acM ïÒµí£)´ZV## œâ‘ ÄR¹ÎÜžÍÌ€4Иث€@œçzï[™1´“P´¡04!LFD!ì†âÌÖ£#<Æ1B•lúAß-¤K½]ϧŒû›c}¶â–àüš\ ç¨gÍZ‡i09ÑJï&l„ÈÃÁ¼#<.µ™‚ZH¨™@œ“î¹­oœþ?ù¤òÛ¿‰—l뛪§vëžJ”Ìì.¹#)¶CåÁC¿žÛõRg –RŸOÓÀ9ç¡l‡ §ï}R°Ü¹%äÈÅ„Ð@ÒÜpÄá’l0ãÌòéƒF$t‰#;!0#;uMXØPƒ"™Å)/BV#)¬˜ €,"›6ÄŠÀ4ŵÐàSŽ3F¨Í)™JØÏm©&ž*T¥4ΦÑJêÞhï‚^#;²ƒÞcx曃NN–Ù¢D6%>ñ*¥°2f+‚@¤!lõ,b´À\™Ý§:btA@bà¹wÂÉYy/ÛŸ K†¨q8åL®`˜>U52% ÈÙ%Ì;ŒÀæ90Õ«ÒÒ†¡ô¡h¤;¶È“hÎ.YÂz ‰®qTäᛌßYÌF#8’àÍŒY`I»kd\°=æÉb±cÀN”ãœÔˆ±ã2Ò’·Îí6šÖ%V‰—0:ÇžØ0m.ôÛ=dx†¥ÉQ20eÃ|Ó˜‰d´à;J‹áÆ†È íë¦ÄHÂïJä(|#;¶Æq†›-L23lìPKfm3©§Sˆ,vC´½Ë§½Yu³˜…¶Ó§–²æPè¨yX’.]7ù«TL2kîï‹>eðÉÍâω¾X¬ *#<í¶LD­ã6©‡Š#wÌQ©|í.å)˪t ôqaß™¬G%œÌ3¤´š00¹âC”šE¶Ï”4£V½”D»Åñ-B®ŽòèyÄÕWJÝ…Še†î±â[ôGEbdÈšDô‡(X\õÅmA-0æPÕ§„$OEjq8ÍÙnaA·‘ic×]fZZ<”#<œRàiÈŠúæª!È·®ƒëhŒV,¬ØÑNf*Sëv̳bƒªîî\W]±¤o°‡¾‡&Ý&!@ù¬Å#<77žŠ„Í“ b·1Ó¨a¹:S¶EÍj©SŠçl›ªƒ@㬻™:>sl鎸Úƒ§5+#† œ8Ñ‘É;š7ÚÓ!?6¶ÊŽGJ½ÎÃ……¤xW+W boH·Y¹H&[ ¢²1 é¦äHi¹6H"ÄzJ’Ä. #e°nÓ1ÊìÔË‚Œ/®DËÏd3ÒœQ‡4:ºº½³M”kA}ÂjÝz1’¿ UÓ¾Ø3Mƒ‡f•R3;g&Æ1‹ïdY.™&.-ù¦Ðb]hÌm°Æ#<}œò`´rØs¨»™·G(“õÿ/Ò—¶Ë™ptÙQsGÇ\f`ôùlréžÄT$Àr›Üð^Ùy'ˬ¼XÀàʈJHP*N D¶êd³Q2J+#)Ù[°Ë;dÒ{Kj8v#; <úrÌäæF$ƒXЦêíÕJÂÌ •ÚÓ±çŽ,4Ë8'3DÁÄ3ƒ ¤ &bžT1*1(Ѧ#]ûï“Ø¼H³‡½#;•`¼yá£6µšF°ØàÍè­m|õ2\Á/emÈÏ¬áØ§3P‡Os\¶(p[Ä`y-èÔŽœ™%uEuz¬Q-Ö@yhg)‘À¸íÖ—]¯jvÁ³UË\³°â ò¡YÇbðÒl,6§”%6s½»ÞÞ¼óWÇ›|zK\G²†hv ]õF ´bõ­Ì¿.b:,;jj)g‰ªâÙ,¤é¦™ 1pÈ.)hPçA¸x¸7#°Ì’H«ŠHLMG8"f1³&ó&o0Ð4ÎI%%¸kžnЫ*dÀë…9û*£†Ù$Pm¨¡> 3$pcgxÇ®*”46ªY5ñÛ»fšqj꘾,´e²Œ0#@ÔXñ×`ÌÁ·Gf®bq…’#‘sQ¸\Ml¶D¹J–”R ®¦Cq¸hI²UBˆu9õ™-léarM¡±`§@ŠÆ®Â.rÎ&¾h¤b±ˆl!lRšºCMØ)¬Á¡ºÉPâp#;˜âfeЍ•°#;*Bš;°Çkº]t)lØ"&ÁÅbHÙhlL\â…Lªã¢Ù$»AˆÌA±rQ8샃¶Ì#;ƒäg<¶Æ+aQPS±³1 Òi´rpT)MISŽÝÕdd„jÂì©suSS!Ø›…H`Á8q†…Íž¤,¤V:%NQ’ܘ(2ç3#ÀÓAWE‚4“ÖÆšT l1%Z &šK0é®i+ŽŽ;)4Ä©-C4”R»*ÀÕ»Òœ뎽ÜqÕÀÅÔ‹€Pœ¨š t4À”}Æ¢b#;4È@]NÀ‘Î#‘ ÈȈŒ#<0Mƒ4,ˆ3bé^÷µ|DlTlš“[õi¶œŸGüO‘ŸckPeTD©Tôh·l°Æ˜ã>x#;áÉtäo>„GìQA‡¶¶z… ÒÜn5‹aæq²–7#§±é÷L+'ÚhóDy¬«eNÔ×O®V]ò$aóålJÞPÇïI“³HbÄCz‡)ýl\zê÷Ú”]6¯†?Xn 9G\„ˆP72êˆ5¢YašG>[ª¢7.Iõd.F›Ítú{úÔ@Úi¶®Jª}–¢/è‚pŒœ¹”¯u"²"ò'ê&¾jÂ^©’‚ŠFJBr¤àêq뵚 þx&™8…¼Yq%÷ܲÇyr¤N¬ ±g24Š­Tûl²M1Ývh¼­"ùÈšdÉß“µÛsÍZhñèïm™­(êD8‰Î0Àf -JüBâ ÝTÙ|õS±n‰^G#nuã#;¹£ê{Üò³Öq³E‘sw¦p‘¾ú³(xÀ#)ùIò¯\|TU í£.<_‰ô)ÔsË„‰•ft¾Í¬{Ñ$Sß#)°3눬†üœ¡Ðøé’ö‚>‰ lÍѪBŸÀ]8œ#‰Á¨˜ÒöŽé^òc5TÌâåq™#)iØ©c¬X0*ÚQ+·¤ðñÏݶ<³L ’P˜ñà ÈÒ2ÖÉ ÑT€ÛX0ƒarZVWZ´™†L)a@cxð”¥Ì̆f ‘ÌÅ1#)–AJ£44 Ä4Ò ÈADJ¢*ª`Pj˜­F‡ßIš¬Θðdm’YA¤:è[˜ª‰¬nàî‚*ž0ÅhK³! ÌÐÑùŽŠ¶¡2ª$@ùˆ@(ÌaÆz{{§{îðó„æ—|+)äZ–P6&»xR$¶ ½CØ ‘ƒ8m~âùŽjnM8ÊÕ½ {6´ÑS)‚lÈ4:HñÜÄW™˜ˆbÀl°ˆˆ¬Jì‘1 %0¯#;Ä7ÆÓx†´„ˆbd Š‹B#;öñÀ€gÛí ¨\2#<ÞØ,ŒáhÂÑžOzBÀú`ÆJxy£nM±›jHü¢ÊIñÒEýOøHp#;=|½I¡@ü‡¸ œ&×bˆ%»,0©ÅyI©ÇjFoeËÔŸ-V±K1#<†&#äâ[”3>Q§ ¦æ.Ɉ›…ø@˜!Þ ‚q{ªš j Ò¨øDöŒz•Ï«<ðy.äåH{þSEyw!”†ç<Ú¯|ÓÇ+Þ#ëË6Ù™| –Òl¡hÛ[oLa· É#)ØÇõq×›4ÚL’‘ŠÄOm#óšÎ8gL†½M(É+§i _„Õú˨4oÕmu©5ÍUùÚ­ÖÔIEªZ«)¨¤Öƪ,Ë_j[IkX«R’*†ŠB݈.4€ƒ€ˆZRHZ@Æ ÜR#)°’H*˜HŽ(DŠ„‰ê]Â!Ù#)ôE€<#)žþýž½”R©a’iý·Èr º–áèθó5`t‰Ù²çt) ˆ‘dY"ÁD@›ãüCÝÏ›é#<Å0#)bÞÐI!ㄱ˜§êV)dDŒ RA@lðÂÛ{"õ<0Qtø"­S0,’E(o¢#)2üä—‰îF¼3Ì«‰'é0é— †³Ø´š61Â! Iû¼ÒlÐ÷Ê|„h¶Ф¤ŒlQ %Q´“h¬Ì1’áM™™#<`ÛLÈ2ÇQj¤nÉ H‰EI`&%i¸4®dB–#;µ ±ª„†Ö8ÁÄÒ1 jŠ4`ÄT…*#m!RZðccÓslíÅ£kwv·ŠÕxÚMVJ¶ÍåÓÍÆ¹ä¨µkA€7Äê1¬X‚2:Ö l*;pä¤Ìॠ@ndY€æikO¹P ûƒ±ê+ÕÅêŒIz±îë÷{èĬÕ\§Z:#sˆdsN2¹ÄÑFOË8u1®ªËÔíëlã¸w¹ b@Œ€ s^¼4B^š„»äQö4YïTY¾†$8¬ºE‘GZ…!((ŽQ·Úh/#;ŒoJ92‚ÓóŽÀo÷ƒnïÂl±#)\#)8´#u@G"RO“Û¦™&+ôØoõvö* §be»ÛŠ`2"“>‚6O§\ºƒ ©Û´J–ž<®—’#)!@ €‡ÄS¼Oyð¸*ddP#)ȨH¢ó@«$#)¢—'dìq’4n+犢k}àáÏСUan76\2ðDš–—¨ P|¹ò¦ÚeÃÛOÓ`µ)m¢j‡F0ìî$‘…#;¸òoXk妓»r2'$m1ÙIeÌÝ»h\RÁ—€Åà]j6;39 Õ íEƒ·ÀƒàX ‘˜Lk\/9WŒÔíDZ²H#;ƒ.´š•¦”•¶ ÄŽ- ˆ£0×”ôJàEK„¤&‘¬F·½´LßzwdÃ#;QÀAB¬’R Zexh‚Ó¢&Šx ç¡Ê#1(*ck}Þ¸©Àò@=!"Ä!'€ZÔàÁM˜¾‘”ÊÈdå(Öƒ¥ZÐÀø½-inµ‡' ~&e~Æ]“d]S“÷#<`@6$°Þ·ˆ6K*½aÞ[Ág‡Z¡»þ@ÿ[@À‚/ꑊÂf«eM6½Ö¤ø|µ?U4™~_¸[ðñóóº³Í“Ð]3hˆo@è˜@@‘ÑŠ©±D#) ÁåË¥Ž?#ͰC螢=p[z9¼§­êÏŠZdmÁ ö£c‘Á†²j¨)øy¿_7ìÀ¡ˆƒ 4 °„°´ƒJ‚M ÊXk¾ç;oôþ‘û ;)F6½üq(vXPì5(3=™ÜÃE®"0Fä5‘Ìàä yǤ-½ÞíÃ®åˆ ãD,læõâ‹ðŠé#)žÍáÀt¦dªiŸ}¨Úœ#)­S”Ì#)G#)À k®Œ½ô˜lúíóÙC]¯:/¥mÀíh-#<<àànƒò€ï Šè;bB¼”{)³e‰Ý·b _772‰ !$2c2zë¨Ñ£4™wWF$ÆÎ—NÓ>}å‘úÝֽʉâIYª[ ÒM>šÆ•ROî”9ݸb½]Å÷vûÈ£3Q¶Mi±¬Ñ´À$ €œõrw(&}²!I±!QiHÔkB–dÚÉJc_Ô~“L×j}½WÕ¿.AFˆcE›EQµÊY²Ú¯Ãªv‰){ׄO¨€"«h §Û K[”=ƒ2.¤.§œ‰Î$%Å8E]ôI]Ù74jøÊõ¦î¹²Å6,[ÉŬÆÚ(ÑhÉLŒ•“+M)-š#<&õë¼[i4$!À‚§u(–€$^"ˆ^ÝSgû~{éùÿiwgy$è¯A5Ô8…=éë=~;m1òÌ©²ä,#Ç/«vA 1fÙ}Ý[Ú‰Ú#)xH!ÜÀr}¤!ÚêuÕ›2EÔò=`¾wùB5>™ýŒÊbHr‹ÑƼÇý« ÒÉ”N¸—!sU)#<%¢»#<#<ŠP€Ü?2åáÁù`-€aRÒ´M^ˆ¦¢0>ìi=°S$S±=ÊÔ ëE€UÑv¶#<"¤›\ÜõÝeª›jM(Õ3Ê•ƒ&›$dAO¬@0|MÎ87Òu„'“IxtŒ…Î̱‘kzf2DÈ &õ#)Ýý0‘ aZ€ùïЂT§ÓLˆ€Cßk$²ú6™k·Â|xRÔw_1€Ÿ–Úõ°Ûõ0Ö^œÒÀŽn&= V…³«[âæ€ççBrƒk÷XÐö M:ZËZdƒŒâß30äÆò™é“à»÷â´3å}ÆéÄÖ™Ã"Û™š!æMp±ä‚‡Ì'¦~õ° ’› ÆR¥SA$€H¤T¥šT•&ifl¥¨ÅEÅ6©6ØSF‚ükWî–¼’ÂÅhÚ’¦aX0!þìhãÞtñí³¶Y¤„m‚†caKX¤ªa=BÜ« }w|"Æ*b·Üã³5m›{å×éõÛ”Zwj”8ê)ê|Óìâ…3N+½ÝÅ+(”2#ÌT´–‹M1KoäððK%Û2Ä6(à°²1:46ÄåYKQc‚ïþJ#;ÅI8A O,WWMã­ô-Öi54R¥½f»iÝ–»»+ƼÞk]Sh²kzUÉQo7u™»3*ºæí¨«¨e²,µywcM·wWw[I²¤©‘)±­dPÑ`Ea"Jœ”« #šL¥´fŠYø$Ë‚Š/|˜&ñŒPÀ`è˜v`ÊŽß«ÂóhàñAã›™—#ä]ßÜrþ;‚(IvàG·Ù±=¹‡¡Œ[¡`W=½ØÀ¤ÞOA¶³‹QÆlêccż·âBÝÚñÏ'Çöº©Ý45U*qt¾£:ż8[Þð4Æš+ùx™˜zä)Øaõm䈃"ÀcW(£&ÎkÜÌ¥“ÔiÂdÂTáüß"#)cAˆ*°M%Ò˜F‚¨_^˜á‡&„D5Ýø˜ï´’þò+ CB†ñ_ÀUô:}d­ªž™Áå¿}ˆi O.  ò¡BØRÕ•éc6ü:¿:¬[3~Ìš(uKFÑJŽøm­Tì|Mœ(€}66s9›/ÐÖ5Ó"øgètóa7T“¹!÷hiµ^Ñ(ñ¦êÉB‘=P´ú¥‹0,#;TÝhA²‚•¤êe°TR:ì921ƒðcÔcp@ÑØÂ!»v¬À!FòƒÌè¶2Éïú€½Ï’=¾(¤É#„I•Ä8B Љ¦n!ÛtÕvÛµJZí‘\n·v&Âû•ê;NzÁí%BEŽaJVGÆ–‹º&áõ[‰]|Oj„Ì„†hÖµ Âf̆y>4h!¤ÔÊ'#;±Žt>„ëú|Kóý‘ýMíì›pÕ‚T?f°ˆ’+ñÑÑäQO©4¿Ï˜U#;‘É–õP9Ã;–C#)zfßYhBÆî§¶ä<ùÑ“H€<¤¥E’=;?£î{£9ªÐ0ãŒgX1ñ½8¬#㊸8ßÑEY“˜FNQÂ}y‡=D@f¿@ˆŒPû¿ÍþíïÿÓ?õÿîÿ«þoø«ÿÿŸíÿùðgŸoþÿ³þßöÿÇû_ƒòøýÿ'ƒE¾¿þÞªG§ýŸÛþˆ§Ãý—û¿Õþ¿ú?÷ÕÍÿïþÞ_ÿ6Ü#ýŸêêþ_Ùùücû?oöuý¿èþÖÿoÿÙ¿û/óÿ“üÿîÿòÿú~ÿ§ý<Ÿ÷ßþ¿åüòÿÁÿ÷ÿÇüü–ÿþŸíÿ‡ÿßüÿãþÉÿ¯íÿŸðû¿¯ü;¼ÿҠܣü„Àæ,Gïþ¡Úˆ ¿&QÛÌ,pÿ‡ô‰"ªp"µÞEÃèr5˜?spL¿Ams1P @ÿž2Ñ#;B}¨CðU`˜#<¿YÿZ?çªj¨‰$ 33çmôj›kí«]ö|¨6TÕcIqÜ F;Ž6é’aÊÇe4(³#žÐõ\ŒöºÀqH×v«ã¶ÍÜ4njƱ¦†AP{a÷8öôÎ’*8É9¼:ù>Ë8o‰†âÀáÆ¸*ƒ7`ÚÙx0*-&‰¤tËIcá“׉5v4Óêm˜&S³aKo7žÏ#;V8f`ԛ͢‹ˆsOBá„;eƉ#<ñâthèùglœof+U—Á#;ŸY|£ÿ3^³ ¹;)a²…qHf˜úF#‘CÝï…ßÊðŽáLkcTQi&Š]ÿæzøúçºé$xC'ØŸl±LÖÌ(V#< z³R #‡ç"šE?ëûq[÷U7E[ÌAKbÉÐ÷\Jj€!ðëú#)ùïÆ½}w-$E¢ŸáÑ_ªXôÛêú/Änë©‹çQ­ü—ÌÆ9•søuÞuñìI¬#ïGqëìôŒóÿ¶xáãÇ9áù>rM7½ÆA?H»Ã$tå’Ærÿý#ù@Z>G³•dz8èÒ½&áFòãñw‰`ÙÒà #5šï$ו@::¢h,1ªÔ±TèLÑ0f&]—.S`¨—ôwÖþ¬º*ì6LBR³<ÍSawŠä%‡5Õum°l¶pȘŖÊLʇM3Å¡pH‹Jfï@¼C,@D¦…“"C ö&’hQvA®3ik¿uîY!§"Œõf€Ý“pÀ#<Âv @4ø$8´#<#)$‡½!˧¤áFëÅžÎpž¨C‚S'Åmõ5Ùc[ÓÓY·Ü[š“hù6ܬVŒW¶öQ[ÝmoSêåjM‚Ú¾­rÚ½þ9£#;2ew+ë¸ RõP#bzƒV#;ð0‡#<«µàäXMláBÒ(š#;’Y ( Ÿ,X6Ü_øˆdC—J#;‚Ƚ$€äeÝ¢…Š{Å“k\; Œ#™†êB"YÐ#<,ŽÂ­Ùlj&¸$1ÜQF^<Um›M§÷D¡æ>ˆûñCíÏ‹±xÐk$Y#)‘›Ç5JülñòûYG^Ç­êø=Í`A  ‰þû#) ä¡èçØˆñU`@ˆ@kµçâú+]mr¥ªÌ¶¦l×4KDˆ0Ct±A»¨–‚I"$¢äÇÝð§êMðôÙö=|#<  #)_\ù¨‡¯ÉÙÒD£ ôk´?$æîâˆi?Ùê©Ï ¹‚| ü Xƒ"þ>ïƒòî9ò¼Ý»ñÙˆH ŒŠHŽEl°¦§² ׈œ€öÂÂÿƒÅ(ã'Š#>!Ê¿®Áðyù†¿\iùB®‘ÄÊÚ*#)=#äP÷ÈJRh#¢îm), ´0þOöêÉ?Raž‡×ǬyiVd2ðuHrfÉ75دuHýÛm3¿ù­£úÑD©µì\¡]b:;¿ßìâùÃ*€÷÷³ðh5ÅÔ¤ƒýÑ(þš”ûQk8+·‰ÅQÎ ¿#;W Œ³˜50ï‘Ä>?§NÚ gA••ëç,Má†/-„±¿ ºØ¬ÜI@±¥½j»îÎÓeºàNÊO]Ï7uy¶í&Y¨{obÊŸû&f]j‡Ì“$%ãp€ÇN~(úk×É犪¿>|¹ïý§#<_ÊÃåÄZ>A¶ýfÓ_Íûö*¡þ.äŠp¡!®†sf +#BZh91AY&SY†$¡ƒhÑÿÿ´ðÿÿÿÿÿÿÿÿÿÿÿ€ƒ„ 0#$˜(b ¼÷‡¸ä#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$¾Þ}:æÙ¨ÌÚÈS]5v MB€û·lºöîÓf[5ÙŽ#-©&ÛfFû»‹ïg¼/µ•Ýk"ë®ÕÝôióx}÷}¼F÷šv⼸Iº-Þîì7»½^áT:¹×.Þ÷×|{ì®™ëG>ž:²zêéæ×xn9ôw]Û„° zÓ¶uïžûào°5}½ñd¦íæ÷¾ÂëÜ*ûï—€#$#$#$ ™š#$øG£€'ÑÒ¤úð#$ìÛE·§©ìÝì=µë´ìÒšh#$A›QÕô:t”íÑ]*m®P¡¶)¶ ³®HIR¨¡*—½ÜP(ô(vÁ#$ 4uÖÚ¯o®×¯›#,ßIƯRK½×wwF’Í×$ªŽØµ‰4Ó[WÛW³d}Ý îõŸwSÞ¾÷Ù‰][Ÿ[}»·žuõ{;wžôöûí§{§-z÷ßíîtäê>úïM÷_]nëµéÞQÝÑ{ÞyG t5R Ö#$•#$½0t+TÑváŽì‹uß}Ι#$ ¥yo¼äzÃB™E¶{ºòð#$PT#-DˆNÁëØ:f`Í´³×·¸õÙÍÛkJwßÝv÷¾{â÷Þ¼\ÍUÌ@0’×ÜÊë´ç;kwÆñè{î¾ÇÏžu_”z{µ½·P;¾ùæûÞœâ®íÚy¼z{«í^é»Ñî#-hRzÓï}ãÅÓm²1ñTÖ¹®ï ¼æùíõ¼«wnus·w®Ûo{Ý#ÛO·5êI;Î.ã:ÝíºÓÍÂuË·Í×»¾ò²([ÝÈu[·#-ïKº÷³Í¾{³á=Ù#¤{méO»z®»×nrîîyå#-ÙZ[sÛWžîù÷›×¹=·w}Ô{ìîÞ¶\Xû{³íonÝŠúê«Û{-Ü™§CîòSÝgÅÝÀ÷¬Ï¯w®+îûØ-½åíªðz€’€TJ¥#-!)@qg€íÄ*®•vî·v΃ê÷½/PO§ß3^M+:ú·Gu¡K¹5=¼=좻¯{sº(p[;L׳uà#$#,½n¬@U˜w¹÷³½µwÎî¦2“Ü}ìåw½'£6îÏ=ÊŸ5{-͸ܴ‚oLº4§Í|œÞ{ÞôƒwW5ÇH Þ¸.z×¶w–öûç»Û{Ìõ{à U@ï±÷[n÷ µ%ìÞïN—š§Óï«mèõ­|}¼ÕÐç¶â>ïqÛ»o½m·Ÿlú½÷¼¾7Šì>®ãêïwžîîW>!§²³Iç:ûqï …e6¦轺õ¾ûÊ|úØ;s8öÁ8^&éz6¶Ç ïžû÷ÕãÞû— ZTú0UQî{olÛï½æ÷0lèàû·¾j!ßôÕíÙ¾=öÇzå|ûÃ}îé÷™Ok6möëM†÷¾ÎÛž«ëCF€t#$>óÜocO[mî âÞæ)¯£ëçÜØtуPÎìϽw—œõlEèYŽ^îu ÷#-‡&nò½½í–>úÝ÷=àØU#-®î¶-Ø={¾ïrØjúÞû¾{q¹ÝÚì÷]ÂúÓ×y×7YnÀvÒg{iïgT¬ÝÝÜ.·fúã½g5w对¶™ßo—¥º^ïh¾íuâ}æ]u»ç¬ÙQ£ëׯ·µ¸4m;Ûïc^÷½ÝõÎûåc“måï·Ò:#-øîîñzïxÜôsîsøíBSH#$ #$#@d#$LF˜€M¨ôž¦MHŒ@Ó2ž ”Ð!@€@@ ¦‰ê#,O&¦žPÚš4Ñèš2hÐ#$#$#$#$A" €šÐ¥<ÓE<ši=T~›TõO5O5OÕSdò£OPõ=OÕ#$#$#$#$#$#$“Õ(¤MLš˜šdÚ©á53Õ?SÒe=CÔz€È#$hõ#$@¨#$#$#$#$#$‰!h#$€É0™#$&¤ÒžddÓhŠ~“5L‡¨‡¨h#$hMDA#$ ¡ &š#,Tý¢jÐÒzSÄjh44=54Ð#$ #$Óÿéû5VºâIþô[jëóÕkÍÓ_UV»¢@E ¬‚DL€E(ˆ‚*z”ÀB?Iøqø_ÔÿUÏÔM½ó¯|»PËæÜ³^·4š E2"÷Ï|¯W½Ôï§›Èå}¶X­€pp†ù¦0Q`¤whSšú5äÑ⸿S«”–ÄdÄ*>ïù+(v¼˜­DËͱ)„”Ú‹'•ö9‡2ŠŸòzîÈ)®õä¬ `¤o"u ”áøiWŠQfÃ#,ÒÇ“ºK`A¢ aŒÜÉï>¯öëÓ¡ÊæúJœ¨Cªmû»CÀÛâÄÕªÈÆ&Úz–B¦ãœº.â™\Û¥ÔUͺyÜhبŸŽëé5¼Z4EŒžú âŸ-<ÒR(1#,šE’E”Æ6ÿíµÆF£ožÐ aß¾a½§‹¨Rh˜rÆê ªÅ„óº£0è›nRmÝrÆúûêú¹íÝÚå|¾;Éx®o’¹£Pq5×+¼îË)¡#-Jb'#$™í¿¨QMs‡B´±ˆ‹m1=lg·gÿþéx6J'P¨¡Š*DP:9¶†xñÆŽ¹5jÅ‚¾Ú)G؆Ÿ£1µ?'»x ‹¿“®o©¯¿òöð5#-“Þ×úªö•¯¯¶RjÀY±žuóÄ9 c®kå‹ùmø ™¢*ê…°³í©]w¹}£êã÷ÜÇ#-û:7†.Z=Í{jtÜ:5_¡Û^?–y—)Ä® ·G”œYÚš2pd—νW\–fTaVÚB4p}. æLè®ìëVðÜ¥y œiÙ»[§&Ÿ©…$E²$9E‘±#-©ºgF ¤Ñ£B”´¡S¢Óp{àK¨>»)Û!¶›Q Œðq¶¬#iˆãNwµúqSª¹Hº&©eUO¯pi#M§’+ìÊŠûCW—XYcÔ·ý~ñ‹zéõ¡™1Këë¯Öê…Ž‡Â‚Ä7Ím‰Z¾(Œ(UFx¡ÄZXÇ-&;ªUí¥`òeká]Y)ª”*EŠ "3 ¤ˆÀY†@¦H~_]XžUKÁ FCD)b£öïáýºQ#,PJ^u ¨`û~ŸÐókÖÓ2c)¨>x•ªÂW#- ƒŒQë ó_oð»,z¸%5ÍFÛyßn÷ÛÈ”«&Ãåw,j+ù°”hÔ_J)bÈ(,ñiFs£]±-^‹EI·ØüMâ’ÑD›4*¢ÁI¤ªuOUê!ìÅv齘O’TãYal”Ó†Ô£øT4æPaŒLÖ¶jϵ„Â!SCê¢JŒTdˆÈ õjÉBmU¦Ï{/#,³E®Q¢ß7¼êäåӵŴ‰ÛT4]êäñ£ÁÆÉE2„R]U¥"½;êÖFþ5jxiK¾¾S“£/^ë,°œŠ0Ø¢…7a°…¦Í$%;Kçu«°±ò£d±†ìâ–ɳ&¬»î‡à(*‚Œ£ö7fÚ^¦¥é ,†ҨÍ%uëah¨±;’™êe(ˆó@Òè좀NUOGÜߪÓy;‰ücˆ5‹ÊÂm.+QÅCåR¦Ý‹˜D¾Ï÷¡ôg…D9z§‡01wæë 8•§}J»aÍ«2Ã<,°"€"Æ%‘¥7ìw3¾'šn»\Ä4Ò“þë7¿Z—Õé9ö‰OçYàÑè’yά0h”UIB#àÐ"siM«íªþFï+>8£€ó²»Ò¹øæþ9(FÊØÚÝ* mç¾RK‘,nHÃri¢Yð°û2ÝaŒ[³S¹úS]ÞeåÛúD˲Â4˜Â#,>?49yÌ=¤|{“گʮïÝÃô5ŒÑê¾ò«#- ¬D`΋‚¯SÒ.¹)ur_4ê…™ž$ï™ÄŸ?gHý<¸Íh îè˜!jÂ3ÚÀO6½ç½…]’mAl(€ôˆ:äf©l>­Ø¼YFËÐùé©!ó&:ΤÄ3¥õN”8žrâø¼"½>îsu{êG¼ z27W3†\2¶ç†´zmï.2¹>ÕHÐþ©Œ²=vÃÀoÏ­Æõ#-«QžU‹);ê×eCšP“B¨ÿµôÝö¦DÚIûÇÝ Û?º]\tÎ¥">ϯ““Oá­'‹¢3êA8é„&XâFršºî7CìkËœèÄa¸{¬²ÀpŒ®'!¾Ü¨É”4è=G^ªö×ff}-£—óÚ¦pÆ)ãÏ«`WVm?—yz ˜ÙK¹J±Éë»uëdÑßVž)gÒ’–jp˜ü¢>9Åî› ¸áÒÖ«ƒº*#,š“M#,ǨÅÚ2µîvî„ca¹ GsO%£ÓNJI ÿmÁ¹Ö»ÓJ\?#-’Ibˆûy…O^”¤~5ëM auFDÜœ„g —NŽ™»Ð“;X]¹ŠèiÃ?ÁýµHÄnÿaÇL— ôÓ‰7ŠÓ“´àׇ-±è9†¥ù8xMhÿ¯Eî³&Ç×®«VA1ä[/Û÷Eö³_Nücpëg3WßMÆþ¼+ùHl†ªEMÒ}’}=ý>}l<¸} AXA– »B”:¶ÂûZPPyÕ"²£QäÊX€§e$Qš%#-a£>»—âеTµEàhAQX>Wýw_{÷Äëiòûû¯ZûZWÜÃæ–ÅEIð¢­QTYUÏ“=b}ý¦oîúiVúTx]Í*Žä5û•ŸSú¹Ïʨ<‘çÆÐk£Õ‘Çy¨¬ÛA ßgZ>»Þë4QÍ(PzT8ëhÝ”©½*"‹EkéŽ\«:'?Ù­<*š#,°:‘-<Rå” ¬MÙÑ•bùÆ:ùè]öÊƶ& :F‘÷Åc³~EBÁ4Θڞ°ù'ûÿ.Æ?+,›ŠÕÙ¾ / ÄÒ­BA¸H yÓŽlÏYóô_Šî~#,k4ŽõI=ìÆ<9Sû²eøT¤|ë)hž¤8\Äw_S8pC!!2-)päÖ"Ñ®Œì|ÿ“…éÝs»Î¹ßŽsBL9Œ!eå1‘Òg&¼}kquwi¼$c³´0R|ÈgÕL….è:¤*!šãú|MX(EBåv¯©±§ó/5½W‘¤1ŠÅ)¢$@Áj^ #>4ó…Ç#,æ×‡œ °T-íV÷ÍË‚ðÂì°ÿkZ[aÆ‘Õù!µû®±\²Vƒ+Zž5ú_*yö˜Ïóå½ÝßÙÌÞ£’W~¬7Ò/j­)—P¸É9ü”¡(M°Œ/4=>ü9¿ÚH WÒ㣺M)’gópž¸xb’ô{é´Qnη[rí)´§aÉAç³ì&¸­u¡íÑ+ã`\;·éÝÌçõÆU‹æ‡óq³¯ŒJ%.÷ðáZ´ô¨Œ²xëZËÏž\ùscË÷dâ=kÑ<³è‰USor²ÈÀÅÉΕ‚µd6¯UNS'Ù|c\{FW.3ªk¼8äwN;–³#¶É×Ñ L„%Tcº,èù>)íÿÑ #6;•½wqa ¡µþ·®ŸÓTäÆÅœKm5=¬[âXáóÝ?&vâs"œs×zmãáÍmI¾ÙRè#$IMÞ´Ž›wc„ÆÜ¥pêr0‘תX ÐÀ.ßÉ#$>^3Ê3c®4äD¡c"bjÑŽ5•çªtθ&÷JËÃEëT*wg§}ôªŠ˜">ÐÏéÞ™Ni×ö¸’R#-»ù§(Îc:Ýzª‡«õ²mŠu¡V±FzèU¦—ÆG­•ÝÛk§¢‹ÒÁæZ_o#-¼s ïX½¬£J¥|3ê¿¥‚åèÜwpîÎoΜ7T’=¦öÇhã˜Æ÷½8­â4ð‚VSZdßÉR)9:sIåºÁèˆû'Uy‚ã{’âH=µƒ#,ùÝèöíãsx±-¼kÁÈ#¹¤Ù׆,•®,ÀÁ^¹¹zÿGÏ‘¥,Ö#-ŒX‹?ætͽ‰UQ9c:ªLŠº]òçÖƒ*}õ‡‚ièœ ìƒdBHáCÖš†º9v „)li¦Ú½í©Ê#Zšh²d!~Ï/¦•­?Ê¡iÕo’pæ!¥jçõxÂÛƒ£#5Ö” ü±òà/ö©ýs âE—¯Yù£õËkë¶:Ð~¨Ë@¿jur8pŒ#Š¥ûês[û\âvÔ¢\u¢¹´PÍÛXŸŒlðâ3™ƒmï—™}…fÇjIzKqŠãe¬Eg©=Zkš—ñh·:…,'é`V†Ã\šó‡j}X÷CõýPâæjÑsÊäAíFi£¹A_NR:o†\èѦ€öìoÙÎäéU ýàŽ»8@+øí3ªx]•–ï7)'DÖe¹«Hꋪ[9mAÇùöm¶°ýO#$C‚n™¶‹Tf´(±X‡õ¦0b¥]K»â¬#,WBnjz%3yÁ9:³,+ðk ü:fÏu©ƒk¤‘3«E+m"q'„3#,ºÌÅ 5P$ ]ž/Y'¢¤ §«ŸÁ4 ç0ÑÇ”%Îõk¬ô˯š]#-12NŒü ŒÇîšÌDmyµÎ´9Tv-£b‰™埪·tm>_#,¿hæ2´l7ŽfA‡‹…’Oh2ˆU·ü0ÌÊŒL-üÍá)Çå.Sˆ¬Õ_½0^€ðj#,Dæ¤GWg‹Y¬¬b¶#賆˜˜Ö¢PlP17žÿ3Þ=ê¾.ÁuJVTÕã·væñ;\Š\u]ruÖ^}»"ªÅKZb7K¯åßg ä#,>ŸÃs<¼™G‘‰.+¡{#áÙ’ñ§I%­ÕO·)xêvêÐ*›Ý¾)}ϰÿ®º˜f²£_ÝÂöHrÙJÃî#ú#$¬ôñç§‘žSy«CÆvNçº#,%ùáóû>Ö®þù틚Ýt9½y÷¾ê”=z4A"¿¢Ô¶DO_o¼Îªˆ©ëƒˆ/ö@ß6™ÆÙVHsx·º¢:øŒ©Ll›6ø'dЃgÙC9Tc8ÀãVÑ03eÁLné)VnçÍYÞŨã#z¤F¿£NuÜ[}›$œî)t¬Á˹…燽Ÿeu´6P-ÓÖ?\6ÈÙwÓ ór¬œ½4Ú$E5×=t‡Å²Xd]ÈQÛëSò;GÉ‹r_-¾‡«C¾#-C»‚Ÿåñk•V>C’ÈOß#,Ê•Àx#D(ýÐß®•¾"¼º¬(¬ØìĤ°¥#(OñÄRuëT·ô¯øŽv”šnÿâZTŽ?¶á‰yÿÐy­-|éÇs*!w0¸CU‘çö™ÚÇ$‚åÜZ‡>Ÿ{¥ÛœÄü]¶›Ž"ë¥>N?¢[HÎÏv½Ùí)ðÎ;9z±A’8¾˜Dƒ|,tcßl|*!"·€Rç£DŒv§t¶•@á“’WêæUÃtWjáýÜqÞ1¦žüÝöó˜/ÁÚÈü¹\륣)„àö!«&õ_{˜ÑXDFP‚‘²tëJtíåM²0ÓfïÁ¼¦¢½g_åwŠ€R¤VfF›~9»ÏmpuL±tî¨\ë\S8 li;6oœêT vv}rïåßö‡Y’òßRâ÷éÑÝ©õÞq统]ß¼Z¾ß)qH{™1úiFdÿ‰ôÛͼK•1&YAÑü£#-9Ôù 83…š¥{`‚—9v£mûZ°és³yPNøôÿÕqf¨þ[êÞ͇³ß®Öºýv>3´¾Ýz1œ.º¿ÃZ\z>R‰éŠÿ°-‘åX¯:8ÎûKþÒ'§dëÑÌuvÅÀZ9_aX9ñ2¼>Ë¥;zy`u¬ï^·#,XNźiXRí^7ÍyÔgî×(ʹâŠFÉC› k#,Öè&÷E\²Ð¡ƒèÜ´nûç¢ny5h0ÙZˆðžœ!¤}ê‘¶lŒ¶¼¬œMÃÛfþQcãLð˜ô:Uïé•KÄQŸðzþyK©äEÎLÇ׿??\ò[×.Nÿ?Èò5%|ËÛ¸¹›¸óTMÕ b×bÏ~;Þ¢ºÇg@ÊhÑöF.rUUÚûñ#íÐCFvøJɾY;]qȉÜî(9ó¦Û â'î[c¿Ý±ïŒ» n/6Š‚|\þèå3wÇ~³‹G‚4+Fåk[Ëó‡‚?ë#-78O_ûáããæÍc D^Ù~"Æ{t»?ºè¸ëòhñùó°Ù2ÌåƒC¬ñXßrwñˆÉas/‡8ç~—LUÆ#$Åݤ8}BLS—Ãü· ‘$2Âiÿ~¨ÑílŸ'XÝGãð·m­.‹ÓA¨/ÊC’þ;ZÎzåÉ¡t ¦§+‚ªÚmo­»Bú[ç剿n6µPæ×#-—'»o›ÙñZu{6œoê™Éðçwï=vcCÞ›³îÙ¶=7*†ä§šNé`2#$²;‰}´5€1SÄa¯@„$2ffd v~̲æýwŒØYÇ ýÏD°?œ£_voÉTê8Q¡TÌÆÂ::ÉȵâB †cþUþQ.!aÂ0×ÈÉZQ‰k0Âܱ__ôã#~nóc‡ïkL±lbç•$¢ŒŠ"~gÎ"0#$Ïß½^æZB¼«û?a¡ãó׉$ÇCj>~œ•øû8ûíÏå¤?mV”ª»%£ÿWÊü(‡ÔGÖ0#$5‚»¡ÝÌøýÙÉÖÞÕÊ ×FÚ–_#,ªeŠÌŠ{ª’${ýiü~«í`1ô+?ê)з6œ|±`fgÅ´q$ô굩Ù_ô×,gÃçÚ}?¢F<.·3ý/ùyðW:¬õg¹M÷5ùÇB5‘¦ú~xQ¥eUÿUÏüt‡÷#Q–Êã¶ÝÖœ|ÿ/ ÷áiœþ³–8ñzÛ;}Ïîþ>˜Å¦ÉØ_&,Õ~E ëȽ²7ü}¾ÝuøxÑuÅ4¸‚;ÚüåVÉËÓ~6µr‘#,¸}Gë㟋ÆqâZÆMÒˆªÏiàoÇñWž‰¯[%tSþÈ>ûýܳ¨¤ÆÌf4ËXHSsIœg)’?àüx°êN\…¿\vßgÝUˆÇÓ”QñÅv L’hw¸œü3õÙ¿(ôÄ*†ï\·ê#}>ÂûúñÉ^¤IR-rB`›m2HºßÙÐÙ­å_Å‘,ÿ‹zØúýþ¯«E"&“— †³‰Ï¨j¬k ü ø™«Ê6‚9eÙiòZ÷<þ>ù…Ð8ÐÐhZqø™G¬·í¼¦@…¾yÌ­­¤”Ãy.*¡#ö}¼ç_ ðÇm»ÃRH7¯þ¯3ÎlÜ ŒSѼ»åþ-#-!ÍÊrfžülŒá'ïñãóVÔÌ¿„4 e݈K¥ ?³2ÍàõøUËôÀ÷š‘¥Üï¸}”m«$ÀôÖÙöЦ3øÏ°¶aõ“ãâí"eü“RåÏ>#,Û¡se«KpÎö¿›_½ÅÑZðv%6g#{`,d’·>¯Â,§Ã´ùâOti6,##,=ú}ø7ZôËk[ÁÊ'7#-NsÖCXhüèÚK yõ6߯Ùl:]Ö%lîlîÖozËóþç­Ÿ·I3\q÷RéŠiû"ÕnG÷:p-§ñ®ÝàÐêÊpmö/²Û3Ý`Z𥷒ì¿L#,v\m?Ó\£Êã…Ù|3µø;L|™>ËÖi#$WÚ¤€‰^ê¹j±áÏã0Ãßt‹ù²áÛë#$c€C»#pÚ[Ì16´´Œ¾8·ø“{zsN5•_UŸ®çàõÀÌäË©/±Ó¿_;AÓÞùq®#$ªgFwFåñÊöN5ñú0fxþîRj›’H¡;@X"ÆÆ,Áª¯¤7¿)ùàºn2ýpÚC—¸Líþ†¤$’úàÇ×.Ì£{q¼oWŽ„#§\ü·¯é½@qž›Î^ot)ºzfs¾&óUÿ½-rq.F’"bøž¾wÏ4g¾ëh<­Æ"\9¿; 1R èÁ4JdI#-Ñß@Z¤ã ˆì:å\!«œ#,U…t\>È8ª-xÉ#,6ABýL¸EŒB†ÉÙ¾u¨NýÕɧµfÌX†˜¯qŽÄ´î¦>Dzå6ž%är‚¤sB)2ABÀŠ·L ÷Y¸sºŒsÇæëQR¡O“¶u¼Ž„©; Ñ1"# _ Õlò÷{\±Å!Jà†P§I¥1ٖ١Z£LLÓE[nš«j0“¹$Zöó>7—§®uyç)%¦Ü0¹\É„,1 ç¼þR W¿æÖ©ÉèÍÓû3 =uêòÌØWxŒB ‹ˆ©$1ÖBP£Õ‰dªBqôRÈN¢Þ/gê#$bÉ0´qª¬HI„Á»D`ˆüöE¹ýïLÏ7÷rþ×0ÕÈC¯BxFìØ³NÊMµ43Zò…`žçbA`„Aº€#$¾aïü¹˜Àg#,³Ûé¾ÿ_¡jpt‡œ^Úâ|¶«ïJ)IæS°c=c”Ð:³´Ç+)#,PŒ~ç½áSU»S_®ËHàìõeÎ4úlòZJ«\í¼„rÐàU"ùèÖï-sÓ¢Æ)´=‘ºkBºÝ›¿]k¹ˆ7Y×™UþÑð¥¶ÑíHV'E*5a”ǰ[t¾¥˜Q¬Ê9X9bcÒËWw]ªPÄðo8îÓð¹áÝÈ«ìÚ¸D7d»±h„aŒ,Ôµ»Áõã;Ø¥Ú©zk¸­j3E¯M­¶'Yⳬ+ŒˆÕ7u'6C,±'š+KOÂ8=M§z(Ý@ëÊ ÜÝÁшs1©I:¥’ŽÄ:2é‡ #“C‘˜¹ÌÕo,:¾ƒÐݙĈÉëøÃ³È»º%êÃ^r{¾>K×½Ï,nѤ$qJÇWaÑú[Ü£¦ÂVkh]KdZÉY&OA°ÕŽÊÍKe4¬Âs¦l¯nG[ªO8«,›}YE“å/kÛ‰1ìÎ.ƒ‚a–&<èk€?/×ý6{™IÑc>vKUÓë¼çû} šgا©Ú§^Ï­à~úZmì!?•pµlO+>/v=6®îšIý´|œcÜÃ^8°¶x»¢€=µ#-Í–ÉuB"ŠŠ&*¡U~eÚâÓÇa˜ÄF‡vé™) ° kµùkO6„Ö«¦óÓáÂóìæû³‡ËN“—Á䆤2€e…0Š1•#-,’#,¡ªb¸SÓ"•ŒM|µé‚Ŷa¸Š™ÁÇb°#$ô:ÄàÃ{† Ùóøw×2j¯>0Ë6G-$wH7á°SåQÞ’ŠNÒOå$è±ØíJúïwgß‘mh½_X•JµK²{,0Ðûè8ÖÆ°,z‹dÆØñßmÜÜÝ©xš¢Xþ µCš[ðçš.8P,"¨S"Ðoœ*ñ“!WTðCyp2b P #-×'”<ß^pÖ†… ¨d›)²eÝ!ePž‡Í?}¢ò–XˆD2gZƒiø“D|zCM*ßí&ö©Ì¥d=CnÍq¨’­Ø+½|“#£‹k\¬°gºXß}¸Ã“5éåÍ«2q§?ÂFpg£6hp† ™3#,üù½ÒÖ›Õ’¦,`Bðo,¾‘6&r\áfØëf)h¨niŠl™„®] rW3hÛ·RÇÔÒ4&D2 ÆQÂðÃZJ`!ƒÒìd?­*Æ`+K–,F8±!3e ¬°l–"Œƒ@g÷æg¡›®{{O&Ÿß»ßÏŸçß×-õ-¹£'ŠékÖ5_j¼ùvÛcLŒbÄщ“(Œ#,-‚È¡–ÕT0ñŠØõÝT±‚ÉèE`V(Õc`aR „DP€i¦ØéZùN~íNYÂHßóñïź¯‡´@Q¾Ï¡;÷2®÷ƒ=W²ûªž«RØIR3èS/¹ƒáƒµ4v0]ì«-ÊÌïm¨"ú@„çl¹ÎÆ97›k€Þ#.ÍþFþUã__ãßht~¿‹…#-쉔j™¼÷RÞC2|µÔ«¤<œzöŸ×—º ®¸ª8¶·k´ÌÙõÔz#èê¼·A¾¥Å(B›`E7uÕ¼§œ;Ø;\»£pò,Ë‚ã•Ù-©±Ö6ˆ¬)b²KoS¾m‡•ÏÏR嵩Ñ2¬+•uª µD81†4 LûqlíÅñßžV÷!ùz¨ƒåªf7¼”9§ÄnûÍúV ¶Â?‡÷Í>FÑžŽRÇ›êøÎpî¾ö$DÓ¡G嫱¼ÐÇqB³ ™×(6lCóÅÄÒ;\êlȦïè·Êµ»ëiþ«f6!24‚­cøwx·ºlqï—]Êþ` jýûÌõ™I$ âk£¥´\ÒªÒTý"±ŠpC?FÏT¸äIDV#-”Y-àvã\…kœÜG¾ñŠ/ØEø±<»ˆ÷s0ñ}!8uA¤|.I#--iê– Œ’ÓÚý}¦{P27FžÎV_¸·KV§¸•²%$M¡ÎšßÑÿAbGE¤ká R7¥Ê·f7?ؼ¾Ã&6/L~^½@X,D¢2RAÍ×i2ÈŠÈ4rÊS51!èdXU4ŒÖIC"Ì'GZ,!¡8²6#,¥¬”Š1Ò(£‚Þ6l€›Ä0ñßäº5"ZPŒ9e®0 4B0\4åÛ¢.Õ(Š4¬Y#-0š7É™iRa¨6­#,¹#,Ô %2£QôÌ4H1Y CšRfÌ7—éóÄn¼²æ)˜hMîGïŒ(¦¸â%#,ßNl-‘å³íùû§|sÎ}+žóÆèó.#-WDi%Ùž#«('É6ÍÍV$s»ºóâÊM³\dÊû\®6~¬§RM?“•"êLGBr)˜fŸÐGS(Çðü“^»D„÷gaˆŒn8Ù,ºËJîkÖÁ¤k„4YÜÅÔàñ>˜û¹õ†c†8³qH†MV<îDxëÑFß âƒr„—PÁ$íÚ冷¨ó;<ìÅ<8ž¦JXXÁ-Ã#,Âx¥•þ㕃®™-2>¯j‹(d§ÑÖv‰Mw ð‡îå56Ü>‡|l¾éSå°ÛeF×·¶¨×»Ê>ÒG8>Ó“n©NHøXî¡ÎØïÕïô6"F&aåσžêB©H½je:qµ&Ld ‚·ŒHDÁ’sfWn$"˃ýȵ7à?k|ö‡Ð|#,dl³þåF”:ÐG4S£ZLx9T1pC§ÑP®&Ý0ë¢>×”½v}›ô¬mº13ÆzXãYR™"hwk˜Ä¼|1Ußo|¿#$ kó킵©MäÀ£˜ý×Fm¹reZH66R#!›òò•õáU¿¸\ìÆAŸ%h†t:¤ã'ï,ð­N0‡õogÌåGÆžìQÈŸ–>M‡þyƒ`ˆ8H^%#-&g#Âë!H¯ouràS˜ôÃ3&Aüºa5îÜ{kµ}-Æó3ëš¿Æx<ÞÒ]£Û‘¸õ–äœ2ršÛfĆ=5)¾™H¹$Ë“‹Ôˆ’,™6£ ·6µÒÒE®mÓj-¹mºm¦–wÄ×îŒc$´²Òõ”XôYH½—#,Fa"ïôíœy#,w½^%SV­èÙÉщXø©ûÌÓX†¢gWôƒ²GQÔt‚ã©#$1ç`p/åÐCÎ÷Á¤ùvé~Cø¨qo!ôñ±ÌþºY‹û&ó†{¥W‹›ˆ5sÏ-f]£¤ÐäMÓ„)asf¬ªýØ~ÍÇï>ó¯¹Ý$s;•þóùoöoí©ªwû<îjÓÍh‡M¥Mꡚ𽰩M;,è£?êv¯Šv{öõr蛯šøw¿•{6ö,C3‡õoƒi€ž à8ÔóþÒí}-cçÈ,ð¼ZmÿvGŸ5Y%ñŸsüµáõ}“Ãé¾®7EÃJàvyóE+„¡Uƒ\©iHP²Ä’I/m±¨1ªµä³‡4¼ýò#,ý/ûDkñ>Ä_£E†MVƒÅ7òþ?¶œýý¼‡ŸP-hè#O‹ã8¹¡—†õuÕ–PÇšü:¹ð³ô]ÇÕÕ/·?’}Ý<¹ßNß#-뮽Ÿ^Ëì¦ùò¯£(‘~ï×Çï¶ÞLK§#,}Óɹ*4Pä^`ëË -¡Ô@Š‹Nœ¸Å« ¦ͼãÇÛÒzlªV^,s©®¤fÎ߃²¬•Tä÷{ÃIQPØâî„øßšÂ5)"co,T1ÚB¦Êg´ÿÝQ)±úO›ëú´åÍÞTEc’Й<^Ðr„t*ôKÆËB:5T2Zå’¢9ü¿Õ×úkMŸõ#$&/‡öZ²I·ßû¿g†ž` ®»P‹èg0“eÏäìÃݙߘGÎ|©Ï>Kxoãì(›x{N̳o¥eY蟽ŸÀÄÿ\ÅH¢‘PE±ìíh"™µ%“D˜€Í•,J/å8ÌMlUü Ó¶íEËw:íÒ×!jœÙ½‡ŽR~Ùï¨CâÂ7ÿX¨”*øN'•È*CHVÔ˜¦®´ªž3 åÑýMÁÄ"uµWÑ«k%·Ít”BC§d¨+†, A#- ³õ_e”åLE)¨¨J”’• Ÿ#-»¡{¯N5ú®ùæx-Ìñ€ãAÜwk•¿{³ on§ý:Jùœ*×îéðÜÿsž˜O+*ŸÝWm6î¿Ôeš_kN¥þùÿ&/æR”¾x>ïiuq…È! k,—űVÝ¿ýÇÓÆ×6¼k„¼ù”wšŸ2ò.ʼn&‰çæ|Öha¢ˆ³Xù}Àgž7¾ÿ²èàÎV³¶ÊÕvoǃëç)§ôY»7‘,´w¹ÐÙHt\jîýôˆt!¢ÌqÖ‘‘aë}xh¶ë,·ñæÂ6Út(Å\ŸÏ²¦Ê9þé¹ùSGgEêæ½”»þ”²í‹WwáY8>å¶/^1ÿ//…8éì†û)|†³·ùkš¥·óHÑvÈœÜ}òj²¿–ÓiÊ–iÝФí³ÇlD©ÀtÍîdÎÈc€õø^¥ïPÊí8jߎëh·;šŒró”A”ÁnuúZ™|I")V>'lÅi–zaøßmñ×ÇŸe’ù;2±•Sƒv­x¾¿CŠ.o媉¾Z5tGƈd:NýÐò¢E¦³H$Ü•õR<Êi¯GØ›ùZó£ëMöç¤ýñãŒQÌÑN8ÿk<†uõ@ˆAÓ-3:"wý7ãÄHñ–,kj"(:e4i^†—ÞnA–󨆄ÉåÔÞvïçç:A!¼™!iL¦] ŒÑR,+åÌ1;KU½¬†Á%ú#Äe'[ìóÛb±mâ#û$hÿ­H>Œ?e$¤´¾ŒÆÉœç¨×Üå#-¾[l%'ØC¬A¤‰NW–4 òN]NAæðL–Ôß6ŸÓU_4>ÚËèñlÐÙ÷¡’+†'¸|F¤RÆ&ÒEjBùÝÊ~?×á)²ôlà6=#$n9#ÁŠ@—cºæs88>ý~IˆÓ4јŠÂCcëÌÓ×ß ò±å1°e¥tù7BI³s™y®ÛT$q¿áªuSnzŠÊÜü%mü=üy8azÎßš™…‡ôxŸÃÍ|>¶’ÆÜüvžrê:V•<ä KDª¦BIÐ4¼7ž]³3ÑwGNäþ¤ÿÜ”ÏÅŸág³ €Xÿ7o‡‚“JÕ.0¤¶ ) Y%üSÆAÀ"·Š@":»=>ó]4NÔÅô7¸™ÄÙƒ“ª´‰OÂP[7veÑ»úõø¾_×Ëg«ëú¢½jî“wcÝêèùV¿_…ºv¶ÿŸë¾û¹y|èýVû!ª¿MðOÃa³Ñ}†ÄØ]ªZp}®î² þ"¿‰ë‡‰~|[þ¾éê¿_wéïÜUÅ¿á_7äé$ý3ôcœòNU¶ÙCîþÒËŸ¶¨žUèï~žZ¿ÏÅógUÕ­ñ –|5ú¶E—ñÑýO#-«—‚/nèîüxéü¹~-þ½:=#-ã;òÓþ®Ÿš+wÐZt†XvzuJΪ᫛ɮ³ÓùS¢ý–q=={¿Í½ßÞ¯Ž‚øÚ,UÌÓ6/ ÿ)eGîÙ¾œú,ª¾%Bìz >É·&'ã#Õõ•k¨é2ôõÅ y°{êŸ 9Þë<üT†Ìûe åvxAºì¯OªUÁRw[Ü~‰™ìå²$z½Úþõ|xúsnlº?½wÒ“×l6}36ÖºíæßÃdñÂU›iÐò—Šy—`=ë‘£zZÈwRëTwÜöuÓaßU#D[#,<ß³O ®ÙU÷uÑ[‹†îi9“ÙâÑuµ•y5qÀúiqÅ_ÌM°ºíEZ¸‰Ò0‘„2’©F1êx¿£Ý£W%Ú3ä×Txý¼yî¿ÇÅË—“‹Ð¸pK#-³â™{}\xlÃÚêâå†|7j„ün@èT‚庼9:^•\–^¸xî«£§d-MRD|}AË&ï©àšÅùr;(ÿìÊþ¿—Û¶T9—‚ªwmè³®|uw~2­4Úñüdqýƒn4ùýOßøªþ/G öÇD ¸Ñ§v6WC½O‘p,Úiº†]ûé¶{ÁÞV¼+­ûµy6cwN¹x¶Û ªx{<ÐDêת1ÏÓžèú‘õÓi~žJß¾[™OT#,Ú>£B³ô«Ü ð m“AY£ÉõÕßÉž4ïžêê䇸ê)¢Ï«Ë^>}÷åcœÿÅÊ“[òZÝ¿ÕóC_ëZ³=¸ÇrÇîïo?õù'V›¿n½›–¼g?T—ö'Sð»Õf=žÏxTØýÊ­'Å\ë5åBÕÌr|:?•úgaôÆñ|šYääG\4!—“è·ôG"ÝQcÜ|Û8£óJ¼c7±Oéürº–$pê?wÝe²­ZØôß]³ª5UÐŽ‰:ÚN@í%>–®_-}±Æ£,ÙÎÍzÂÜv]¯£UÝʬmÙoøŽ]Ùáú¿Ue4wG}¯†º«Pã\úRª—‰@^*¸¡ ¸.¹R1×ñ}„WÄí—‹ÎnÆÙr˜ºñ×çèÛ#,°<«–>_å½÷‘VI?íåõCü¯óhmÐ×FzMu‹­×+TÛ£\}Ù2ôãÓ.û È®ô»©L Êëå…ƒçˆÜœÿ|l·ÕžUGÅü埲ÃO¬È‡Œå#,,qVýTÉNˆ$“ÃA£rü½u–ŸÛøA­k9ôšãôñã™IÊ#,Uz)åùýFì^“Ðüù©»VnR¦ÐÍáãÕ¦¿ïÿyÒÏœýµJÈÿæ¸ij32æFÕfXLeƒPrŽ ##-~_JŒkPa¥Pm±R8‚<RŠ”I»b4CC˜ÜXH*«J7Šˆ±Óü»µ6‘ƒmpú=3†´Ö¤On `˜a´ŽÅ-ŽÀ¦°AÍaFd”À„²&††èB’)cA«°EeqÈÆ8ÂwÜÅŒªkq¼‘$]X$ª~Ü9]×rhG#9!#ohµW}ÕRŽ#,§XŸÝû/u½òo‹YÈ”?×Ì~ ÔU庬üúZßüâÐÖàæ\µ—;s×±ÒG!D.Š”‹"U…¦ ‡ðõðþjîÇßüùö¾‡@gP‡{¼ò…E4­£ Hèé@ŒÖbí¡i7¨1lXT¥Q&FÈCV ±6Æ›f’J¤ƒÿlˆm%#-Bëø–¯·Sh®—ý>&x=h³ŸämXöñåÙWBÇ4?Ë£;1›ODKÍuõ+®×Vs+®»ÞVíÑËÂï/Å#ŸÉnÛ9}–ìÙûü~­Å^CÎïwßöùôÍHë3ýýÛx¿Aâ›qæÝ¿—ôÆWé»çËóÑRþ~y¢qã'³2ØgÅ~:FµJF?;ÕmÖ½PÁmºÜ~Z÷SIÅǶzG¼î´‰rh8w5­6©$,ú#ö(ãqú?gÅùþOõíöìÛëïLײ¹Sý§',Aµ+]ÕþþH…÷­ž¾‚ÌÿLîãâéìÑ>¬ŸŽê%·÷Ûò‹EF1„¢ñª0¸y³¦ùrÿn¼«¶ºþ}~ïÕÓ#-¾:öùwKöòl«‘+ÿK7 .ýÕþ­}õ—ÔõcþêJññóŒyïÐð'w_{9÷u†‰ýB:p?Ëðœ°þ¿wM±‰£4¼ŒVä)Õ¢Em(d#¬b”í’úX*5¢G ýºÞ/tyçœÛÆ9ËfRɲ!±œ‘Um(clHèÀ#Ö¡SÓ‘DA¨6ÆÑ1D™ 2(ì-p°#,È”¬¶›ÝQ£àû)+oZ£d--RµD-• E±#- Ld§I7›¹àÌ)×nBT¤PTÁJÓ!70e[0 ˜ ‡ky››èU™:·†RL1 6²“†ª-!óiÜttYKk™Á$-éÅ%¤Òd(°”Æ&Ó‹0q½-¡n·[®†qR˜(‘"È•1…S›DlzLˆ;´‹4°UãQ9£AÚ†3XbëL¨Ç:ë%BC÷Dé?,ɳ8+qðB¼É›m­HVÉ¢“É›¤“Mß™Ë5¨>y¤GM›e 2=®¯bŸÜgOûDm¿ô¬¨´·ÈõMüã‡t&vOþOcx—yºœulÔý™|ÿuëŸÅóÇ=^Ÿ-4 ¹Û:—*çþîK+õ3À÷Ëå½ã•¿¤ökéÙF+ôE¼ÆM6‘°“öA·­):)|[¦>ºãÏAç9Î2% pŽ›iØ?‹ù©óêÔ(WgG™úëÛg$=š6Ü«ð¿çéˆD­êѲ$q„³ýÒµi•B?Ñi.©9tÎÈ\£ô#!E òãMÜ„ 1Ô¸çš=ÇÙ·š·G$Ï4;UÖ’š9sßâô(Áõ¸ÒIBîð¿µÏ®mNxÚÂ-sRë›ÑÇÞL¨§‚åY²õzêå•‚×-;ó%‘æõ“XÆéq»V¡Õt®_>öb-µ$’ ¿³—@Äw(p#«Å5›j¨‘f¡JýcÓÉU¡¬!PnÎ…TfÚO¾Ò-BÚËÛ mTwá$•åE_¸³§n¯‡'$ñµúêS#ÛÝð5=#,þŸ§nÑýW€¬¦[!ûõ•Q¬]ÿMa´SmR62B8Œ‘!¨—ñb¶?ß˦ómðèìR2#,Ø£®´ª¾ÿgïçýŸÑéüÿ«¿¿¨êü=ž8Ö;å ÑgooÉ?$‰‹sÕ#$L$É‚1ž½qaêL‘Ìs(²ý”V_i ™Ë1¦41-6—¤ÆñR6šÌ5EÈÌ75@‘s€®c0ˆOSꦘõ•ß…a˜2¨21W’0k 8#“3 ´(T`ÄÈ‹Q`tz®m/öàDbk¤™î›Äf<Ž F9¿ÒˆA,k!mb{†„&‚þá"I¢5«ŸbÔLj‰fíÎ#,U`ÈFÉÙŸ Öj9PuGJ´>uNÃXÎ…„3+dŒp²&C­•ÆÈ5"$‚‰±Bc-!B)2LK™DháË,j8—¤N½XkYX6ÝÕРíMÕ€¹ ¢Ù6Ò…QǪôM4Øú{{ë¾7ŸtõÞË…×t/µÃ w$ºi@¢O†Aájáø]ï Â7ŒÌx0“#-œTVMó÷÷qÉûÿi°ÉœWöaßžT‰>áÞû<ÓJF¸’5yje—ÜÛc4æm¨a7Ô¢–p¨qW†üÎã‹DXe£B„N CŽÄ×ä êC.ÆÁ‚°_óV½Üq·œ¸ ’Xßj#ª1[æÎÛŒá¶j7š í4mÂù‘`ÃkPCj<¦Ó?V1`âdB†Ü¾†¤G#,Á¦¼¥»Öå Û®û^MÚ1Ö5­ßW·‘3¦µß^MK¨ilmÃêšõÉJ£•*®ÒaTŒÖêJ4+B¯“qåv#-wQ¹ÛLp¸8ƒƒ%¾ü˜g̙̈!ÜÚ9—„Êå(·‹|âÙ7#,­t®Š#ñ’Pkq¡¸"Gw‚ެº­Ãr×|Eñ´î¨K¸£m¨ê:ìîˆ+&óÉ[¸Tnq°QÍå»á7”R닌…`'áA¬XnÌbCm°·¸±ÑuÒ+V;B'F)ºêèã9žLìÙS(ijäk1â†j}áá6¢µE“†7Ea–`0œäÆ´*l9¾®I²‰B[æJÑ]níaZu `ØåÛmŒœ´‚y&Ö¤Ó2 Ãiæls}žã•‰ïÐi¼§ß¦©ƒfM²~òÒ([öw@O! C.Ücõ{I^NÅ&Pø­º*´”¼CÂ…räâ¢q~.»nz}xÙ·œ°lµÅDâŒEÑðDcB1’ª7óÅ*j¶Û¡¤¯ý‹Œ‰8œí3Ê"ëé“ö0¤­ûa÷š~‚¨ONÿ¾B¢j÷m±N‹rbfaÞY;ßù®¸^N¢0³—*I®ª0·P¦ç¨‡ÓšÖ¨ ‰6”Û¥å(Ùk I6•Q’nÉB@cº´(yWò|¶&]îžaèS8Ú-5Ù[)Rén݇yðö¢?ªÙìuy¡Äˆxb…š,Nñ§)L¦ktpö°šï#$BèÛa:©D£.ò[ÒjAHI¦Ã·Læ[I£‹¨Ä`¨±VÞÚìì¯&Ý×5ù¯>D²Íó““š=æ{7xN†tów˜ÓJÔ¦´‹möÛ-vØv#-C:!£m8Ÿý“uòö±ÔÚveSâ»ÇufýéÍ'¨šØâ<¿£“úÆú3b´ å ÆpC¦‹e¼qÓpò˜J'J±Âç¹1!Î5õUåýçýíÿ$£Q>®ø´šqz”Hˆ›[Â~Â5~#-ÏpN¶¢µÃð67ôœù½ŠŒî-Ûû’×þð‰yþCK£R À~iËUgã¨åa°²Þ­Ó.-„cÆåA@<[Ôÿ~uN){.™âå`Òrït¢M}>îõ#-°s†q[<’±â{D3^Eá¾:_Œ„§ÃË3Îãé—™RÛ*~T•Yï&\â‰ÕéÁ˜H(Ýýu5R£÷9x#k…¬ÐæM£!uÐÆ‹mfßWÃäþ6o®ßwgúëñ+Ï;5 ÄÛ0;cV²¥R=çç¬À·ûÿ¹-Œë„ñ‹ðÿn“eR úÀ7K?•°PmV6!#‡#&b©ò˜Ý}²‡á¼g²)IVì–\—.B„m0J°þd¶ãÖÉ‹aæGhZ+RÁG,Kµ§qYìÞü}/±ønû•êî¡Q‚b%à¾ø¹€‘6øÕ™Š?ª$Iû'ó$k­Õdñš‡v«ë%!#-’2l¬²tKûEB|Û#-{ö¨Ú¼Ž¼–ÌÊöÏŒ“'÷êêyum·“!Kºü‰=†òGmûm3¢:O#-–"q£)•º6ÍÎím¿…Cî”âÙ”,Ôö BŵØÛrØš)¸Fã&ï/ÍÚ~O}9fo›”+J¼1Þð™$ÔÞxÖ]—D›d{tï&þAY‹ƒ1»â†‡'d¡Õáä…õ¨R,aØ„>rhϬø®¼Îv~önÔÐ7I>½z‘»>/÷ iïgN‰{è<ŠjiåØf¥#Yô¨®[!+Õ6šÉ?¿=2š3â àRS.‘ÛUë8CðÛ—I6ßz~‘4xb•ê#,“_£õKçÓÍŒ×AÇGÃ! C!·v¾¥4ÉÄÉÏÆ©1c¦¹øeÄÞÂ.d™f\A‚D}ãÞ‰ÁÌð ü—¹ŸŒ2Þó¬Grɧ鶖“#-l(j§jÂqÓr#-LÍAl#ÓXsëàÍ$*ØtM#-ð®Ò“#,$!vð¹·ÆÌp¬ÁbÄQ&–OÍnÙ¶8ÿmS9lsE7ʰ½1›Ad´Š¶xÊ(rƒfæž^ëÛ…¬„i¼³É愈­…rà T%&&Z7”“ÊâW6d âÒ íõöîr=“Ï»cŠzày´.n’Ú¡-·§\ž­º¿žäÔeùNÆid“ý++ uæ vžË£C˜-±”Œm0á"ESßuªõï.ÄÕ;‚²¬‘”E-Ç/”Æa H •àóµ0·Nõ3\Jܘ! h»Ìȯ_Ç Ì'MÁžÎg‘ð‹Ü×#,Oµ‘*áT\²Túòò?P‹ô³ÝšÄ¯øÍ›…º“¢]×(:*§›PGëzÁG¢QöÆ#h, ɾžP§cŒç9 =ÿÈã¼i´Îo”dÓkåéñâ¡Ö¶ôÖÚRòp•ÔMáÌ9:nÓÚ ˜Šç,Ó5ûï˜Ío/ó!òþ:dψÃÖA|>™•cæºh3yæœçeÚš‹Œ£Ýª#-þ¾?Q4®>\λÈú*ÖW,¡‚šì‚”bq¨nœj<5»MíäFíû…õÛŽ…•–Ì !'­æYCÆ<­.1Õõ¾ŠÓàN§VªŸIùEÀî“Åyάy¶b^j~8z(¡ž×WÍ«VðÉ8謽ìu¶ù¦uޝ7CyÒÞ³YÒj'‰’O%Ê•9#ªë—Ê8ê>–·D?»Rì²ù]T(‡òãiÇÖ#_¶«mý#¦uøÌ&‘éÍ,Â$ê’ZžB…ÊIlÙ\È›óÝ–B` „Ì#,êÍÓ(ðƒÙxíäCVý±K¾üù­®è[r# üx™“«q³[Qx„0a!â[Î}•ÊO¶Ý ÛIßÄCg9˜Ÿ·]86[@BåjåÞ¡Þf»u28K¦´\Af2{¦¸Ç(ØóÛA(·Ü}òÀ¥44¶1ñ_¢Ö*å«tµÎ‹9J´¨É4´<ó}[ø>=Éï˜ç“<;ˆÁ‰ éÓZ=3iµ4pñèQë1ŒšsZ-¹¢Ý3ç9gî"Z›X=N¶[lù2¼™Ú×Eñqy·àŠ5c¸ðs=³z´î/±¹“#,¶É>ÆJ_zÉó»õúŸŠn«ƒ¼;έŒ1ž'fk4îoœ@´Ç íB²yÌŠ›dçÜ“*vÁÇêÛ#,xj…Z2¢#»”é-µñz\w(Íä½eS”,<ìÿÎcÖîs‡wûo£}½[ùζ¶è$sLüÛœ‚ïPbÅöÍQ<<§ç @~$þE!ëb`þ.ùS.†«c4ZkßîÖyiÊ Å xݬҬÔbÕ‹¤Í2ôZ`®­œ|1ôUF½ü)³ÕH戧ñ)|ú[P—âŒfØ|–›œßfÎÜNÔ­—ºÃ6dvÑ”ÉÙ{mæË¨=ádËëÆo7}¸SZ‡¯¥.Áö¾}°cj„g¤èñjp•°Y‰i̸–¦Ïîføyûÿ±÷ñZåï1ý#-)Òuýû½•QM ’#-RçýµBuîzõáfËï°8Ö²¸Èí—ï*²sCJõnzm®Ï–»2QwÂ/zg6¼.•½ŠË(n{]5QŸÛú¢óåÑoÚøà]x›¦#™Zóä4oŒ|yÕýÏÒ'íçÎÌyóSÞúwÿd‡“c·O–¥d{\õJdqÝ+q· Tز¾<#,ÞÇ'ß•†œôN­#,:÷(J[–®Öž‚c©'”_ S¦šâú2+l¤Ó1´åž:+£Eu¥ÁÁç¿8XçD(ÏÎÞçYüèç­ºÚsÏé­Ñ‘RnyÝÍü—C\±¸Ñ–S¿Ñ]sªãŒ¤ñIF*ªéåR)sÄ•ë覿1ý¾üÎ33ñ§X'Å÷¬ñ~Z©ó°û®ìL¤ø¿g£Œ¾xSŒñŸoÇï™4{}¹nÐÏéÕ(Ó³nvr£#-„-×ÏÖH¾ê_±†Ì6å^æ/§¯"[nÙƒhxâ>=x<œmF$éãƒ.|Ï{š*›Â~|á©YÇãÛLí¬£ÈVåÙ¿UîK^{xµ˜,Ï]”Òáî½at'{åßÙb˯/(‹óÄzò¤øÎ:ׄC~ç?|ßèU,¼Ÿáé¼{ú{C¬¤óZ•„Ý u^/ßÙþÎeþÉ|6ùü18Ç–’£4ü"S/*sZ‰wè³!XEA?/àóƒcÜ^»lBJoºw±Š:jíÌ'Áíìˆèl7L¾™<ít“½D[ßëY®hæ1ɳd°÷»ÆÝq3K'wžO[ÇÒëªás8Ä}­«n&…J<~Î÷çòéftkr:$ý&^˜ü.8%¢”ž2v]‡Þ6ÆÐÛýöW3¼,ü•D3­¼Þ«®¬)Q§ÿ˜XTH,G^#,žwz(câªøêX¬ñÑäï™±qL{ÕÛ‹aÆ–UâªèE*©”u[:›VëªÏEVGÞó|oŸ¾ƒKÖsùö†9On¸—9ªeáôëž17e J1÷ú:‡}ûç›ïøÎw£™T|}:¿/y=ÞÔïÖ2‹ál²T–§v¨ŠUç³5ÃD*£Ëb„±}µtªV+¼!ºIgép®ÜÙXÙlÙW_¥_kXõðÔ®¯v®Œë¹ß¢¯†nÖ2jq%¾úI«M®Ê«·E™Õ¥\à-ò…˜svÒ¾ZŒíºææÜ²“Ž0|ë-'¿‹1ïûyòÍãÑQp¼£p½üqÛŠLw¤sXµó]{Vgãß’ñǯÓèk>8;’wsøÏ8‘÷mZwðæ16ûO&?¤ÛÝdÏVM“Kª>{î³ÎÊdôÛy Ö_.Ÿ|ÆevÇ MµHÆÇµr`øRíÔ!û+…"øñÀªÊB¡P„Þî‹8¶¶Ý:•ÍS‡v›ãÀÙ#-JÕž°Ã\^ØÁxžØgÅú¤~z­\(XÄE{VÙë9ÅYïä=¢ú#f+ÏÊ=TÜ»B@/_^·!r °bsC·LxV+ÂôézŸ]lö;{ÜqW#-F¤ÉÊ2dr®)ì—=õç¶PãÑeÒ)p÷*<âRáïíâjcå1Í}—O­üp-ºb«¯Ã¥q}V³éÚcû“‘/ˆ—$œÆ<±ò¥Dæ¿åÏÇž=¶ß‹ñæTÞ÷)qy‰é#-õ¿5÷/šõQFÛ¿*Šðmóïbí$FÆü!všRá]Y]“¥*¤n|V)•xYGz=þ dÒr‡iõƒ«Í’ sÎdÉÓå‰5g1Ž··x±ÅZýªNû4/ùúvàûÇ)`ÓñíèûÞÜøQaÚuí3q8=ýâÒóWEÄì9ä/@·uÁ.ZÆøêo×ðÀmúö¼¼RüÔnö«ÏËÊ|fûäÝÙ뜧Ï-³˜>»}dÛ>¤þ§þÿLÆ-”DC›?^j*—_ßÖ*‡uJa´C{v˜7_vyQÄ?šé½5óú·ÂB(H[¥²Ø•\RÈöYU#•,9ˆˆñÿ7±IB‡†Ÿš:ó,¢ö˜†¹vîø®Ú¶•@½¡Î©ÌþŒC œo܃u\âß’Ëü4øY[ÌÝQðz>/nÇÛþ“¤ñÇljë·¼¹\ËòãíøD3NJ1>8Ú^öÔ¨ŠéÞo}¸áü¡¬íÓŽ˜sœMQcýrÏðÞZogˆ¼ÌõæfSøðr³^0/öàÔ,ù¶4§ÛƯMF{g{í[?kYão‘åƒ2ç;G?¶>ýoŒù¯_á¿iüú>¶#,ŸÆO5ÞžµžûÜÝŠGLA#,¼:D&”/»íµŸ~ØlûÑa£áó|*ãÊ5RýbÒ÷JUo벋bÞÙÏ›ùɱëÁí¨—c#-wK²#,«†]–NËé3bus%ºf¸ÈÆnÕpÑ•(÷cÓ8c=ÒTd‡Ð’ÆšÛ75Z^[sÀø)ÝÜæúdÞæœò,ñ«lLoø'`c¯é¸`ãÆ#-„ËÕýÎ:5I³©jJ#fع¾”Ò,ÆZ^òø6ó˜‘öSU¬öϬm»«ËßÃëóßçð·Ã¥œêȶysÏ9×Ù +¡¢ŸÇE#-+Ñœ\uÂúdôLö¢›”C¢SËÕ#-ñŒ*œiONQ®äøïzWŽçWTø×:'m^¥HS=7GN– ër‘ :žú:›¡ÖS…ìU/k^tk²ÈëvìÓ|4%Vêƶ£JçáªÜz§±D0WS…äeÙ»¼Ó•6 ¾¸qÝ‚‚LeÂÞ8åtàY‰ß¸Gгd«îæÊúኹŸ¦0·+!Iºm®Óì|f47€ïµÚó„üVØîÞ'óŠ ¥=l©~þûx–ÍÕ¢hÞó&œÐîCÇ(Ñ9l4#-H¶ç©@¿‚𔦫Yî«g©[Yž‹öZUW×çIG,¡N_åïݯ²ŠÈY#-ª?rÝž—·`ÚÂýúºpƒ\‡çxnYØuﵚøðæÊ%¢§±o²’]ï&9næë‰}Y£¢¾byD‰åðóáXÑ·——C<çwÑ1£åÚ>3™S£†Ú!Wˆ3©Þæ3éÌipv:þŽý89/3Ù‡]7ów>Hu¤%>\Ï~,P;jnZ¶Á}^Q³ÆguP$òut ‘éõ>ë·ÙUsôAï–Ù]<ñ1Úp¥–úÔеqòQ}ç¶W§H¼>b{Çóñ<×7®e··-4-Èš»ãxÛ\HÛºmîãœ4y©î–ú{ì€h[âíuWˆ"-§}ð©gl~9s@›_~¨M²y¨lÙ)t+¨riƮѾwˆñ.Qò¯»k¢õ©é7j_=úEOõ½¨=áÛKŽÛ¯Êé£Å–63+g7}.èYÊ[ü°ø•$c(=EUR£š'±x*º}]U%Z±6ê;lSP¬éªR<‚ŒÁÐÛPÓ é1B‡\û[³ê¬dŸƒçÁl\Í]®ÉµI#Âæ)ê•°šýèç[«®×OñÊ-bùÙïoéé¶LK\ÐqaŒgWî ¹7Høë{öGhônüË4/qãÅÙ÷ÞßÃî¯EHXü;‚J§Þ#-F‡×Ñgâ“í(ðZ¶Bï_D{ï% Ôz­…Øs‹¯µô–5µ:tì”S/ލtlr¬·ÕŒ”,¨¬¶fMÄÛ4ü²¹Í™ð‹ë´–êbRUÊ rÓ~—Õ:qVÛMXù9²…­zc  ¹1‚”™¾Ñ²ÁÙʪÒq$š+G–sóš¢i2µ£ú &_}¼oW={Ô!.T$m/]y™Ò3*û~ï—ñ—œ@µÞ7"U™4çog/ï‡cOó˜–”9·|×+Ïl$O]£¹Çnw®¦ÒBDÀûq$Ï•û/JÅåò¾×ß^{ë.·x^«×ן'ï«ìø~ëÌM¾ŸœÌâ3F”Û”o0B1Cèî_ލó*cuÐé­Ÿ–Ûœ6—^ïÆ66™íÓµœØ:ÊfHDá#-uCêéryÐ*#xrË:§Ï{ÖÑ™ê ÷R‘¿¥Ï­R#ç¶`Á+ùGÝ·Ýßë¾þÛhéÒË:yÍÞ=¥ð-, N3ñ“5:>±n+xyˆ€”´|¨æOx¯éçHêïÒ.V|ÓWËUÖ|¦íÚáèÅ÷UîyÓzý4EíßÕ¶BÙmÑóÞzA4 ™ç­WØ`ÁåÑ}ðtSLäzã‰/º;Fx”Α¶vŠæáãkyvû×_AÌö¼ñmqhiAµ…—ô ÿÁ@uZå7¾¶®œ9Þ%›l\ÝÃÀÅzCf‹ùÆç|ï9.Y­.|ðÍfÎü†p›at猼+âØTÚSªý²Õ ¨Ô[tÉéª2,ÒçMj¥ý»N¶ú`ÝV_•#-ó±WÁޱqO’6)ã*øô®ÆFØs} qwAØ?œ=ÿÜ1¹6êý{ÀWoïÄÞs7-îÐfüâ=¯ ëNklyù¹#-ì—´J)oÆ &¿lÏâs´#,Þ ƒäŽcÒ{¨fݸF(ƒÂ= f¦lôv¿·Nüóvu6tC›£t„}ÕëôÆó܃ôr5Ö¼û_¿³ÑªPƶ‹^Ÿ)©IÞžšëð ÂÆaÓƒ¡ÑÀç°ø{/ÄâkÓ=)Sº¶.´££b³)mé^ÉêÕ¼1Ý0éµB{1^F¥²%ðsB4$c´ Øð Öýf9ò–2™Å×éuÎuÙ¡Én‚ÆàþY Ç•$a˜hÍŒÌ_:ÅPeF¸çžÎЗTÈÏw{UЬs“yO™×‚ÚpgV‰áØ<Éf:‹."Q¿[—éWT½¸$¹ˆK#,v)l‰Ôº^ª´|0Ós ©Ì¯‡KŽl¸"¤îðJïmc´Èv¥F*íÛÕ5Àì¯#-òz”Ѹcݵ<¼Ô©í©ÛÒ{×®÷—!ê÷Ùš,rŽÆ‹}K}ÌÚP44gV“'Ht8!¼±Ìã¯2áèÕ¯>øƒÏQ·Ý^túuL鸘#,#$J[kš ZOë¬.ÙØÇë¿oF#$áñ}}Û7áƒ~MøßŽ#,¯­Ø­Ý«H\ÛQ©F êm…èÛïð•\¯ÚmãïrB²r¶êï¦úå#@ .iÛèw\®Ý–•CUú7Õ96ï—:ÍºÍ ã#Œ©Ëë_½ë<+]äH‹&.]Ü_6,9k{ºãÈ÷B‡øÄGñ¼‹vÝ|½PÒhwG^#,.—¿cxçWOœþevÍ4Öûá2­Ë”¤­[‹äyâê×1ÐIàïœGj񣆿˜Fw™³ª¯ÂÌ~j –ËÐ#-2º ãÛRßMÿùÏ~ÅaÒR¡Å¨mÇÊå4Û¤ÃÓþ“ËøÏ’ëŒ>ìEœuðÜxŒ­›/­§’q¬ßž1ýwP$È~žêé©>ˆñ(nÕ¸Fu%ÍI$jÿ¯øÏÑÿ·J«*a«ŽrèA \aòy±»ñîuĨ‹ê«¼¶L—æs‰¨üÿàÐÿD*‚2Ѝ,„ ž4°?S%"ԖɱŠÚ+W\ÿ†¤¼§ÐÅés ú Ð b×µ»èñå¡ 2eƒIˆe*i‰HòXÍðA‚°ñŠ”Ð^Õú€,[*5E0ÕóÛZ¿(ŒlÜB‹ KU“ Ó'%×ÈRdí)Ý6wWe£úÍÚ˜lØ>Žé+ra;Ÿ’;ŽÄ‰‡yP}þŽ?ÒÀrÃãÆqÉ^„Oì·9Ò‡üuŸö{§cd‡2Ò_‡é_Å#,©l;18ÈSÄÿ|¿ÕØïúßóp¡ß%þ¤ˆ{­ó}Èííñ¯ì箟7:T‘9'%”{#-[åäÎí0ÊoB/ePd¾^Gz³ƒÔ(´í!Ùù8¶qÒÛ«ößoÎ,8Ú~Szýð/ý~x7ßô‡èÎç]WC*jÒo¦ËQXnj»úZÍϾþ˜DŠl¾÷ô@ªž9¼O¥"æ=’j¹ÝäÀâ¸Hc*2íˆrÕQAÄÙŸtìk4OŽ­Fƒ¦m­7R­h=‚)#ñôÆH˜‡^ºîåG6&šôm{nÞ|I±«Î^!Cy<·}o÷ìø¿ZOHQIá9ï s27Yt«¦5ôGVºjÕH©ïçÐ5åË'6*¡ Â1zº9‰ZTIBˆzÖõÝþØøG_º¸éçf {\gá”#,FžHN®ý\MÚÁ´ 1hs')‘d5ùÔæ‡D5@ý±?'­2”Àåt¬_õÛh#$N³:^pF¢"{v¥Úú032•¬â\Õ£>,Še€¾:V3BÒx²7.¤˜Ù0ˆ9&´aé:„  ³Ô²Ó­äu¶©`÷ÅÝç@>˜†P#,Ñ¿³žÛ°îk9a?¦#,C²)ÄžŸèÅEÓb3}a¦¾÷勞_|&¯,lœ|Ól0C&uk¶|Â@{ùæþKìÝ*gÇ­p1aMq Ø‘ÇóØÃ–Œ^ø¼4§úý;kÁzÑ=\h“g~ê;lj}u)`a"ÅeWhï2€£Á‘v#,´!¸Å¢^“¾”Ìi›†––$÷*ããÇúñI޳ãxfí@`’ò÷‚èÚ"?]â¤øç!¯èXG#ºkù¬Ê©˜ aI]8òóÇe™>pM"Ö™;ŽÛi”5™Ä=Á¦y@ÚÖµ-æ?#,‘b§RfÌ%Ù­ÕÔR1#µ¡¶ ™Ç(H¥ÃE8‘G„Sžyó¾ïYë7é~PÆ{Ÿ™r-p™`˜¬e½÷@ #vÂÞÉݶÈvR[6pÛ¶Æ},ò€›øw ¿pÂÈRù Ý _¤Ž4|Lú\Õ7I5IRjuªXrMŒ^û#ÐEV¾“9¨}éä1Ç~9µ»:uÔ _œàuv4æÍ¥3D|¢;Ä!a“¾ÞŠ_’CQÜb Rši0ãÊ…ÞËÊDÖË_#,Zk‰ŠGžR§dãÑÀ©ìòT¾´ägÿ:ÂoI}_ì¨|zFߺ–éR£ª‹ÞPºF:|¥ì…Q9õ’Î:áÊñ¾+N{#zª}'.p0¤òg+#$Ðä æ¶”%.i¼c¼å÷¼™éäòyå› ]buX?ÕùþÓÇp¤yž&X“øC‘ׯÂN«_SÐïª2O”-q‹ùGZÍ™Ö\Õú0ºxBI­R~zÚÌŽ8?Q6¾,LÌZ°¦ „Ýè[ñ*¹süõ›Íà±I¿ß(HªZcÂ>çFtZk8ÝoŽ`_4†ôNÁS§´HCLT[Ç£¼èÊ'ðývç'ºå«ÊÜ4²Ù…nK‰Õãýl×,ŽŽ^ÅZ'Pý Ãë‘s-ðã8ñÓã%`ì”ôßKNrж¨olBå¸kÐDÏžö€ùž#--QÞê¦Év&jAÏ&¬bD–®Œbí¾}»ªáÄË%½6ÑŸê\o˜óÒ¥˜qR[ì¿gM¨ùœÏàù5Çœ6ß]ÇêáuÖ¤Öø vL1¤û!‡0‰KÓIʧæV_°”²¯ÕëAi#¤Êî—£¾¹y’"Œ¥‹Í”}ËTó2CeC\ûãiH¨ö…Ï5ç¿A¾b¯‚èÜež™â±qå/6 ~nYØ åË’ nkÜgiŒ­¬m²ÆÂRsp·ÛIíÙôKNäÆê%Q|¶æß;Æ×(’Mç„-Á÷c»³…Ö3P½[ÓÒÈóÚ¼ÿIÈÝøÓäç2óDÖ– V ÇHEy9ı¦þgÎÏÏ#,)ur–ý£˜&ÓË*“1Î÷xÑû98uši8ÔÛÐxEøôÕÅ!™J™¦¤õ?hŸ&LÅÛ#-T=ýÕÑãÔ5ü›Nh[Ž;Úd™ô½Ü¯ÅŽþ¦°ƒæPFkK¿$O‘íáÓk—é¿"{{ûj2ž®@Å' ‘TÆåçÒpÀàé¶1•i/Jt¨jóψßQùõOXŽŽwo¡fFöÓ?tzÖöÅÓm”½Õ8#-.ìûßéâ3‰àIøW#,O2Ì8§µš${wç“#-¢Z|ź›L/æX"+$‹Z ^ÏÂRVññÒ7tm“1â“ã»^KdJgÏ_Ùྪå±Fó?¡1ëO…}µSrÚŸ–vÍj=39Øæÿ§îíçÏïú [øÕö^/ÇÍìºèú¸vóñ/áŸ=ùûe›T6dõF¡{&šoFÊÅ »l__ðÑg(ÂFIÒCˆExѲ?6¡¡C.¼ë¶µ¿iͶýO’mêÈ’M#,é¿ÓŒàØÚ#,_*³¢.ØÓKÙ¦ª6¿T£¡>Q/)aö,âDÆj#ÆH+ѽÎ]±íÕ#,ÞGMÿÃ#2ü,/ævwÓfaLîøíüeX»;›Ã°sÛ°¡Ã=…% b¦Í(´‘`L:„tï¿q_,éŒ&ëÁ®Lî"ÓÛ€±Âä]B‰ji1hà¨ÆN_·éZ;gÙmeae[ú!s¾¤kÉ4y :h•»ónµÌ0ÃoT¯„®ˆõÙ‚)›­}óXNÍIÝUèÓ+ìI7­—øv)gŸwÆ'æf¢j“ý·ü©HAh ± Å¥+vîßËNÝ«Ï<¯3:t’L«ïüºy_XhwN›j€«ðÖ¯’¤aÇUþD?æÁ³§ùÑXŠ Ä!îŠþŸ.G©ÈàïöÁ¹+#$cü`úû/BCp»£QÏÝ»O~ïÀ0‡ýtžõO§>ü±QU`ƒèÆ „aü$EÃütä‡ûó7ùŸÏëÈ1ö–7¿Ù넎q¾¨;fuUÉQ (ÞÍŽÃÊhÅïMNà·×âáÌ™+t[¿5˜atך+NÕ ‹†嵞(Bð_e ¼H"ÎÞUÜ&øºo[¥Ÿ‡/k£žÇovÖ'Ù^Ò"r…çËìì–/åTå#-N,O>ÌjLs3±n^6#,ö ˆ]â*’²¨ßJš!ÚqîïTSlÄc«fªV…›yá8ö·»úçê?ÜۉDZÖyaÈ'\–XrïM‰ëÖÁ2ôžþë_S ^#-¾†Œ4uq•m¾;QbÏv%BöaÏÈ㬬æâqÔj™ÎnG—äó~ÍG½­´½\+"ðév‡„^èÃáù,=<¨sžœÉkŠè‘$$üAÎ@ ð“rwû}ð‚%³Õ™øvjá/JÄ/#¨„ÈÈ$×L>5ûè=^..>.p^·#,̾,à('W;Â_½7ŒG§ç¤_šçßYâ]41ãŸZÔ£cA¢#$èsP)#,P.糡€)´ïˆüwèå½ÁCªP«"ÔZ|, w7@ôoBàF(Y€ DàÙû£¯¾P­Â&â’ÆdKƒµÊGU¶@²AФ KƒT&c§E…úܰ"«dÕ(Ó€¼EÒÆec1|#ĉeSu-~Ô–3 Ðf¨;FP$½CÒÄ ›¤Ü˵%íò»¯óäââÌö&·.h0lLi2IžÓº•ž=!ãÃ/ç½_ñØÐà|çK[˜£$1Qÿ?é|õ‚ÏÝá)†"%¯Jt4ÜiCƒ.|"yWÀzNzvßì~4ToÁ¾b–Ce¤…µÓ¾õGXd>Ü'Ow†ãǨ°==’J4”R$R0R"ƒT³ZfJbÇê¼üÛìg³®ª¨J‹#ERxtçå›…ÀëÎG‹À醰ËÂÍàxL¬.ø†º°·L›q¡‘ ^5d…!"‚æœh©AEY+Àº©~5&ReM>Xá·n›¢§¨ñ½0#Sq‘A'i±Æ†ÍýÚwÿvè;ãÌÖá˘äi2é7Ú•- °œ‹¢b$’S¡EŽ´v35ôï2S°6âàðI´Dq#$Éz9Q#-‘TtL6ÍRp@$Bƒ¾¸“b²¥¬éºŽŠ6´P¾ 6FÊ&ë@éUdýXšLš`׳0¯Å(M«B\ìuóÝ­ù‘Üs¯7!Á¢Dö=ô£ÕÕ†³;HI‚#Z1'˜•O]C8)Íà©w&²²1gº‹/Nº‚m Jã9'zy#àÖŒ‰ÅWãb¿#-ìªÝSnhÚ×ÂÜÖɬm¯›¬ªümºZØ­¨É1¤`bA¡1²Â|ÎɘˆFm¾úl”š0W™ÑI²²Š””b(Í´b\Ýš¼Ô’pî©Lƒ±£Ò8¯HZ<.‹…»ïܯ}¥ŽSÀìñ Si4;O¡®U‘,Nˆ‹ƒÐØøï‘oã\H':ú®Pógqk߇©õÊÇ…O#<§·ÆàŠt’sH#R# °‘dbP<¡6#,‚ÃÁ!ÇÀ¼ï’£œ¼jk˳0ÈÔë–d^ÓÃPK=ÄoMXÅÎîMž¨r®#,VØ£j3BI5_IwC:!/7aÕ#,Ò…9-þµ¬„û+ë±A:­‡Bç禙å I\)ËMÅ”`…ƒÓ[›[©lm±P›;­oZó÷/˜±´¯”ºQ¥Ý«vÉëÜûÝáôÑ8gOv³³ ;ÁQª­xuÉ™"0¨Š°¥Ä›\òQ&"N‰™É4Ö¶¡Èd!°ÎÄVáY¾¸:çÊþ3דÆE%B"l„Ô HI#,^öàŠé¶×Þ³cm€3é­°A`˜gkîÁ(x±f5‰Cå^4–”,ºHTa° ¸nn{ÞT5é0Ñk¸Õ¤;+#$cVšhÄ’1¯yÛÊ­&®Í+™×Ã(ÒjÅK{wµçš óöÙåR"-0¦E Zƒ§ê:àÊ$U’,€¸gpBdqåÞå€cÝ_=—½ÓÑ#$w!å80yrûs¨Ô=gnýf Q¥ýns(c#,ñ{©ç)#|#-„‘ZI+ÆÙ̈“ï+«»ó^÷²µr\®¦áž÷“q×f«»¥$±ÐÜÈÆÑaP§ŒÔ_@€÷”ôöV™NpV”B¿#,A–#,9ò°ø‰I’‹â l7~vbò}Bsp4ÌïãS%{FBœÛBxUy3D’˜Í"È™Ó:!‰H²s¡æó7z#²™&€q£c™k#U@T¢yÂ|cpéÐ×ÒŽº`,Û× K7fÎÀú3¾ÏÄ3W\¦«]ìÆœÝÖFÌe²’p޲~liˆCTÊ„›X~®HÌ%–J½sÓ£ÄÒcÆ1¸Ó},qáÃ0î‡OIA¡½M3ŸÓíâhõs$³21¶zÃj®¾ÙÄ‘¾œ;™Ý»è=-òÃbŸKÌLŸk­þ6×x„9Ëá¦×4ëˆBèÓØLPÒéN÷6¦Ÿgû¾ùÜÝså ¹6÷fX7ð¸Ü$IÕ¡¿@ÓA6¶qßÙ·sô:s^œ,˜$#-c"ŠtæÎÜ÷³¼]•èå“ÒFärG#cÑÒ?bHvÉ” „X  â¡J…§}Ôb…ÐRù´1ótرâÂ^ÔgÓs*îwéò¹×Ÿú6»/¥DtÞB‡|ÅE5å_TÌ’ÙÚÏh…U#'¿=6;…’û®˜¿¬kÐÒÂ(o¾úE?=GÙ¾‰›!ÕÎp&á¾Û[‘-E¸»lÄ1L`@»Nü61›ÅbƒÜTV©aÈàÙAÁì~Ä*«ºtàÊg³HªÚ±C]qÏN+ÑáÕÔ¦ƒÃN¡ÕLu[ä¥g©ÊZ;ËÚ`¤ 2ó׉g0ÔìgÍã`qB†m¯€ri’;ó®ÉÚDå”fz;ÎZÕ·-ȧCìùèºñ#-:`g* sˆõAÊï‰S â¥-ÖPzjÖÒÖ¶×¢;ˆ†’5‰vT„5‘ik’®ƒ‹’H‚1#,´¢h©<ûמfØÒc)±š\6Ób.D.¯ÌÚ°£lˆ‘³6wÊ3vwÜà}F¼ )y8™.bTæ‰H¥›°&2 áÓó¯+{&ŒÍCmÜt—ZeêçÛWáŠ^åÙÅdg6AfÜhzTìä ä´î©à²‚©(âëUø¹ÔdàÂ$^nvÕB]ÆÌ‹Ôô#-/6„|—Oíã~˜B7'£^O¼“6,3q;™biºÃ–ùÔ™È8ù` ïÊE¹ï›4^'+|”>œËXØŠ À‹¥½|òûøB ‘ïZlnE(ž$ƒº'„Y$S"p%Dú4î绿v3Pݵ@úC‚`–fŸ§&ÿ$$´l´.ïslÂ2aiÆña 8)Ÿãb‚>ÝAp`¥n­2V-ÒŒ &›ˆwˆë?£äj¢Ž/$èÂßc®PË Þ÷Ù}Î| 3¦Æ/HwcÛøÉŠþnÒ#-QÍOXý@ M6˜ø½’w⟢’¡ÌÃO»~ÿŸ.†ŽéiÚ+¯8ï„ç!w™IPMÞçø£x–-¬ÓñáoÛÕ€b΢o¹2PwbWpVr«˜E@ /ßëûtîsÚï(¾XOâó0ᙽKù­YÕ¢0mŒÓ14”“R›à}vøÂè–ZXZ™µˆ}r­@éÎZÉmΉaýc:ކýj +I÷ÕÊGWEçZœõé qW"°ÿ!ŠõC$1¯öyD/Í8oѲح$d033xg ÀÌÍü¿Ñæöïû‡Éü²ÑW¨ÿ'ýhWW[mgà¡O³ï˜š¼9¾¿åâ¾ZÛB±µq«Ñm¶½ñR|ï_YònŸÚ‹"ý Åò﵎øîÖÁ–¸4#$Iìo­³uÐý^›À¥QŒUF“€1)õròÕQý“b!Dåµµ óø„‹>Ú¢UJ„â?!?ÉúÿŸßΧ/–¢±EO¿ïÌå:ñ–_ïät)#EL)yPYûûû{ºw÷ñŒçЬïˆo<`ð‰PûH´A„R$à†.§ø6#t"-%§ö_yŒ3ó/÷úª‰éLÅ¢jÏÂqcéúZ^ÿp$™ñé#¬“¾nI\í!(C#üNPæ”(D?ÏÏó,ùýék׌m+¯p!"ÂwÛ·]€Öi» üºØÚY¾p­®Í9ÉÕÑO£^ûyìÙ4~ô?GûÈ\U§ƒP¿Û~Û‹tÛºÛ»dðû7ßÝJmÑÜHéΪU”‹õpi"ÄÓAD\ˆ§CJ#,ë6#,Hc×ø4±\ÎÉž·F#,=iFP†R¯ÝkxVCâ#œ:ª£BG©£ãìÛáTûh²WÔYJaÎ:}ÿ—?Ä5&Nˆ2}­0aÑ”²)XÀ*,(š’o0;ÿáøÓì~Oǧ6ŒéÞÙm é£ÇKŒ“:H_‰³€Û“Ș¨LÓâ/4\H¾ß1©znTÆš Ñö¢¨ºQSΊ,RÓüçe÷|´üﱪ¯6úu£ 8U!R·Û»t « ?Ê¢F¿W#,aÔcôñóñaßüг5jg9†mzŒ™¼kÓá´+ hâ…Ë„ݯÕȹö.³,úö B ÍçǺòÂ^"O5½:ÏÅY#-<¡¿Sù¥$MmOœÏD¯¿ÔÔ€1ŽýlþŸî“%%–ªò¹Fü4*׊èAÓþp„"¡ËCŽ :oCäíê¬n-´±8‰Þý¤ãä­ëóhûþ2LÙxÒðò1!$"#,l Åü¼û9uf—±“kf„zùÎŪC¿o)£Ï®3MÊëŽ)y¼¨6Åòë=6ü-íª‡NÑ´ü—RæñY°àm[6v w#3¿Û;C¸üßéL‘Ð9~ÞiŸÂÙÇÉgšÛéäKúÔ»9QY^E½¤-TˆM ¶:“•¿'ä?M…üZtYêû}+e#,Þ^¯?ÕÇØ#$“EÙ¨?ÅVÞžÞlˆ½¨#$ÄoEñóÓ#-ª…NR—¥í!áãõrs*cÑ»Òz8üÍj­¥ôjú#,|ü{| ™™ƒž¤Ö©^ó[s¬èC1­ñÖàwnËôM?]G¯vý•íèÝÍó­Ùquörc^8Â1²OE¾¨øWÂ;qúV ¦dÆÓ~z¯)¸syºPÉ4#¢#$š#-“ÛÕêù,ÑÑ,ÅÈqµœÜýéË[[å®#$fÈ5lœßÜéé2¼zIÕÅM#$0ï7q8¬cö/ýínäVI úgàÎɼˆo4#-â„"˜¨²ÿ£«»¹2É¿nð¼–Ý(‡–?%<<þXÅÕWj‰t·ü©³;î"a–Vüãà#$Ç eŒ-”l‘8º™#$0ø_]&Ô3Ï.ÞB{#AÒÁÄÌ&mdÌ9ëÂÁ6Wàîð³Q9¼'8‘óUWGŸC3BŒæòM×Ô•«}\4e™QW =wJQRff ú;¶1¬I×½½§«ÞsÒ…·Åem²®á²Ûñ³~#lGÞÆXg˜hÀ4lû·G=³›ÇzæffÂ)#$1YÞ­,kº­ªCìÁž:oÒ!@<} š–L¯·ñmŒ5¼mÊâž”Fë”]~­b¥Ñw@ beaî#ö*¬cMã_1¬'*¥lªœþzçnË+¼òc«Ù²Ê«ËÄ#$Å-‚îˆ@L$nÍ+ÉC~èúùª3«ò³=ǯëÃ>^K†cZÏdÚÚý+åZ[3³-;¶aÉÂ8_:ŸOØ… +ÔYmñʵÓúí¶ ÛnÚ©~ürßUòÁ;Qþ_zj{\º;áý0÷=hû)7½{ì>„Ô^‰Zwc%Kã%IisTG\¶µïo&Ûëßçô²‘Fñù;ô¢-VÁîbŸ­ý+¬ü¸×ß3[¢/¿±Ü‰x/4Wá—›ãÕ´Üq­£íWYƒç—ðŒë^o½aË"X‡3{h<“ô§l“@ü޵ʇ’s%7NIÝ#:ÇhAÂþ M<òѧžÌ‘wËÉçÊRÂÞûTê®×+{¯¶| ´§’?ÇeVÇž#-ß(ª_D%Í2„+ |<"»Úêæî!¥#-N~#-Á ­ÍA"ã… Ÿ(P\GBÀªê/†=6rÓþç„ÊÝ,¯Š–œ>ÓÈ™Äêgöêãk#§·Â ¾Ðæâqz&èC®žÆ%ýÿvñ/´dw¼j8ÏE~‰úÿÅ®8ƒ®º.y¼NL·peŒsÿ~RúÿN¥xÛ¢‰ã?r–‡±Ä½hÚG!®ýnÈUí´vËŠ bRÂÑüH!L*æ›Ï¨îúΌnjõÁ¿¤é´VrséYèäÄîø‡¯Æ³Q~jq.-üâŽÝ5Ç•g]®uOP–;Áûc4ãØçÙMøwíçˆ^#ÔÆ5ªózëôQŠ‹´M–ƒ>P‚Y!¨ÏÃ>›yönnª«»É»ë½«Ö«Ã5/€×ÚÓx©ñð€n”¶#ƒÆ2ºeayŠ%½æWÝa5¿Ü²0±>ׄÌpÙ(Å'›š!cQ^í>{,i9®¾e-68’?W9¶ ¡æ<6§Ê÷ê`5QjRw)é—Þæ¡Æ­ˆSí9Q>±ø³ý¡Í„Å8ŽÞ¦áMöu#,2ŠsQ,¡%øÈµÚG}Íj“¿¦§àÆøí´l–ìæ{|S±õ´¾(‡~è>øœñDÌáòKsïÖÆ‡ID+šH­Ûwóã¹ÄžRŽ9w™±“Rc~ÏŸ¥™|ºsO˜²´3¨wBoã{á ž8Öümžó¢ØëN°áBt(Ž’N祿´R1—ù;Šm£ã‚«.éÝç”:¹ìÓàU¶^ì¤ä÷?§Gùèé¥÷ú<›? ŽÍ(:8ôŠ×‘Ÿë çNaaÞQ}ßçÇìæÙœs89Ìzò…£!–uürù ãÖš!«ÉýÌùík]púe-v£Ûobç)c¯c×o§‹u²^ï1k;Â;o}“ÂO4YžËã_*Î#,cUÊK Ü£Óôûã­piÚv§hDzÄï5„ÔÉ Äi.$ÇÏø8b‚ÔÕO¥íR½ûžBT@ZŒ0èÝK,TªÄ芓w‘ Êyv{x2_»"§ø"”÷üî‘ÛDíãrëñÇ‚³ãñ‡ŒÛ»æcÏÍà±Ö0ieŒY§NØo.7á#-½Òr¸ŽsÞ;á9>|`„nþõT}jÒÛŸ""Óõ²N]îÇàøêçovŒEfCe™g©uÏf£ý}©¼»m¯¥7†Ü0F1ÒêöŠë›í:8Š‚¿0{Ù›]xMvãý®âið]n:÷ñ·Ûát=ˆ¥M´~†¸*£"Ä9æ4ï8PY«)~žîžs­sZ²ø[}Ø û>ÎÄcÎ  g½ Ÿ“0wÎkŸÚ©+g}cioYæ;*ÐJEä¼]4ce×tÁåc…#-115º9Ც&S"J»;Çqí;sÙ‘4b=I@åˆDÝî ý.З²®ÁM%ÁÞ×í€û'q´®dI9d6C±«“H¹‘ |Ò!˜N¬:#w;ăo+œ1¶pœ³Êëâr±Zù°äéÇÎá>Ï«¥7HqOW…oÖ±„¤¦C.Ãòz3"pgF)»a…+äÊ{w­‡¿kæÓê#,>¯À×o8žE·¿Kã:Ôß2 aÌ´ÎÏ=f9pÛœV6’äqø99-´Ž¿³˜üSïÚÎ5Joí*`³Â¾÷¿zÓüÕOÎ¥)†±œ[ºÜ†¾~wƒD5Å×»05‡£úzéŠ]ÝØ÷ø9‘áÌ»Ìóôí}pg§j‰ÑOò øÁxr[ÎsIµ#-磔Άü]Î]Ÿ)“ø´g‹Âý˜=:íüŽk„aØ//´mõqŽSV]š“NN1P>Ôõ'«5 èš0E|ÁßV6O]ùUô°àù¸½2DÛa#-îqW:'X2 ö‰Ô:é >j-áïœQ俦=]žz›=Gñ8~Oî$pÁ26õ<ÐáÔð5 þ]cCÊæ¸¤³Ÿ÷ô',ŠEMˆì}‚f£mq/!{§>Aãåv~çg”^ ΘšoŸêxüêåvÿLŠÉ!(¦ ØÏ£­ŽhØî}IP¬åÊÿî>CüË'E{(ÿ™|€äDAè¢3ï?_[Ýì ÚDþÖr ÐAæÞHMóžë×üç þ* îç-ÌŽ±›Ëå?ÙÎÛ{K)óe‡¾<=OËË ûhÙQªGîŒÖ·:¯x)¦tÇÜá¼N&ïN”#,µ¯eöýÿ.Ëo;“Tyˆ::ºÈžÝg–Ô³–åt§eÝ4j¶±Zh!fì14ˆ4ÿªŒ¡ŒynßFŸƒ6‹ÏTøþ¤ô{ÊÈ"`á!3gd`ié”-Âæ ÓM ݦù·œæ>š3kI±<à0AÄâûèüF°üK·H}®xB¤ûºx÷WJQ·„"‰ø~§üžTòw‚UiqÈÝ¥%kRJ'lgø|u®’ ÔqŸ¤ªW-oQô¨¥±$Þˆd‰nùdð—êq}üÅGãV½×î=:Zo¡ èz·?Â0åF$[EÈ:cÅï{zz+|ßè:y€úRUÐŒ#-Ð…—Ý+·Ù¡DÌ뤣¦§"£Ž;hèПEPÓ’ÆÌZXÜ—¾«fÇIùY­¹UøÖöø[Ó{½œÌuÓéÞO;8;é'•x¤—Ò‹^ˆç®{ªÕåæä1Ð!²4¥öèI&èE*+ƒ.Ϋl!8/÷ÿ£‡›ëç¹äv{ÍQÌ€,aìeËbT;;>8\@”‡§†M’z|» Â#íÌž$êLÀ~§1by_5¸ãQ…îÿÈU÷OÇÝÓoY»ÃÇMbr¤4"§9yKÜm44ÑóQH¢ã|áˆ|žtFf¹k‡ÁwF}.vs(†©v$þºr#$ØN&âx“_£E”ÛS”Ž”N=ÍÁ#,¿¨•Õkã»Ê+?¢•½ø§Õ &Äö‚„¤°Á¯tr½o5ŽTò ‡|£®öº­öY äÉÛqµ²ÅÀ¹=VÞ“ìÝ6¢€Œ°pÎMðï#,ÈÑŸ»ß‹(4½g7[¥C¿#,Ãq4ͲÊâkº“Èl÷fNÇÇep•?»Nn„Ó‘ÔgFë^«a%SSõ窖ê¼×x”zŸãvv ’åLÉÑã½Ùùö¿êó£Ñ M*€O[DÏJ‡ƒ0áòïSô³WqC©!ÈÑÁÚ¸ÈæõRÊ*Ã_gú' 4-¯¯+gì‘̿˜Ë(—&«‹ªÚïú:ž³À¶Ų—§Ëð=:ø/‘oLJ%/‹ñƒØ˜…!0‰ç%¶©’j-жL!4 ê¨<”P•m#-)iµ±e}ÝVq£÷òÛaì¥v~ý9i™AéQ9{SL8J½¤IQ0æ!¥®ñÄòÆ ôS9—Žg|LZ¼a,T²Œ#,µê¾ýVaêEô°4`IU^É`ÝÒEã—JÖÐé2ht© ùwÜ›GÅ9Ói–èØ²”Ó¦p„1•ИàƒúzMóÓ–0vþ>(Øx©Ÿ+×;8z{s^SE54–ÔÊ2Èò¦/ÆxÎ&ë/' ŽPo’Ç­K4ãš&dõúÜxZY-Ϸʾ6–ë„êkŒïuZ7s$ aäMKPø¬ÌÄöÅ)Þ Ò pSޤkûžLÍÖÝŸ~¹¿‚]7ÇdÎ_Ýa…òöæ’™…Û/nœzcfêBÍ­æå{áuý}µZ÷õÄMεkÏË®§{gß®¢]þ1ò^>Q«­gÈwÛ‹'^ ÍÆ6ó[­ZÔÓˆ?c|ôiïìÁdš#,Rìþ7r#ñ¯íܟוœú›onïÛßǦº^ܾó¶é½çRJÓ²mß>¿!==:ÉUŠè›éòŽh¾ß¼ùyA¹$Í]—TG\ö‡½ŸÇowéÎCëŒ?h‡S·Dô?^D‰Ž¸$훇¦—Í܈²&ä˲nŠÇR±°ÂÍ ¬M§<±cоšqø´táäGମ¢£U+šo‰]kÀpùÛfŽQl^7vVüî»Û¼&Y·ìÓºq0ÕÔ9 £€‡€5*û¤X¬ÙÄ@Þ5sˆEá´æ<`Ù’fhy4†e81—·¯²Pb" ??t*´ø¯ö«>äçÏZÝ¡nÊåDª?>xnçjµ|Þ zŤ|¿ÚN{)Ÿ¹ó3S#-ÅP?ãùT´‰eÇ˺ÀÔLÐÑñ{ÚRtÔ£Ëè­Òo½MFnð䩜s>Rétzäqÿ\¡·¥#-ÑÎÓ’vË&áÇåÄ[ë9οw=`»§ì!1rõ“"òvϺÎÒ(hg_ 4ÒÁgá’šxÅp¯¼°©gL{Ùþêºd¼]MCjÅÚÒLíÐlÚÅ„„¯Oh›J|^›8Wú¾b•mrÆüØ' ˜îçÀÒbu®P·×n'¯…«)"絞›ï-™ðù=:º³ÂËÜÉ@/g‰„a¤ö¨©k˜ñqhöÆ9;=SKùg3æ¾Ó:6Ó€#ØYo­îô\$Ü™ÈI¹Íƒz1k}–ø›ÍèÝŽêšè[4âä—,ÆÝL”·mM;fSº”m>ï’ãÈX÷—3]#$v=+—ÓÎ0ÅtjÎ|ì&w9¥ÚI!&I$¹®Á¦Î;/;êo/WvüOAa~CSÇZäw9N-Q„K-–[rc–ùBór6Û!1{²„ÏÏ?‰£`¬y#OÒÆÖS¡m¶ï™G«,:ÎØcŠ@ûê\Åú R¾»Å|m³xG#,º£D®ž\+˜_‘=ü›£âð¯Mel@® C0Ô÷k£ñ?ŠüûàĬî {n•NÔiëѽ£vè7e^:™ëfÏÛ8ðjKWOáû¢qͨŴOXú;½bÒ2ÇÑGÑmùîü,þ:¼÷›¡ÙÐk#,žh™òqÍ™S\d®:e!tÞðÎ\üQ–†°¦9Ç+˜â¸ìº»Ö¤ØÎ»;ñÃRÊ·Ê=·'5"­#,6Þx„×K½ÍÔà1P¶ŒY¾Œ-Õ ¼¦„ʳMUOŸ;±E+×V¨ì½é¿ÛôËzžŸ’êÇN¾©èe§2¨Ý¥UDõ<¬Òå–*2–’³ wƒ›¦åIÊ8b‹êyXÙ>‡ôšN#,“oyÏômwË*óÚB0Z÷ÂÕL[`ËBdÆ×!¶Ê•#ÄäJ&ÍPž;ûK8È‘}Ú·ÖÆ7bUT1•D1ñeJ¢‰-YW­̤Åíĺ% *j¥¶j«¹êƦ,¶åCñ`Õ´“4ÂÑá3€PŽP5ç¶QZ;3µˆc/-ÄÝzÇHÏÙäÎþäm=‹b×s—mù$£”ìH½Ëææk /ä!ê¹àß+÷EIÙ¶…?¦®¿†¿50ñÈ–ú­ÆU®ÇJ…úMïóyv-I¹¹úX¯‰?]ré´•qt{%enÜc±ÙÇÚú)ïÇž ŸKÙ{¯”ãÌ–›þ¯C„Pûy´@&!Dƒ•µXšË MÚ1¯T¼f‹´?V£z%_CÛÇÇÆš;rØ(ê펾¢ì-ì-º-ù¼#-÷WôF®Ê]ÅtV&Æ·¨KwÌJ&l æ[_¿G[#,J2vvòŠÐÑÑt ˆÍú?›Ç8Ú¤Úª"xfhÆfë*¾!¿K³t¦#Ð÷‚¶šwÂR·-gN®=פ²ÄÏ¡ðAÂkªóv#-9øÍ¶Æ¿Y¶c£iÎÑ8p»}੾ڣ˻µÞ‡6it@íod-A2þ%‹~~Xéš/¢}=!Òçµ'ä«|°ë˜Öl¾«":}òפ3Ó^×\jêJ¸e¬Ë8Õ8hO<×Âtl4±ªÌ#,c±Ã!Ëî®#-Òõäâz渴 A¼Iš³m3¹¬½,­½vÛ{hÕÊаÈË}eµ˜<%„mkÎ7/ß¶qãÇC@¥»ví”¶ h[Ì ZT5T=©‹èU;iPó…7ɤ^G°>?+?»Ô¥Ûãnq“Qï|G2z2C ã×N= ¤Ã›øøùqÈ¥nf»]Ÿ#$<-+2;­ª‰ò! B0&N tttvKEº9V‹r1¨W Ç­öÐ¯Ž¼,¥5vußÇvßsܬpÒ›PÊ ©›ç8½=ïí.‚µÒe½º¹©pÍmÚOœãÍKÿy€I½¿·å³›òù=§Çæ>/³ë5 §‰ÖàòÓÅ Bqm$~¾)j}"Q¼N›Ps¾4X¬ƒèZ0Æ×ŒÅ(ÀTq#,ÐÌ kŠ£ÒãÒUNÐ1ä×Ph÷‰¹;AÅèçòËFWßóÿØéhåü‹ñüæôÞp†‡ÅQûÿÛáa->«ÿºÊ¦`¾°ö@»«í' Mée¤ta{‘=qk‚ŒXP¥Oú³í÷à$Ñ£33"áÀ¹B àwæIØ?5ë{réþeØôøù^$:BsF} ãÃÎ>uåUþ&ù{üá¹¾„bìî”áãöƒÛÆÐ\ì×Öýomû÷q6‰íxxÕf³.¨’—AC•°Atì#$²ÚÉ9V)»F†¿ü¹g·Ûö}iýÿîeš¿Š2Ê Cc„ ì$Îqtnçî–ïþ|_ÄË÷Z´â},[êœúBA”™%Ui¥‹¿$.\¼ü#,[¢_:Zƒï+[÷iÚIÚ{À°W+ôbÃýÃpÉ =!æ‡#-ýa˳ü…ÁáØrÙª‹Fab`”¸œñ¿–mHÝë#-þ¦ÃP4s°f”;˜Hn ¡ÇÇb`Ó¸-Eb‹±#$ÍöÑ ÍÖ¡ðž@ãÁ•!ëíU÷6z†/ô°3ã¥J®7-.¯ÐØ—[#,km¬m4´Í›Væ†ðbõ 4ûÉ57ŽíèÚ£bD.¡øuºÁllŠƒu,%‘Œœ°YH—l9ZÁï. ½â´¥²@÷{‹fÈ@Šsw2¥{#,³$t©V7GqlÉ“$j«€†-ˆÐZ|¤?&O?Ý#-$C—¡øãáB3>ÞÔ$$íïóazýA8‰1Ëý9FèQMÖðË8rýš•¬j($¥\B€èa¿Û?=³™OlÀq’7Ó¦Xo0{(â RZì?}ÖHÕ÷Jø &ƒ½£Dy¡Vó(DñÐ-<‚?Zu—YÑPÈ#,#-Ty”dtX¦•Åg~øÃaŸÿÁÙ1&?Š`2&fI&”–‡Ëǵ0wÒR°Áí÷úh\ª÷Ûáߊi37h7è{­s-K»¬ùd¦\š¦E¯(4ñ›°Þ%8Õ#$’¤yÆ1 ÜpD‡ P˜Ñæäùý¼>Z¨Æ#-äÍæV¬?uG¡›ì­”Õ`RH³ÒX!æÊˆú´› ¤£>ü—À#- j2K¼gc»”Ã6¬Ì`@UîûGw`Ä9Okf1LÿHôÓȨ$ªa %×ÏÁ½Îà†å4‰ e8Âi€ÕÓ2͇týÓ…ˆHÎ7¼OD%íÔ¥ävµ?»¥—"سu×JŠº™G9Îj•UGMj´†ÇËŒšBsÝŒqâSb?n7ï=æ\^tψlõs‡R2 pÍëÀqÈÊŽgQšéƒÃèÓVaÜhM¨l E™Éò#K{Oe¸UÌùOÀý¡2~Í‚‡´¸fÒÿû!+#-h„ß¿ûoõXÛÛ¸Øq“rÖ$õ]IŸŽ~Ä—S_îŠÿ\üHhÀÊf¶ˆ(Åëñ?1“ë«Ôl<>ÿèC³À?Oͦ­MæŠC#-rÊ,L„ŸwÕèúþïpea~×?R~³ÆâÁX†§fÊÑ·‰Ro€‘â ÓCž¿FàCdglÿ¡œN ý$¿Óg©½cV”E‘ƒE¼gW}/‚UÂk!°áÅÐlbƒHBÈ74?ˆ.íáþDH™\Þ%g¤2M†‚:™lübþoÍÿhnûŸË×ÞA ÑCLŠõv)¼àŸ£%NÁ‘Їgq ÑÂ]z¤µÆä dP„ øà;C4 ”7£‹Y#-°†¡DýIgÄÌÀnY#,ÏuX¤ŸR¦®°ùçy¶L…°ð\9™ް*T GC0¦ â)#–¥4f¦óffj‹MägI'_HfdÆnˆ}ÄöÌ*Ãɨ…"k­ÏôðUõ³s‚uíEè –F=ýyÉ#$ŽPõ òØÍë a(̺˜b4#,‰$GÄ̇ç~οÀèþP^ƒioZ`/`àop~×Ò“±öH…ñ`tsúŸÑaÃt÷Þ©7¦JM3"‰1ŽB#,ƒ`ý:Ç’Í_íûîÎìés ãq¸EŒ¦h•ÖFÖhÖiëR#,2ê”Ü#,>¬ÓÓ’RïñÖY•ÊR!¢Î$uä’I&j£Mb“2ëSRZZÛi¼ÍfjË^³™Y„uã.¡Ž½aG‰ê™¬ ±#, 7„Æj—xd{Ìuc¨Ã@ë(Úˆdf›i¯âÂ59{þJS#$‚µpî”!NM¶#$Ú ÆŸ$ÚL|•¬6Ìš£aÅ éòsC ë²ƒ|Ž«þ—ø}õíê¶ÁÔàÌÒEB£÷;¯²W2Կ׳WHŒgž]æJKöuýEÔÐB@#w±;#,Ûè`ÍÛ,ƒ[‘zDiâ‚7¡éÛRÉw¨I9ŒQHLûCêå¾%ÏØ~Ëôþóf—4>°¼ÛæZú- £#-5ŽN—i“¡UÞ¬³X0IA#$ä)l¢“húÁ“$éÿ–ÜkÉ¡d­î.I&؆‡…ƒ¬¤¦³·DÊùfeâQ>o½KK’¦µ–šÒm§)|¹ìpeú#-|Ï(ÄéÜzâ’#üwîÕÖ/‰™X=²àeê|2Sxž Íþ0µ‹68o¡Ë'NúRçvôàÅ$‘$XÉ#,0?MûUӌݩMÊ".e Ä#-ÕI ü:ößÝ‚»>ò—ï?‘§Ù y¤·ÊÐ<ð-¨ò²g3öüE&Ò·zaŽŽØü }9SûÂÂΡ‹h$¸Åù‰ ‹/ÆÒoà,"0ø»v ˆxŒŽ©fÐ6vìå-HT$!!¶ã#U3C}ÀöMYn´ÚâÍD8EV÷fÉeBñ@0bRõ%)Q2?ƒ¾áõ}gzÚæšWŽá£ý,åÐÍØ˜ÌÛÔǤ5úºV±w=ìуF(qÒHô›ÆÚ·2Oxb¶(¡Ñ¸„>Rb¡iþ …±H|#$¢b€Ó¸E „À1úûü_ 1´bd/xjåavâgÛp äF ºŸçˆ`Gï .,ý»º0Ëï¡Èˆæ#$!qí¾‘A¹"¾Èñ$ãÿo6‡?õ=[±ÈH §³æ” "“å_uªO»‹^ð–ö½À)g×B DQÅÌ1Ì@ÐHꙃ{F9ÐÜþ¦ü&µ<îaÞs§ë^†Ï¸l–çôè›°È¡ˆ&`C{Í ;8@áóµ ®n##$ö!©‚5!3ÓßµûÓñ7U9ÅÜÆúáúÀ÷;·x§ç8ˆsöªwR‚$n7¾]Ê 13J6Êv¤×Ç«®»üž' 2kÙÉ@å:#-³¸ÖMˆ8-ùÐI×ò.ìy7(C…½½Ç­ ó  Š@‡ÄîO`Yܯòaq|UCÜdP »Ñ­Ïé|ÿ&‡ 3 PGÒ½pCš@u Í̓wʘMÚÎw·„ —އ®Méˆôño#ìŸ)ó#,ú\õZÍ369 AÝé# hw7rXxžy©$ Pà™Ææ#-ƒáI7-,8½Hfçô#ªÑã\2`È.¦È|Í@³÷b2I$ú.*žÑø¾ñ»Q#-±Yè6€,Ûêý*þtŠIøûDNØû}^¾!æÿ.ÒŸ'¯ï”wûZ 'cWòÐÖ£NTc'"³2øçlÇÍ ®iFí*Bäôh' U½_¿Š;…ȹWà|*JÀá5XiÅñÈ,Bvmcè*¿\gÔÆI>êF†àVø vg›œ˜,O¤+b/QjùŽÿ£ó.fEr†,šl[P¼ZRy’¬ˆ^ªÔDiQò’ŽMÈ€Ë>0K‰µU4¢pᱬ*蘑¡)—úR¢qÙØÖ¡CˆJ}žÏ¾C1ÍTF2 "¨•!g^¼ù»m ´f9¼H­ÈÛGš-Ž,%Ϭ® àû9{wWK™Ì"¯!÷ZÌñ†DY>]aBIh´Ó/åÙ=‰àz%Ø–5QDü»Nó>Q®b•JÁ®ç)Ó¼àùø|f§ÑyÍ<ÍñfõöiŽGw.‰Öˆ+惩qe¡µÙ=,B¶Ì*#$v ítäbqÙälŸä}ÿjýYçñì LjóÄ5HÆÕ$øIox¡¶ø°49'ù¥Eý:¤¡_÷¸!°Ømá¥AÀÓYÙ´ á‹’7µ-PÀhw¹€áú¼|½OÄõ/£î»Éè}”xÂgaM"t h”ýwŦOyêD®‹%ZßꪶÉ b®`£÷¦~çŠò5|†ï|¡SÜK'É!;åÄã÷~/›Èà#-x±•J#-¤*S§ÉO$è>Ð2û²EÌ"‡ ´ÚìÆ¿(¨Ç*hè©-#ð®0“·tÍÑlе?Ùô[O^R½ÈÈÀ§ÏS‡Dã×Ò8>€±ð.X)Jó#- G ‹ûLϪV­Ѐ’’¯÷8ÂýÐ˃V&.}ú®MÖf'ŸK_cEZúØ:F?ªkxXj—råxoQoZQ‰qùщS9•¨#$6’L d]P·5#$?|PAHK#$Ì@eÊ€³P*BM“XɈ"ŠEÀb$KÙRE¸‹ˆ$„€0‚ ˆ”U Ds]T3Ы%D8€tK#$Elû½é$—#º]2¥Z‰ÔØ”íå*î`r¡K4=¿ Ñð}ú™Ô­SXX¸X¨€ÈÃxH;˜RH§u÷¡»ruæ‰O¬58‡ĸPPwŒHJ  ˆ˜vüŸiÛ’G:‡´ù Æ™ŒÐÚÞ&è:&ˆ…}{D#=õ·ïƒ(Q†°*`#†ˆJ†ÈhÑd”ÉDfûÝåo¢¹ªõ\Ý"ÖÝ!²¡1ef«¶ëÏ2íiåÛÆ¸Ó$É[¼ÅÑæêóÑGÀ XÀlÐÊ#,Û̺Ž`ø¾Þ%Á((8¯¢ë§ÒÌ ñ$ZôŸI#$³˜{½/4a³¹#h‡¼Çi½ûù›fí‡ëê&,A2ªYÄdHŒ#-#$ 6.`É“²”²dÞÍ¢…ô£¡K Y‰ä¥äôJ¶¼z]Ü@ÒQè˨åyÛ=Bò!oâE¥¥éãHèÝÁ;8'êžÚ)'Er‚Î=Á™Žªq#$7’J;À¾íåû¢ÀzZN|ÁÜç¡<ý“ºÝVû­¸ÌmÒ‹¼È9#,l-¤*ƒEõ²Qíü¶{K‘}ŸË ­ŸH¬?ˆáºZ\%1AE|ùýȇãî>jzÉ*I]þ¿¶å>ϺéöGøÕHV黨ïÇÛ.ϦšO`˜êŠŸÊ¡ñ&Il™p`ÊV© ÂÂ0…¬_A£è6a­˜a'¾Û¾MÐÌáÛ«Z AMR<³Ì3P¯ !Eº*§Üqè>]RýD ¼#×G<’:”G<²rBùõÿ:õF@„jíÊA02¡I®!ü@PG¸ïwÓú‡¬©-#8ný‘éÓÁ ”ˆÒc³ŸgTJXþJ(Ü ! ­a|hð|ˆ¸Ï=JÆÖý»™›§_ñBM¦\iO ²…o@1+ Þ`äŒ#fb‹y~6û•ê¥/K»··6YND„Êäd½¶þã‰2•~o!÷nͬҖ±ï`n(ûdìÌ×ùú¦èR7Ή—}ê͵$æùý„Rzhù"Ò Ïk& "¦~ãçÜgŒ=¹Mr bÊ(‚(>Ò•VŠ£Ü9Œg#- „þÜ)ÊŽÛ÷l È¿P¿s6ÁŸ,'Ú ˜T&“IÈ~F™/¹8Xc8“‡¨H ’æ*Ä Òþ•JD\?ˆàã®Äëû¼;ß½-Óö)  :sU¤R– …ÅÀAÅç€]±ã¡Â`\zŽ¡ ÓÌ@ÐGÞ;®Oá…Ó§‡y»Š=#$ï3â¯p~W˜d*A#$vü? %ÅŠ©…ÏDMOOiÓ’a“ìߨö)ÖaØ^¨º*tZHɰ«pý¶AÁr"l#,^Aé±K×s…€ßp»"8‡ˆ!Ǥºˆ¢¬;ÌŒe|ÝçcôtN7ÌÒ9dëG®«O‡œêªõç9‹a´ëcÇĨ±!=>}οîø^†#-ÛK6ÆÙŸivÙŸ«'k*gèG±V¾DJžYÉ›¥ wDw½Ô€àऽo=¯I"©"iP¦R‘6˜rØ™Œc­¡Ý‰uÆ J?m—ö^çòæs¦qàÉ=«:ëT9TªMäÙQ\§Ê¨´§F¥‰—k#-A¬>]Â7‚•\´aö µ‡6â,w Ûü¸§#$àa—é‹ÉM·îîa&fyUØŸ[g¹BI¾UO#$>]d#“I™ÚH¾euìË®äNȬ‚5¬D(wÕ® ?1fÐíü¸–#$Ý« .=èà¤zC#’ç·tÚfŽ»‚P9ªPý„TØæqn4ŒHGB"#,^6ˆ)aˆÂJ@é8Þº­Ð|¡=Áé}D7Ïè?d{ƒãµV#,ˆ“du´ªØqxvr9rœÞíÇiª0‹¼ ”#Ô~³kíT`)<€_ƒªg<³®þ#-™Cú8ãÔ>t` ‰È=вtú¡ß4+Ÿ½Ý‚Š\…Qß‹V`RôÌàÇ›·Yâ‹}ÀsÇG›ú#$-Ó`ü¿3=´Ûáþãakn8t÷T¯fÕ¦aleìÁï…h$Æ„š˜Ù áxØÃ_tµðMÈ’çOèo+ʇÃçEç-¬ h¿Í&¹1÷ÑõŸ,4}gÙ æsU¼Iœô¯öàÙÙÖWÙÆ±œ#$ç!Ãì>*sgš6Ûw|ím:d´]¾&3_‹ìuj ’"7diz–fì,P5b,å©p&pºÔp…‚RƒlФ¨~@¶ñfM‚ p,sÅTäX~èûü нÅàLÀ~Ƈ?Ž !ŸDT 0Yÿ'`£à \zxô§SÓÞ™L!±2BÐW¸HªX5ôg'^zæÐTwüÿFæãìÐlžµÔ£¤r¬^yJ'(¶9•è¹,àJ©’Aÿ·¡‘?2>²¼÷•¨w¡ß‘t#,Ì[ª›òB„âà¡5S@nÒ4ö%Àü©#,e!{1€*¹)¢°7o–¶ëïAÞ*s¾fb)™ «¼õRR1·ËKºíçn¡â¼h ’U†ª©öB1ëã_f^+ R9Ç8!3câd9Ù9烃UÉ4ò”i·ø>ïPpoíbñƈ枸ZÐÕ†âí{·~§,d jÀ,ŠØ?Êoß.oÃ#$$££ùþ#,sðõÈÝÝa†7e‹?“‘çæ+®$ôùü~’})ò•ák¾ô>Ý>ÙhDù#,r C0.ªý‰`ƒ’yÍÖO‰öºô#$Ê#$7‡Õ7/ÏÃèº= éLß’YÀ7²Ž¿ÅBBMì#-I×ÅF±ƒjkv‚0#,N4™™"ÙYôGYþ ­É:ý_f ¹rŠÃçQQEX‘³ÙÄBIÓÕ€¯ìŸåÑŠ±Ž4@?ŸûLÂg G˜úGmOÌK—ö#-Ó.îM oÚ€öFeÊ‚W¾¬uÕÃëGŠ`ñþÎý—6CÅ ~gÒŒÑ9ÉòãËY’ŠÉãöÖdožƒ0 PtQÆ_‘ŸêÓémúíÞnŽj¦Ôº¿D>”ת‚ñ‰4í¶ÅŠ4Ùï+“<ÆB&û*„½ ô#-l†–ÀAQ‰0|O¬ìTœÂÑ‚FÈFÆèÞ€/aè·¾1®Í!¥ŒÏ¥ˆ±èŠÔ¿ÞËÜçùöu(¿¨ÿ'ˆUËIy¢íÖ¥®X„ë ü'\èÿË4Ó¥m§ÒìÁL0˜9fìáE/£Í#,~]®_ÛÅ–qmhÒ„ît ‚¹ü—ô¾»Ðò‹â#,èñüUz;µᓼ¬øSÇ[LU³>†EìÙ&æH»iœ4ÏŠÎ^¦t¤i¤£´9SW#,ñwàý„2ÓÛQñäpV6!h>쉱7p°t>ã5X'õ¦ö}¶ì$1WþþXaø9?ÆS=:¤K 4Äçä´‡çSŸëØq¿ jV`qRýwe§§D‹_\P ß ØZ!emö3!»åçcUué^U?(Ðí÷w|pÅÉ|Q  Ô º>#-%]êv3†¨•ý*â>wùì…Ñþµ–ʬèåEx9T~´Ü’Ãõ¿£˜¾o1åÐŽ¥Ø¹‡+Ë­˜œvóé·©Ú£üY#/Tì¼·ZÞx«ŠJuÕ³CÓgú_ª»Vá?šc]zîº*6)¯A ®iVÑJc´íÕ\ΙçSü²7FéFs5û³SCpßPį/Go®‡K“•¼ï'cçáQÓËo=1ÝúœhqÓ8™àvŽÚèYŸWÃ%å?ÇËð>…¾¯VžFŒãŠ-4ß¿¹-Ês§öntJP¤ž%!¹Þ=j;ó'óU)èµe*T)Ѳǩª>*¤¤˜ìM†VÆvmµ¼'|#jwSøke%_û=3~Ú>‰no?²îs®ŸP¨ÏY³0õ÷ØýiÙ/ùWÓÝOšÏ><äé#€þQ›žñtîóŒ"BMù a`v-Ùû #,û¢/Z}WßaÓ mñýøÊÔb°Ûl$Œ“›Ê‰Åèö¾ý;›ß‰§'ž³=[j€ŸÆŠI†kâsžNK8eùð·ýº\>–ûƒu÷NÜtÌÎÉ›KŸ*ì”Æ±ŠeŠ,ÇÀô ú˜ÿ—_WøGÍñŸðôvOöV<‘ êÿ’t_—øW&åÂiyþxÓÍTë¹ý‡¯¬å®†‡AKýQ?ü FO±3;>þùíjä™’#$­“‚c”€ÿ >®XŸ»ìø~_srõži°iëKÁËûq‘o]UU‡ûcê`À’ƒ"Ök¤5¥È ‡¿P,ÌûÀ²Ñ².u#-wfÀ]¹¼âQi )1B#íy'ôYx‡n“{¡ÖTª6ì2ÎÄ’_dIZ•a0l‚X,pÖz¬ÂÎÉ%¸y¬±Ú¦Ý ¡ µ"ÑàóË œ‹ƒuË}qÇ~¬]0t‹noò1h†ð#,ãa ÓˆÙ†à¬¯Çºôï[J”w#-—|$­TXS@Ög˜Y™#,L­™ý#,Žó‡ûÆÀ^‘ƒ¥RµTª5E6’jN̆÷;Ͷ7#$œažwédW)Zô'ÌúGÖÃú}Üÿc&†€XYS $Ñ,Ê#ÞwN\}"¬C×øžÍ@çU?w!z3ŽšÌ´ž=Jh'‚o;k…UjRz2#,w¤­Œˆä¯lM ìhÃoPÏž)T"j§Hë:¿.áÂL0aÀÌa Ô5† ÀXšˆÄ@ˆ …(Ô¥ "ÀpàÙ}€e÷ჶË[ã¯a»8ù†t„ˆ¬ Fe¨´¦Òüc’ØÜ¾]ætH#ÓÀÞÌõûv<3.«*i#,³í´æ/ž·ø?\÷Â~BÉÓ°%êßèòroÕø¤:Û§¿šWsèý¬cWþ¤ýœ{(l9>Š}±îõÚ]§UÝܰ€óNA ’@ɆÝK« "´lᥲA±¨ÛAp¥i²e¢TA àÐÚ`ÃÈV¢xGé (Cø§ë*ªIJ"#$¢¤äȼ¯ãÿ?ý?‹|K5”°ÊF[/áß+¬/+Yü~t4Þtnº?bUÀÁùY½>#ÒY_jáT!IWvÝ›ð<‚àq‘?¤74EUUUEôúª` !½ÙÃN—·Á¬HF’g6tïí5·úK‡QëÔÕx•QXˆoš3ó½%ht^H-Íis,h?i#,œCžO­66À͈¬4²9üB#-€‡²÷Åm«{Aó.é^CÃ$€ÚuC4…óEܱ¾T Øå$ê2Ž“@µQ÷Â<{Vq‡gÐL^¬8㬘ȗ§xØèÖ¥R\êØ2fÌ¥Ñe ´—E¢#gó•0|Íá  {†1ËŸLyBˆÈ|ý†3®xxxê3r&¥àL qŽM€íݶì>ó„i#-7À‚{…yJ‡~Wàb W`œ­båšb~'YÔ¶îlãl€:F´²p{ùcý—¥…U¯Ñ…Nd! ¦ó;¬™Á‡ØY »©W ÐÔR\ëyjI„‡ÉÄ™­#,‹õ®úvÒá0ï2$^&V»I~”Á‡ƒÖõ–9›R\⡯fuáUgG‡~Ê”öž œtg[°âå¥#$þÆë[Îîýz¸AäsmÜÌWS !"Û:a7Åç4I–ºœ–Ë5mÝ#Á’ïHàM‡È멽µå¡6­œt88_RqGyCÉ…2{–÷rþLx¼ÃiÓ²™–Ë#¸ÅŒW¦B§ùoik=aÙ ÄNƒ´7ÎX r.§‰u{\Íì,&a»š¡ôMSFÝžß~¢g׽ă'e°Q6 P4ñÈ„òhIêÃ:máWÑá×±!#,ö¡acc$Cµ} ň_G<Ù5âîV²²ÌǼA›TR¢iÔˆ™2ö¾HC^ÙëjÁÌ;C166êCi¼##-+ßra0ê!p1œLæ´ï6X‚E-t#,Ð&ä,Ð&IeÝÑ‚6&uyîN¡#$Òàº.:1®‚Ïx[œ)5ÞÃD&ÈÌk«m8¶K6‡n¢Sb\ÛUhPœh—°_³Bƒáð‡©ëô‰6q¸ÞõA( ó€îǥ腣l™>‡]Ë™Œ1†XNí#- ÃØÖdàk0‡š¥P]M’tð‚±ÈÉ<™ñ-–ç±­'c!»~÷\‡s¿æ°xb‡5ìÂ;£0=ƒ°X¨îrÞN‚Ü÷&eÃ{¦Ûˆ+9­ËuÂŽTÈå9”wß8‡g^È u´òèoƒnÛøê¬Ò3€r ^v=Çh¥‰§,ÉÀ£Áºnˆ])¶A•½Õ !Á.ã‰Õ9™™"„T±äö•tÐÍ'‹þóS&x–1)66,xzø#,qÊŠmPYÏ»´!ÀjYÂuÓ#,˜ók7J$")#@w`üÚà¼^õ„†1Ç…Y«¥·ªnضYX¦^ 4B.aÍñâ1!.»‰¹vd©Û’É‚¨*4pm'w`ÐTD9jèÔM´““:™EÓcT£5¹2¢@ì({£«›ÆxM‘‘¨‡;”ÊÆJh-2I9}uª‘¡i:y™ëÆt]Ng*8ˆp úü<5™Le±W™—fdË™™™clƲ¬Ë3$•c¶gŠå½[3ÒëÉ#,‚;0“Êq»ìÝÚÌQ¶*Wâî#o==ùêX5‹Oßå¿I«+ÍÅØ‹€þ'º“Å-ÍÎ Îîð©Èß{³šgH™ÜvÖÎ #-ïgÉ­nª¢»AœÝâYg«³›Õ\X›“3Ö¨d1ÕQ ²&†¦p‚M–`ÂÎÃæ\½Ý™˜|y88déÑ @„ˆ|BqÏ–’ƒ6‰ì:#Çòù›PÌM6†Àe„²F™% #,#,7ا—#-W|wh+wºdQñÚµ#R‹LA‹ˆX³3¤–ľœÒ1­Œ eeÝgêµ§ógTU²&Xx==a¬šrHvˆå4R‚ˆ-mÀ<ÍÂ#$ˆ#Ás芊´ls+ÌN:€NS›$¯fðò:`ÿ‚¬jн:’ƒÁØpÎe¼z#-¨b2-A ‡`jžId#,PÉ#-J}<}’ÆyIµ`N¾}8ß…¯ ï#,¢3²€nèSÀG™²Œ°¢pð;àZõü?ðQ³aÄŽé–·ls{ÏD²[!Á“‘ÍíFÖÛ®Tcô`ÇSzH ñ®#)¿ K7zšÜ›Ú~è±ÀhØ-#,»XæØòË—"Ë7c`{æ¬jy…{ËBhpQ¥Eèq„×dÌÉÎãUt‰éå´6ö¢èN’4Ŭ(d‰ €iŽ<Œä °f1ÕÔÉv‹–#-,Öß»åsˆs’’ŒÒèHFJcAaÜšˆ·&¤æI ÐË4ÆsØ`¹ÐèªL¡™ì–c˜šƒßs&þ eÜ!24XID„L`j#,‡±v[fUÉl¼ƒÞJ#,P°ÓÔ·„œ½T´U4­PÚÚêƒÎÎÀ'ÉȽQ¾ma#,êrCý¶é‘ò¾ë|üM3æ;`—’ F;މë$ÃWW´ßíÎ(âVm»Ç®MÈYˆLÓÓ—ec˜µÙ ¬=çÙõÑzð º]Ñ9'tB¡F¥åƒPó#-@Œc‘³tŒ#!ô¥PdªjPÁÇ:›hÆE’#-®Æ\jÓ#,f¶MÃQCƒˆä˜±  g>#-†“LzhŽL£4˜ÒËMkW¨ie‹Ë£”3¦†$L$lÜmaõUìÁ>PèΩÇh¨ƒÔ×Rw|j½c€ZqÂ%NÀn_vL#$X‚ "È(¤XÈŽC™™…Þ‚ÈzŽc¡èÅfÃÍÌÍ䪺J~Ïl•Ù†s6ç '[Øtˆö½Ætwy‘sc#b†Æåí 4­äía;A‡‹ª(¹—*.I¦›´ -¡<¬#,m÷ÜÖÌ—“.t<Æ*_”<õðzïJ¶vΆ}y¢Ó{^ ¬p« /ÇæõÞ…Îo4†±µÎÞPÐÚ’¸þm¯j•Ýzß«žoœ¸Ó0FA‚A#-¤8'-€Á à g¿°lÝL{Å0f@IºYVI%Êfd·"T#ÒXÃ5aGîífì¼ÀQŒÇÀ¦º€C!‡E>·qö¢¦_&^#,¢Tõ^ˆicL]Öô:Ä2rõãH²%£cS©ý0ÃÒíݶK¥bgˆé èˆ4ËœO sç9ö¢:¦ÁOÕ]ŸØ‚GñLVP¨L\DÂÌU„î•ÒìëÜÛŽk³`W^%ª èk=Vùêã)ÐXz÷áåãžÈ=‚ÐR´”>ßW Óô‘8åÓ3¸_¤=Ö[(@²$œœ b0FÍôн›ÓÔ(…£ùg"<ä„D~ˆ#-6ÄPj!ÀÝ÷,-FÝ*#Æø°èñqFdßìG·ý¬¿ôûëPÿWýUÌŸSÚqÚº|~³ÆB(" õ#-ˆªŒ„ý¯åZµ?j§²êR‹fl’Œä銲ƒ»»Õä>ÖJ¡»Ô!ãÙgò}ÇR-9c©ü~¤>°# ‰[㺉7ôVÝfe(Ö:Fž[ò HO.¸±Š–MÙH€‡NAÔd„êCpò˜B § 7Ĩµ:w_ê[¦JΗsOžTØ "RÐŒRžÛ¥ŒwkaôrÎݾ6¾wjõ¶#Lb)ÈJ)£†&!¬8þ¢¨‹t6íó:!ç+#*CË/…‡ð‘ÌÝÇ.ª/—vãÌVNáòº'FL…þQ(²è°K¯?h\ )€@ @ä‚@>&LjK"?e>ÈI›*}$*85+`Ö%;#$l’Ù鵄62v¯ãŠV÷Û¥x„±¾Œö#$ÛŸON篼þtÔvÐrbá<¢‹bˆëòaê#$`M¶ßj±µrÑT´–m*I‘ö†CÄŽ#$¾ŸÍ@d[iA6­T‹6Õºlˆ 0XƒÈÇKÿ|ù†@‡«šIíâ_ªöl—‚£G­ÆJ>,(FÚZ¡d¤ó#-ÊÛ\¼R±:Ô#,b5…5ˆ`ư†#$Ü ÝîÞ †¥Âã1´þÒ}¢™_ÒšôEâ½*RIöîÞ=m¹½›Ù¹®Ä£wv‚KË®£\·“ç›x¹ºcÕÍÒÉY]v“’øëéäëtžÃ«ÚÈQ!™Qÿ¢Çj Fc`/ã¾É6A Mk¶·é¯Ëùª·R7H#,¿Á¢YRË“ŸŒ‰ä"©0’er-¡xÜ‘K«­¿ ø‡¹;ކj§c9PÒ]QcNÿ€™£íR`ûy•ùa'“Iðšjð©0…/!d5±Öz¸ÓþðˆÑàZì’O]Xzޏ¼À\#$õ†1óëjÎïÖ¨6tùÅU‘EAÁ|ƒåÐøg¿Æ™"Àÿ‹¯:“ xRñ'zˆ‰`ä³öýŸã“¢I#-Ùº>‡ÌNôyù®9Ád«$ çñç.".ÐÄ¡æÈyƉwì4~Sùšý~îe„ª•RŠNîߨ:ø§YäÑE£°Wr{(5¥îç=B/dE  l#-üd"<…™¨²#$1"‹êTæ3)_"¡ÞË1T«e³ªl„í¨N˜bêz¢qü)lDm‰°}ý™WߌÞ)Œ½TŽÉg;6 DGŸ¸ÖðòÓåñî„*£FS¢ðFÑâ%ø™Ú´¥7ñ/Š÷[ó¡kÔm¶MµÉÍXÒM”IQŠÅf¦µbi¬–’m‹FÔš¬Œ¨¥3*´µ©m¬b¨zÎI')óüR¤é‡#,Â'šb Š)@^~ =ñ—‚900‡vùê ó}Ú±¾lCC³*&Ñ?L¹¡€ðÐØëŠ£šˆ5 QoF,˜ÉÅýNäU—E1ß%`èÄe:™&¹Öw9â1o;ej+ÛŠ„šàcSXhÛJ{¢\#-D†mÒ½”Öæ‡ô1*ʶ c[L£—¤X^`#-+ 0™*óë¼Vâ¨rtQP’1HDƒmꔪÚ#-ïz}9 qxkQ:eꨋžv´€±ã«Îzòê\¯t¼–^õj BSÐCÝtê‹ìóžš òƒ¬©€%U)½müiƒ(#-„ ð#$°/}í+íZØŠÍ#,¦m¾ÜÕÍb‰6ÅjæÜÚåW)š…*‹f`µ&¶-ˆ´ÔE"ϯ3íž#,`ÐC±¹¦MdRr„€¬ ,Š©’©Þ#$¢7Û“éê¶÷áì?Ã馚8ËHdaÅÂ'¥Ì‚{dUèPLìAt$už½ÿîöy¾ß‹›ü¿ÓoJ``æL’fG2Ë€O¢0IgªÏ¢ñ@ Ý1UB›P—ƒÎQm$Ë:ƒ>§Q ˆ@a!h_d‘›?Aß3<˜ööõ¦K¬MeBšP¼PÝ?Â#$-⟤ý ïú/rÖ!y^Ë6"UöÍE>¶/Ä2©2^ø›Eb_©‡³óÒ€…rcî쵞Ú3T‡Y ]'O9ÛÀÿx8ŸuWꡃFpi¥TÓ—Àò_NŽ`‚x§c]¤²ºP”¡ªœ#,—ÕGç¨jaî°A$}#$å£ Q¶©Ó\ƒek¸hT D(¢†&T»c ù8u¼Ó¬ùö^ú;‚ ¿À¹dÃ`‘haÂz#-¤õfÞz8MXå3³Ö¾.¯g¸OÓ ò#-;C½ÒôlÒ3n—(IÎø´ñɳ6ÄërŠÚ¬±ó°âñÎdT$Ù&á¹ùÛ#$$*>l=¾¥û5=J#ã»ñkŸ­yiă=ÏI#,füwz‹ÑŒºÿ¦æñ†=§÷Áwo`@#,—A:à,œ§]ä Ä2!fóª¢m¦™dkø2#,äÇå2»Æ°çפß‘GW #xm1ePãW8r5’Ë!Ÿþ»³YÖqvÀÉ úàÙI”æ /ÂLV8¸æcbqB$‘=à}#Ø #$ˆ'°ï²Z©Pö˜yÑä#$žÄ×4>ŠSX«»«‘ÑYŠÛ·øžˆwȈ—?ʦPÒ Õ”ÕQPiF¢  þuGÙÐñ¿œR#"ùUDªAPY!BDêõ~ƈá@LÀL¢±=y /N=#$ù/_¡~]ð‡gøc#”ŠIÆb£èÈyä GF1¤}°Õû½s Ë@­#,Áù|ŒbÖ¨M‘EëìÄÓ–’s°šL`‰À¹c±€|B9uìŠU^ªÅM ƒ5ëý—M[ÕÏÛ^2ÕÛ_²Õ3‹|c~šP™ªá‡Æ.×#täBAE¬¸æG.+Ò í¨[žVij=á1¯XˆQuËã–ŽFÇ)½«c‰†72‰ó¯9éäoŽ|àœ+|Çu˜½¾QÛèÖmçiòC¨˜Æ:gô´ßJ7'Éã¢fæ å]F‡‘vk°ÊY,4]Œ2®ûÕÎ]uÒ)•s)2÷f‘·[d¨M„ rŽÜ>Ùã¤r¶§œÝ6÷f‡£•ÊŒ#,†ëÒú‹¯nxöÆT†žqÌ=p&ó–ÁÁ}7:ÙדlíA£WAœ7Ë`¯WpS³cïÓƦÝóð‹Sµ™ PV;Vì¡Ób:)£rѵOk®&{MUŽ„„{œÈ¼Y–çB§fŒVÍMÒê+ÈØ1ÊÌ0(ô,øüq9°ëÓyõã€.ÈÁäuسPƒ•ƒw}n7ëßÎæ}Ù¡ÃÁ§9v§ByX~.oaÖ¸G é¾4;‰+ „¬ X˜¬DK}X˜ÞK^­2\YËž™gR±ØW–<”»'¨=åú=N(µ“5Ä1Ã#-q²*&1`ðV0:A˜„„NYì3ç ¶ÏÛ?8*rDå|Ç#[?ÀFæY™´Ö†Cqq$ÏmyÛhÐLÔòe ³Üìk$àG4&e6A,Á2BAa¼dTsÁÆwÌÓißÎôÀ8Û&÷n­ÐfÀt¾±À3¹±,IÀ©4BèÛ‚kÖÉÜօܚ躷r¯_${wMúÔ:"VƬƣ5¥„„ÅŽ]Š€@É©~eá #,…#-‡l É¹ÉN¼ˆµU*X‚ Œ2ô»{|§Ï§ ‘3‰™ƒZ)úî¼–D`Œvc„ÑIgÕ3×õô3ç>†6d>]zTµ!"˜Dw°Ü3PÙ)ò>ó̳Ã9àv—bš‰Ž±‚¯NŸZ¦ÉÙÇ»CÁ 83kÜ0)]”-‘êdÄY"ÈsJåÚ˜¯=R#%ù}<Ρʾ“;íôæóð²ÇÎUC ¡ä9På+CªC­MóSR6ÛåêȺ’$â„Nût¾TýHéÄÇ¢t¬€%vfÇm²ÉÞŠ\Xq¸èdÑìݼB#"6öuì¼÷Ô-Ä%«ªlúîfVÆA»sŒh4ôY§ YµÂ˪0B „ˆÑ%1A"43dÖº\ÙGuu6nEÉ£V”¶»~_.[¹ºî»kŸ`W,AFBÀ¬qÄÑ1(Ši"ìrìnäDp¹#,”eˆÛ¨È"XÒŠÄèܦDÈÞ’55ŠfZe­2ÛìòGƶ3AÅå̇h4õ1*žŠì°µºy}X÷Ýó·îþ4ÝnŸr‘fY'š$UcDÌ¢ƒÉ#-*«¾é±((çAKd#-B€„ƒCÚJT œ'æ.Cé³î{“ÁÙ]`‘ü„@Qå°¤Lq§E…2LYÛX<ƒL–iù$Ñ#gm‡²ÃšÙLc‚¡B§4)œAׄʶÈ0ÛêQO_Ñ:dmvïž O½$”Q ¾øì›„ØØ ¹+öéZ` TÕ%HÔKmY½ÀUc­Ü{´Ó^çžñ< Í òÁÖ®ÝoªYò@Úõáy;ÝhÕt×iÜ(ˆ#-!%JFE§#qÑ TÀJH#$A•¥B%…(°{MeCš¶‹3#$Ôð!Ø}TgŒÁ àH`?#,ø¦V4w]k´VWã÷xÍ0KRim¢á_çÑ¿§²s^\òH<¨üCf3_FDÊM& ÁòiýÉCChÑÓ#,ÚäÀ½øÃÞP¦¼b#-xê˜(¦çâºD×-nóD¿l“iKm‰›UUSæ{3°ÉŠ$#$ôÏ`W]æ:3ÏýU“/“C<ç[¹;'c-=8quµÅˆÃ(Yµ˜J‚AØÆkºë7l&…s÷à Á¸BDÜhnufìÁKDÍü-ù7uµ¥S$ˆx»ùøÍ»›¼i‡mpÐZ‹Î“ÃvÖÓ=%W¼žE©ÊbŠ'òë·ÓŽSfCDÚňÄ)´P‰9žf:PÄÕçWMr©Šª[”Y ú¾‡’ƒYhZÃQ²ïˆ˜žÂº‹üKÃ~ÜŽ\uÍTNA-Uº7d¬D{ìø'@Ï/]ïkÀá[ºˆrc=#j7®Ç[ÏWÒm·„P>îg.åÍa¨Ùe#-}Z†6MÆJJAlœ3«ŒS-µý{µÉ)ÖD3ý:òÍ™¦G#µÎ> |m¶Ú> úúÜë—fD€Ž5˲-nM¥Ô6Qxq 3@Ca¯#,J·Zm”&N@Ü ¯×¥ dHÂdêh]Ð,,‚9C‚ ò7 f j2B„¼+N„¹Ë (°È ò$ d ‰Åþ.ovW«áهƈ‚ÌðCP&$ºÄ•Ü9–QìY.Ýef ¦Àò`ªÔ*êi‚q¶$C˜#¢­`ÄÃCL  \YæikDpžQ.ì¹ Ç ¹c$)i/¡ ñ±h!¯kÚÝ/Ÿ—›ÚmíZÝHÒj À‹u((T«fD¸BÔÔ‰yÇE» ƒËóç8yâû|%0úစ~u!§XªRîdÜN6`¨Mzø@To|ðD—í‘ÛˆRQ#¶–*!mhòÉõ›¢ºXÔÉ7=Á#-!6‹ÎÎc­÷À‘ |NÑ\†E +™¿'P²6„Iï"L‹äÌ9nÔZw¶ÑÜ×ëbëNÕ2¶‡I<[;yŸ£öŒ#,–0åY=|绹h9/‡C©©·!°vòd¿u[áÈbBBTÄÌÐiO¿bäùò±ÜÃ’;Ø-&Å#-©THJ'4ùzõÁÁ1P{NÄJÜtz>½ž7Þ™<4>Â×›qÕù_ªü7”÷|ç=@ö‡ËÇUE$Ÿ2¨gw÷‹Ì&Hƒ>`h¤ë~å¾*/PÅŠŒÂOÍC«¯ìa#,Õ¨¯(6‰ ¤ú0Ü¢lPUÕU@?bUÄÞugk«Æø»¯ÝÖî?°ª4eu> lLXbc)FB„@.ŠE!ßjé.ÄœýV€ŠDÁ#$Bš?ë8ÓÎôЈMÙ-ˆî€$Hò£­ÏG_iU*-©^ºVH#,6)99lÁ~,£†¡Å—êáM¯¥ =Š´ŒêHFØÑ~M`ÆÓæq#,1-äÔPì#Ë#, ÔÞë># §#-Ma'™p@ŠõýRª `ÌÌBit».) »©+޵V€ð:ŽYÇè†F„1(ìêrÛ&é•öŽFÖÃmezq“g|áTcïÉ´f4W"J¡[B£´ôp`Ìÿ3W@PäÑ'¯¥WîmeŸM'•y"/s†Ÿ/#-|îÀÿ[8jõ%õP#Ü=ïáƒ: ±“†m(²Öb‚˜£uO±àbÐÓb›Oîوǡ”dm"¤QAÒÃåÓC®5Öj‚æøïf‰Õ2ç4Fwu-5êöE HΘPÎ`*Ö0?¬\Ý×Z|'žkÙMFØÚËAŠ(ÈcJÈ7¨_\}5©d#,FSÁ‡#,cf÷† ±áNÌli‚ÀßiCr6å€l+§Z¹0F5iïŸÇ¶mló7ÖVø×g…=‚ºô^&#Ö;‰§U/O¿$ÅXhÕí?‹m8’^·©Qµžé÷t‚aãyQ×çGºÃ;#„„©ßtèç16¨Çù“™6C¡7Núa=´¶5È7DmÜ#$àE#,l4Ä%HÆF¡&DÅÁ„0êW6Õñ´¸ªo–\•”£’Õ^#-(1Ki>G¤LÌíìiCt*©L´ ‘v"9ÄÐ: "Ä`FÈk2å0ŠD¦L‡@eí@X©“Q¡D7I¢H[$2ÃQ ³#.ƒÓÞ,Äû0‡wxvšÃ´¹nñHÄuÂ×C%i82!©q‘†2°È…0-"f’©KCt4x8Vh04bÈ‚¤Võvì:Y‚Í"ÓìÛMþ®xõ¹ó‹ç«©“3žÑÆ6‚mݾžMFÖÅk0b÷¹ºf"QñDk¶8ÏSŠ(G=y€t®JN RHpx? ~XqùDJ¶ A!²fæ‚P&Ô¹Á ÓøU36ÍŒÃeÓÈ&bS@+SwÆþ|ŠHstr(¹r”¨-zà› xyR»Ä w`ˆë¢ãx‰¯¦³ 'Lìlê]BðØQ¡F˜Ëu'pãH?ïË ±Ùx‡´é5§®ÕÈšåN1mÚMàC¡E×SL×ËmùjÓØï¬#-»Q¨œ­a vßX(w)ñi¡YsY']7¯a5šs?¦qµ3åäÓáÇf:TpÒ,Nx-#$i1Ÿgµ) DE˜iaB¨ÄÚt/[ÕUJº&,`H UˆØ²µº—Úße±‡DV QóÚl4tà1@jbAl€Ä4@B©À0‰„‡#,º[Žëš…m‘qÌZCÙᑯ±ê°U0$“¬õ¤£¿+»/àNäR@I¶u¯õ+ÓI¾|Z=ˆ¼±3ÿ½FŽÛÚsQ¥dÀÔÚ÷¨ÁÌZmIÅàün-§qS^r>usPÊSƒ¬Éf2F¯iŒV;¦‡×æÀlQ!$EFç„î÷ÝZ/M=ê§Ê!‚yËž5sD‚ÀÚŠÖŠ·F·5m±­¶¢ÚÌ6·5·5±VÓ]Ú¹[],–ºé®©¢„‹¼ùl>®òâuP¿‹ˆG/T’Ú] ÌÓt™Ä€|hŒMVE&ª¬á]0|OàŒ$‡ˆŒAA#-‹)“%2ÍS4É)I¦)5E¤ËE_Ô˜’ÖÁЉ¥JÒš"aF´ßFæ¾}¹†Mi1d”JelÚ2dÆf™¤Œ˜Å$UbRÄÒû;¨ˆÚ,Šd)%%˜aªBdѨ´¨¢‰̦Fh£•¦)¬l„Ê%&Je5)Rf ÓA¦H"Å‚ ŠE‚(¦º¼¹' ³ÚíÀ·éòf„ß-ñ`™Áù²µU}Lbõœ&å5òûòÈÔ‡ê)¯:¯D„²V>“}Ø©ÊÃ-*:rÆfâIm0–øðÛ(H˜4czKä0Ï(`#›êvøË òŠ”Ç÷ÑïŒ1ÐåÊ,à=-çC‚ÎH>/û –tìñ;n›õ´$¬Œª~*’‹4-uga“Šè¹Æ6"“£§7¦KûæcÛéü¨oÊP·ÛÌdtm†8`deS(—,YF:®_3#-õtÈÈZ #ed¬Z#fm2ÌUTQˆ‘{xëÙ׿ÃÈÌÄÅí¬Ø!­tNß¡žÌûž„åTÞnq†½GkGE²¥Úžè’ÌÊ$×ÕÇ'Ðgõ_gIœXï^·áÛò…s±#-FXLÀ'†ãÙØŸ,—Û÷¥sÓ¦Ç2Ã%Ú¡:ª‹+©¾g€dÐi+/ÙßfåÔ8¦}z^¶·à¢ÂÄ&‡É‘ç¡å/Œ'ŽÓ‹årÄ(¥ ”Hª’$€Eî&'g&ÿekó¸+ÜêU¦–÷øªw®| ã#$ײ‡;>o#åü†˜%©„:H¤±7¦ÁÒņ$ÁèåˆÃu¾mGÌËqÙk/âë ãäYZ|K±(ô9ã›ï×y}ÐØ![þò×çåî¿’–Â/Ýš L$ÉÝ#-ÀëbÓ,ÅÙ°¡tá&U°2 K f/ISGv£(K9ôl™5Ó5…¬Mp`X#,Å3‰P ‡8>#ñÎô$¦Øû™ #$‹†p$PäWJÔ…9÷ÑÓÊòAºà7nóRHLt;Ͼ1‹M3ÁÿÚ™àN <Ùy>–ˆèÈ×éÔ q­ÈÇÕf[É­#,³&ªPz„ _9L,]tàÞæ³Oæã3#,åsíÇ‹šÓ¤ÎujÈ;[lÓœÈÌgRìY!˜ÆcáèѨ¢«RR[ 1qšÖ"E–,.a™˜\édÕQ%´\<ÅÁ‚®Ù, ëR´¥#- jg|O\Ö²\œaˆÖ:S‹#-§=O¶k±í”_á˜jÀqøi„>0LϼӉR˜WSÙz‚!4”J„#, rW¥Tg`Ú‘Bæñ;PZÙ8ƒ qcOj~ÇIÒžåàÎÑ•“%ÞkQ¨(ÛD0Ò6èÁ Àn›kC3'Oc2>é:Ôô˜¾6$O.F›ø 'DÌà·»0¤áæ/Ô@eh} GWgm>xÓ4›»jOǘé1“v²nuMU§7ŒžøY4"œyqã¦vHkÑwJ±Õ†B¹àè4ÐÁ¦®o1a'c©Ûjñ˜yëft°ñf~˜í¶.ѺÇ4vØ ãámiçmFâ–d’g@:wCéhz¬¿¨¶Í”#|O»jQã;fEÎÜš«á¶‡³.-B~×’tžð%&1´KQª¬õdz„8$‘œF»q\[V¦XÞ7»šk;9ç[åñÎ3#,Ñêì…ìõ PíÆIÓì>ÆÆ1¡°w²‹ÕÆ#-/ÅLlõ¦¨¢ˆ'KMÐu¦tcD$u¼ÇXBcÃзÛ2h„YCç5B†8L&/0ÆCfÆ–CHŒ9bSbGf¤¥#,2­(šâ4È%­WÆB ÙˆÆrÒÂîT GêX‘XD¹jÓ }dTÃOLw6Ûlª_ʰ©É¥b³¬XW¸Š²o—}Õ«ÙL 2™cY§®4Næì÷^ “¾_‰dE{𙹒¾‰å—@e|ê·ÞyЃ©m1¦fnÐcW§x1ÒMRn&#-Ì×T Á¦»u´ð¶pU‰µ:!œœÊmx2îâÊ7L:àêçµÁÓT䱺”×£%ívs–LÙÄ™H%±0û;Bb2$¹8M‰‘©Ž$¡Ø™ vÉ’3(j¥³„¶"#,.¸ó‘æ!¶›ëLÉŒl`L’g(¦¹jÀŒš#,‘õæ^¶.°œ#,#,XæÉŠR"Cœ¢ÀÒd!“)Jˆ°·&Bǹ‹#,Î89#EìÆŽÄp‚UC=N€ÜÄ! ÝÎcŽîQ[ŒI.ÞT Æîæ†sFq’ã6†ñâHl2 „8•ø˜#Å(΢‡Vt4º Ú3=±Ò¶Ü¡@àpc¢M”2,ÇqšÜ.f;×2èl:†ì„ÉÈÕr3sadÕ)Õ•‰cjK ž£½€Í¡‹cP46æK hYvp-ó¹`°¶s\–0àèn\Í*ãa4ªQ„bHÂ1 ¿ÔA(Pýº„‘HEÚ.k#,#$x³L@iSzòC2ÀÄ}éJƒ¯ß»¥Gëò£©À(j„ïFCÀ²p4UŒ0KSmš¿Gu÷ïß·÷FA˜Á'0`ˆÒ9¶H¦ƒþ€ß…½m™j¾¦ª6¹F*¼•vT‘ `\Xª=àÄ5βHÐDŽH;Áií–³Ã=æ.€Ó`¡× OŠvʪL>ô çg…–Èÿ³ƒö=ö!;N¥œ8´¹¸5r”ÍhÞ2nýe¶ K±u…ç\¾Û\8¡Viåî¾²BÄ>ÐGÍðüÌe;…$l¥t•–Ø2aÂÀÈCš#,á°ŒŒ#-Þr#,ÜÄØ-–:ðÜ!°C£«ëÔŒöŽÆòÚr€¨'IÂïEʉ9!âîëª#,4J‘$¤¨q)€žpŒU$#$aœJ#,7,(0…(†Fà@¡²/G”†ÊÌ"˜X¢b„¿g"d&g¯B¤$Òmd-bÍBp6Ç5B%!“K”=Þî`mÎ š#$²“wOÇ!íUù„@#$ßñû6–U¯Û\¿‰S—WwUÙn»³.nºnØ5sd¯ìךž_Å»²‘B@ûT—`ó#dP¤ý§1þ²;ÉLw‚‡9§#$ "™Ÿ›‘»k=cŒ†Y‰™àãˆÖaGSà`>…?b¼ –4_Xíccóg&EßÎQé4pqs`v#,ÿðú~†9p\‹`„ðÐ~ŒLìÇÇI‹!pH€Œ,J@,‚›v¦Òo@–JcÛä÷£_ÇbÞ‹"DÓIE‹oMU) R\3otÁv´¤Î‘s.„2‚d€ñý#ÕÑçv4 •#,†Žóïh¤Ìù†Áàz?~?Ü0Y‡І#$ø¦ŒÂ¶h<ƒï=Ô~(|U¤„DYcR´m¢Lj"a±šfZ5‹llkëúªÑ«µãíW†"|’ç¼Â™z5­P|#$T®å:7÷y.Id¼‹£©á|;Öv$h‰>«»­pH\Db&&(¨ÀáDBU$JX‰¢Bœ¢#,Aź`2L'›«Èðñ{µu’I#-žGkÕE£ëv“ú®Àï0 Î÷iAÌœÓe2†OÊè´D~œ:Xo$ö#$'õÅz)d7œtг#0¶ºÈò" ÖL,· u(.¿‰ÅZLic­$YÄ÷›ŒÆ„WKo£oeëoDU3[ů>WzÌ¥,€Ø¦ŠÁ7V—#,fˆF,`29þ3Œ¤LØÇq]Ê ¬CœÞX*~' .Ä#-xU1#"EU­P¬€£m& «ôÒÞýóÔ|Ã1ä›ð‡™;\sPЪ1Š¡“{Mã¼Ò#, 1¦>;të­‹”Hdƒ’#‡­R—UBm@é3”@R'BÙæjõG2$þtÔCEÆl#,QÅcZšà›ÜD!²"P ºÄ³.Ò3\¯9âÙy~@x iå¸ïó)¹EÙ$„-«¦ÄDŒBÆš)ûC£x–ŒGòŒ A¬Æ¤ ÄWè²ïŠš#- Ð'ªMoñ`‡äãê’ÉÆ`ü<¡i»Ñæ?–Œ»ãl8‘µŽQ߯â(Û´ÏÇq#-8ƒ}"äaV¦¹8+hŒŒ&EÇ×”xU´¼ýäRwvyÒˆ&ÊŒ­ÞƳ[JBóá‰Ma»-Ñ¡Ý(7¢±PÊµŠ ¦-†¶Ž0ÔÔÑ#,RâØEop’pˆÈsÓ‚%^«Ñ™ÖõL`Ç“‡ÆÈ§S›,IÍ,Ì’«¢T"Ô%osU!.âpZ|'tN]ùXn÷ÚËA•¢ß2‚£ú´œ3L£­‘œ¤˜àvìIîÎ>žÄ#,`A†rŠH8Ê‚‡jŒ”ª$ƒ­ˆåù¡Âð…tb„R®–¦Ò†“—1#M³À÷%j:5⋌HÒ‚sÑÇ‹ˆv±ì„MN·¤YÆYÚ·¦4$Ñ4?f;#,Gv† †*†&š¡ÃXÝ\˜d‰ ³ )e h:àH#âUDÐ@L\J$M„± ØÔÈD :IÀlP$PRÀ&Ä º07&½#,Œõ£Vfhç€PW‚¡[o Ÿ¬;kÊ>eP6ý—½_ØÜ¦œR(±=” £üŠÝ(0ûˆ’P,\ôù3¡±_E=©a × w5¹—ßjÿ¸ÁÝ #-=2#,û Ög=)‰¤”FCÍ+éd‰Å2BE$c#b‹ÀZ?¹”kh±TZɶ¬[JkU ‡„! ÂCÔø*¡õ(]°|õIÝïèõ.|‘påþ„ú¨Üzûí@ikE/·#,\MoŽmJÕá¬1ÅÕ•°µU/‰èÙrð’W"†ÑÅëN¬ïT–ÊeäʵS0é²¥â–Z̧ZËÞm´›(m ­B•  ÔÒ—ZŒQWZQSŽÃ0̳pd†¼¥gHgE6ᨸxRA#‘Ñô·3y&dšEAN” mnîÔù’µÓWrD©|SNDIž%ÃRnˆ›™.‡Ð8¤EojÀhš”hÓ:Zo“Œn©´n)ÖcÖlߘ“HLcðDA´”ï°ž°!ÓÜ[ÅZmùx#-¶|¬!j©1sxŒ3Bfc¥mÌ‘¤îÈÆÓ3³ïr¥NÒÒÂd±[®’èVødÙfM§Ô>“O®R™ÉßYàÉ›!ß4Ë&¥j+¥xˆmã8ê©#,—H‡ÆKìi²l`Àäà£T åÜiðin:ÜÀXJ 1R#“‘Ž0 oZ·JPE„RIQ#$A¨´ÀŒDrHFõ”§·==ËÁyž/&Árå}»Mqíx×)Õ¡`ËC„‹`ÚDbHpi@’$H²?¾ƒ`» Ü)R'¤*CpbˆÀ#,J8¬(-¹1ŠJ¨çÝ 5CDQ`HüÊSH‡wm·hs¨»-ÓdØîêõ'E|{7ž‡vv¼´±°51bÜ  HÄmýÔô ái̱¶Ue¦NìTdí:±«¾7Që Ô›ŒPB}¯å² v° Þ•*¨F†’RÈ$.#-€‚Â1‹#$TfdB#-ÀëÔï Û´Ô1R3žê!pÓ3%¿cn{Ùž³a 9Tü8n€$Bš×åHìÍ€°©ƸZ•2™„äH¿^B•VÆèmË_oò¼¸sØ7ذ”ú"z&QµS 7‹h•Z'Y„ ÙnX±#-I”×K[o¶„§Ïºf¥¦ÒÌÓXR#$‚0#-ÛFúà‡b@4H"Dmf•³K[á¶ÕÓVžÕ0³IP"#,EÚ¨KVˆ‚È%E‘¸Ò‚YH©˜õ ¦”ÙG]å"¦J@Q-@ACrÉ­¥³cf)i3Õû*è¡(Ô¤­)µ¶›f™µ“[#,)¢•)·ÑnPÃe¨¬¶% ”eiI#-46›e)¤MIš6˜Ê6 †Œ­”ScdÉ%‘¡cdÕ‹T¥PTT¦Ê”¦J¤µ•HbÉ¥´mfÓJ¡%&,™I„Ó$Ó%K*ͱQdHÚŠX™µ&IVÚ–[&KS&H#$ƒBU  %P"@Z´ÚK6¤4–ùÍ«]6l@a‘(…–ˆ-¯sjÆÚ±­)lšmW Š @d âkTY¿ ûòq#{æu9XEšÃ#-`n5Ä<“ÍÓŽnî™ë®ï Ÿƒðp(‘LTÄ@ïL&J”QU8oInúö¦Mx›ÓXô.…˜ü8™`>è…97CX‘e?B?//2ó½Áñ~#,‹UaºnýGö},©ýœ~:k‰ŽÀ©3…AQmCìT¹Ý#Û#qþ•+à3òÁŸ9Âëͳ ÏD%g87r“Ѩ«%W’ ªV÷~­iiêÙFá>$ƒ:Vo=Y²Ä¸7Ú#-3/æÅËZ—ÔôR6W‰0½ë¿«åø/vß§*@åÜ#$#$+lj¨ÂÉ9EIH®•ËA:#$VB¢<ÊtiPGÚ#,ScøûD¹KÐëi=òW#-êb“ßY»ˆÿòÔ¤†E‚BvP?Ýü×nì´Ñt«KbŒm¡•”A¢óU@Æ´Ñ #Û´ñÔèª.ø¨µÃ"€²0GK¢a˜H,³Z)ÈÒ›hçPÄÖ±ÅZVFA4^Œ"¬láÆ Á¶í•H£9±0ȆÀÄàÒm i¼Åg5hæ…ŠŒ·T1†¡UPŠ5Öé dÁRÕ¡ä‘I…L~u#-CYtCªû|e›(˜OØ#$£´cÑÁÏV©hqßTh†%jL©ª‰‹hJ¸XÌñéW#ç8A"tŤM¼cØc§ßÕJ‰,ΔlhšBWˆIFÒP%èf¢ø/Šï‚èjOœSL”Æ•¨ŒiÔÔ~îüí £em»Vº¥ˆúAû\²:Ž´¢Æþ;·?a~<þ¿°:ƒ-ýsˆBÒc÷M«‘©oðÁ)Œ^>é¼?#»ê­È¡*xI;ZÌUCK^¯CŽMÉ#BÂ^’‚ÓÆZLߣt£yÎ9lšRšàhLp˜ Vú’Íô´ï‚êTFJ߷ʆÔ|uR±ðîy\óàÆ¤ÐBhl6)!@OfÙë•·«ìs½{J£Zûl†ì#t¤A¦-õ€cbJŸ®4„sϨ1økB!™™ mD;PàÀ¶Ú΢þ¹‚rL Ñ àü~2¶&´ÈÓ\é©#-‹†áJ#,#,1ÒI1 $€ÓR’“Äh÷ùî£Ccp›‰,rÉ…¢eWÌ2”MÒÓy™Û¦½Õ²º Π:Å`0W#-…PÈe#<ÀzŽE®ÛÆ<ÌQPi¶Ó‰øSžv'¬¡\ÉÑó¢è˜5$&N^!˜6 r8˜Îàñš)®•¤ïS©ó¿tUA°ôâv‘•#-*—£(A(‹Ç2®3=*8ÝùJǼ èD#$Š„3"‡ZÛnÖkLG&(†k@eBÐ0*ƒh‡~|ù¿›<Ðrš{q”‘È”™nØŠåŠ$CR42pðÈRÀ!€²Œˆ>¨É%‘ª§9,®Gîâ,Mcöñ„ç“Àå…ªžüÖ²#'p›š&#,»M©˜#£€!GÂcTá¸Ü íÏçˆG!‘Ën€ìnÔÔ@ÜA­9#XÉXÈ…Ù™dÊÝ´6ÄÎ6$IFÁ¼ºØ3!… ð}t.Ûä’0‘}(¥^ž¢õâzvœ3»¬ÞVÌòT.4ï;¡3sÎŒø=݃‰Û¡d*‹•%„¢RÛ;jÙìwÓLI´näÑ‹á*#$馱ÔS!` £"È#$q.ôã(ý /#,䊂ñEÜõÊ@Kµ"©¦…S  %íø¼¹bÚ1˜l˜ÒÞ[uß«;YFú®ï›“iË‹N{ÝŒæ–! 4ŒYuEUb3+8¦7š§×¿ÒùÛߦú«“#¾lv­ -H©½®º³0"Æ#-ÍèŠÁ>â¢dRB2À´0­ÍÆ`‘ÂãÌÖä³KžyÖƒÄñã?oÞÌeç\eÕÕÝÛµÂRH6A á­`Ü0­Z¢לó$À¥PˆfªY¤7e§)#,äÅžLƒÃ „¨W›Ø»íxtÉj0 ¾dT‡vÊ;_´×Š÷÷_„8õëÏ£mÝ•®ðväÂg¼#K©ì×ò6ãExß`ï•¶drpй»¸KËñìLEWÔ9RŽ–ÿN}bU@‘½òeç?VG¨:Ò¼ÁMà‚vriß”„:;íÂ'#~3<ôŠÝ1!ONÃ-Ž’¥¯Ë¡e¤šÈ#-²f©‚¬Œ>«ƒ“¹‚YÓáëFP¤aéßrÃ\àvh™ó‘$›†X ÛŽòåÞ.¤Ûñ¹2&f$'^ù^ê½ÕÒJ3v¶ÑÕ_;«^¢Øµ·žÏgÇyeã§vâ™0RR(&Q¸€\v2RK²Ê(± »nºÜ]ÂD:€ÊAã÷˜SŽ»×~TÌ}o‘Ñ:Aˆ9vžB`_w”éØÏK5ÆyØÀSµ®Ú#F|ºæJ¿r^¯¯ÈºÉ Ù¥’~-ì¤ÚÃZ¿cZõJ5dÚ«#0ÿ¡,'J¦LŒØnBK`,¢¦‹bƒU«”fÙ³I¦Ä®º.UÐæbK#b±ª5_²Ûs±&Ú+_U(ª½^¯f­xÜÛK6þñâÛ&ÚÒ©Vµê¶ë“£¦'Ÿ]æ2àquœI2Î×Ë9¡fSá.C.¦Å™Ä†R„XK@¤¨… ‘…©¸Ì“§Ó$ŽoUî0´E–‰_5¼1XÐÁ X‡bÕa¢`š•ߟ³3nˆi:5,; , žþ,°P6¡ÅÝzèO„”T5A0G-†T…±7DgBZS$›l*~5J£-Už”T*éd“W˜d¨àsa–@õœ ;làtðôÑ’·}B£Q#-ŠD‰UI#¦EF“hª5;[mÕZûÕÃÝÂW˜è`aå#d#-òÁ‹ØN.Y¹#-‡*宸X®#-=QD¨C© ŸÅ˜™ÜA¿ªßLúº_pry§­×°+ ÞJ…#$‚  OÀ³‘w;ÃH]a#,biŸž>ïú0ˆaEÞé`¸"ÚQBs÷ý&ìaA£%U¨%ÝPoÛaÉ]~W>«õ·6ó¸xÕR2ÃpðE“‰‘û:Dt@OŽïÆn¡ÌÚCx%ó6q_æÊÒ¤úµ™ð¯¿û/DtùÓkè‰D‡l]Ðé›bB‡eÕÃè„ë× yÎþÃNG²Ï(ò;Ø´·íðø‘Î8;;ºïxÛ§ÅÅ·b§Y#47îé Í»6â;:‡xCAŸx=È ut7ŸSTSb—˜Ï~^—Ôíë„ßH#,¢úˆ$’"!ÜAD•N¹VÕҶص²W5®V¶Œ Ñâ‹0D/¨¬ˆH„1.$¢ŒÖØ|×Ô‰ÝfeÇûÁú8Ü`̕т±Ai¤±`§(Ù öç²53™çRumµíSô~ǧr°xIõ4ƒzz“Ü”u‡CÈÐ^‹8+Ò$ y‡LU’ t„€u¿ØNãû™-R(ZJEpÒò(éõvµuâ|¬—x»™”ºûióü;ö°K m4`3 ¸uA#$çQ`àKûQ è4’g·‰w\Gü^ „Çq´„6A?GS‰6ЀÎÓžéÜ®t1ífüÓV¯Éò‰ ~±H"ž®ÒM{Uš†Ýįf–ˆ•ó¢ÁTÔ´RLÕJþº2Ãõ³)4Љǡ£ Õ@X¨­„‚5ý¼ŽZ`FÑ#HFà ,wd0¬ÐÃ-#-“%0ÀQÖú {e•u ˆ€h4”6„D6tÈ ÕIFj%wØ*¯³ÁöÈ’Óm6Jç|ëœè1€Ý°Há–a2¬é&k-®%ŒA¤vicB} `3Lm(G˜˜éÆ"Ú¬ÞThÈi¡±ŒÊÉZb‘¨MLc£´j-BÔP‘L¡QH•¥ rѶ#ÑHøøò·‹êrǾða F ²Yš êCF“†5ðÞ6M«Æ¾MkÅ^.\6͵tè„&5KI£•hÌ VVÐÝ#j´ðt¥×gw27·†dUаÆV+zÚÄMê­âë ä;d3q%[cci°£’E±rÚ6•Ý5b¹nk˜“çw‹.ºÛèÁ®H+œQmœ-Md Æ­;AcXÂdNŒêEßþf³#,2ÂF›áĸkL(Ñß ¸yA®5&Ψ hÕPa—M$[H 8ZÖÔ¬¬ÆwòqhÊfV†ÆÙ#-THA î2½øeµƒùÃOØ!¢Bv¤¢1‚–_”ßCéñRÛtžª“4M“ÑÉØ…ÑûƒÑèªF}±¤ÆB’³ÇU]ç]y2Ýf›¬Ýhʼnôþœž”¦Ž™hþ®¼­Èèèø=_&ððÑЧ¤°´ÜA=ÁÀþ1øO¾#,¶M¥wäøÖ±«•T´µ¥i}?ÌoÌ6­;£Q¹{mâ«–•4š/KTb•d¼[¤ª0±X¢å­×R±µŠ*§Wn¶Éi¦°‰’ˆ¤A…¬¨:ÆâªÀ%¨DEGóNØŸ¸2œ‹õ#,H¢‰ `„ƒ(R?ÕÄñ?Ṵ̂Ÿ¶#-‘D³³Ë»áå•§´v(±ƒƒ—Ûé4\Ðôê€T) +E#R«öÓ`­1%úh@V%Ñã#- ¢pN——B¤Í2k 0·ÇmR¥U.Öñso99JŠÐx+™1÷¦š5’F´6’_žMD4£Dg,ºY6²kκ™RÍW5ÊÞ6¾ÖÚòË×DQl–¬6Bª:k+ªî¨­ëWÅÞù÷­ÙµÍ¶ú*·o7uY‰I%µj’i²_.ª¾B«á’¢IUëŒ8èµ+Ç—Q¤:šQý…8ɸ°°ÛqaÒ*Ȧèp¯Ð‘•E.I}S@¡xŠâ&±“,€e rIbHÇQ-U{<Žù#$&¢ÿY‘U¾eÙ±úZ™„lF u0·h8]Šü^ú'ðIžiR$K î_|`ÍãáÙ™çã¸Ä-Eç¤üÇŸ&LÏ!+S»¢Ä)Oè±çÕÑ͉ÉKD&Lpž’ ð(Û#-ÇiSÂíÏm-`ÄaC2ÁÑ— ë­>¸½R¢!§È1`@#,(Ýaó>Þ˜œ\#,\È{@¨l㯊.ð;Òº}d°ò+Ø$-¦‚¤ªPDX4´¥-XÖ¿¡»Šë6í|åvÆš¬ÓYO‰]FÑh™‚"‰%D/ëÁÔó:™éÛë°Ê­ÑªðáÛ†ÐÕ(P\«9‘ Üê˜ur$XOcTât’MŸˆ†óuET¤¦#G ½WëªwOŒ l]w§,1–3…äC9IÅÃÿ†®b§9ÍEáŒ3Ŭ! ´:#,tÞa©ãQÍLLhdÓ+h¡–µŽ a¶#$Ž×Tb8p{“më²±#$ƒCD›¯U#,€³È|—Äd>Ðð<©’@B…UX"âC`m(f¹FE‡IÈq‡-ùÕ2}>¨×›l±X6[„Æ#,¹îà¹WÔ±d#,¯#-”å‘Ö‰eš„…°3'ø|ùËs’³m΂~÷ÔÏ#$gQÒUɳšb?…Ž51º’cåÚDGékF%ò(“F¥4C#$YÓ‚91o£mw6 1ü‰.HlÃd4a’%@b( „#,±’åLïe(Ê©Cî @ÏæñÇWëí6~“#!i…‚)6 çP1±3¹gû‘#$Tî×Lg[•åý[WY©Hf±ÞÄqm «¥GPå QÒò"¶KV¡9†`9Û©Šê¦¹ld¡òú#$Ö5ÜÜ4´hÖQÄk(0µ%•4Ìbµ-´µ&›Q‘Œ¦¿¦•уDʘ¦¥Ÿ¡Ê±mLÍ©²6Še”¬)MlÑT¢XÚij[Y4­6³JÒÖe¶hJ±²ˆ6*mBbXUf‰¦©h¶ÛÆ i ÔÙjùU}/âݵ¿Oaü_©¤èá€Ý cD„Mä*uÄ9~ä¢F¢Œ‚ A*"4DD©mck©[W6Ý”¼â¯0¨ÒH?¥H—‚Ÿ‰°IÉ£ÖxvrеúšÛo³¯­mË\äjÜÑŒPñ!…^É‡æ »¢?œ½Î+}ÞÉå1ys®Yœæ¼»¯¹\¥^^¦‚ÏîzéÈ™«ˆH¤'ÑÄé°½p\vð.Ú)x£N¤&ó!:¾Õ Þ$€044Ò(p%àv“Ë4¾e#$pwødÌ9š” ²¢…XO¬7QÃ?êÂxÀ„V±„5X„4ØÂJ”TÖý#-Ž#,eQEn,M‹•<^_§Ì¹©³éûœ„QM6«×ç½5¤éZªùøú¢Ÿ’O¯¡t0û ¶£a)[¸œq”NÑýìI`^­£ ‰":bØÆ?ßûæChÒÛ&ÆÈÖ©d"0EE‘UŒˆ…ïü5áÇWF°AÒ¨iU”HDŒg#,ˆâ†åuÈFšaS°$RFÜH¾>ßgׂb¨¡LcÇ‚.µ#-AËA#,»¢)Rª”Ë„“ l€0A6‰ÔÄ´!î(Æ0MjÓßqo:žéx†ˆÙ“<ÕZ3@š.ÿR°±I h•A gJ&ŒRx¥ÀŸÍR :ÇícG24H‘1C슗:!Ë@/@^u ŠGd#-xÊLçîÒçŽeéIb\&‰N:çLM7›Æ†×&VÔ„Y…aMR`ܼb#$€“©Õq#-Ĩ‘(ÏB \i¸¹²U8ÎB?¶ ˆ¼æã»Š4A"IÉ ˆÐ ³&Ýt»]^Jɵ妥åj˜€#,=#,‚Î#-PzÅ<•G;4ž¯G/MÑ6Tt€?‘˜!—gPž§|ï"'ªª…Šè#,Eˆ¡T’îtw¬Ó=VjÔüÉŽõ\÷،ͷê›a)- 4 [M,X+¬úôá›Ûæº*9š–JC¶…;Eó ‹D¼$©"Œ‰“d«Æ¹L®v)¯Ÿ ù·ôæxâ|HrUBsd)H7TÏ6PÉ,¬K@‚Ȱ‹!áaqÄÐ%Ï&òå2Ryç˜mø$ØžH›l®!€"hÎ#,Pú¸ÙE‘CÞ×"|#-¡H( ˆu2îÖƒM&ù.Ì8¥ÙÌɪDlQšó0Áèˆâš(nŒ‘¶)Æ':Ý$Êqبž^¿îj3±–„ø‹‘[»”•ó”G×H¾Èˆ«#,#,²¡´®\©er(ЏmëIüuïW#$>t‚D‰ªÜ•®Õh…7¶!ºr´Š{›ñ¾¢º5Ér¿,lƒø m´§~é!,L›Û.´A¶¦0ª†WE»ˆÈÇ‘©iX?I„ƒPàâHc¢?RøPà\;±b)5{À"' i)¦˜p†f¥,kiid§bºÐiˆµcÆ7ÂpÁ•€0tÙ2ÂÕ©kç}vj —ÄÊm0Ú5¶. s%†Ž¥—¿aŒ›«†…·¦«øU¶¹Îl ÀùAbÊ0G%±—žÙÛX’cSþŠa«›_ü~Æ`ί^ë‘o¢ØÔŽÅ#,6€ÒIrΆ €'$BîÀA`–#,!Ih¶XqK” dˆ©'QŒa[åàPyíL‚žò#$Y€:¢þ^ÉÛö#-+Ì8›øPùxvêe»#-ÔO¦]UÝFØÍ‘¤<ãG[&(c¨³g@L4&#$d Aézt(:ÔíäŠCH ÝÏ%l WV6L]äþñY#$Aˆ„"„ ‰’H:AÀ&ÏÎ)¯f¾{jYyºª#-»AµšÃaé©1p ›¡»9ZËVy'³ÍSÊš`n ’<Ú#-#- Ò—“ "[Iv¼WyÓuçyyÎÚí l¶&ÕbÔ[IZJ6Ò§$®îÚ¹wko.€˜4*šX Ðø~>DZ܄ f<#-Ô`ñ‚î#$l ¼ ×QR¥0/æ ì𘅨È##$§è&‡À«f[d0Ì ÌÕ Ò±^oSr]í¼ë-!Ñp´Ë…ÅŒI¬AH"Á-€¤#-d²Ú„𦂒F«ŒÇ¦›¥kRi¤7Z›¯9±Óf|­#—WIx·ŸOÎ!cLL9xô¡5ªbÀ¯P#XCd#-ÊÈrãO"–î˜tx†ò%¶FÖ¢D$ZnÚÙ©/#$Â…8kBþ­«@šT¨­G£·Ë›^1`ˆE‹…QÈ"†’€ËɈfÙlÄp##$³rŒø)ì€z÷΄{Ž…3óPÕ$&7Y,&¸wd™†–€UgNW\|¾gžú­ ÿ ³9Šû}^ ³¼?wíˆ×DúNÖ%BÚÜŒ#-½¡‚Ë6ö›j¨óÆ!WžÞ¶Ú«Œ47÷WF˜¶•—›Ý>“ܵ¾¦kq´ÂI½‘Q„ÍF` …¿žÊhå‡)ƒam¥‚L‡Q1°H B^„BF䃯'–)1{n.8ãqp0ˆ²B1ŽÙç~Î<ŒóÈ ‰m8Ì×Ë:H•LRÃ`×2…ÄMÆf#,ÂÉZæ[÷¼ð3´S-A¯ÁKå.öõÍ©79 " Û¦™–Û6Ǹ…¦R.Ù ‹Js#-½Ì ¶¨1‰3Ïë—¾ðÿ7ž¾¬qÈ´AÞ z¥ªØ/v“]Ýg‡Ÿ¸òÛdGÐ_É@_|Ç*D€¶ã§wž!Ö™²@ìm(ƒƒ¦ì#,ƒc˜©¡¥„1^bSwèÅÓc´Á™¿eØñˆÄᯔœ™.•6Ë3{lSQ¹olìõB-Xsù!’fûÜI4àÂÏ?eLB{˜Läüú¸ßœë鳓܄åÀ‡.Ô¿XzG¸* š„}Ú Ì#.Î#'#,ÿÙØeì°-ÆÓ!žñvTK¼Dƒ¢!ëk@íáP#-BÕݨE ¡ 1(ÐûB Ö]ZU¶ˆƒ y”´:ÊýÚÞýE·W#,A@àP…¼#,ë˜OY³?Ûò-árÑmT’£ïM<5çkÝíuÊëIã$£,E‰ „7Zl 'b‘ÐÕID Z ˆÓÄVˆˆŸ@AX¡ðpDDlKŒ¢jMØWž(¾ƒ ™Äk6"Fšj6ÖÌi¡­ØíU§êÿ5ÒóÆ©˜µ‹[‚´Y:ö#$ÿ°Œ€Lºñc¸Å…ÚµK2õƒsÅDÔq;T,÷²#-»GÖÐÓ«MÝĆì5bËA§$))<µèý.N¸¥PáI#-pÜ\!%ªØ‹õ‡‹G#^è¾¶ åÒäª9'j7óC´—Aª²Ç‡´O£xH‚þ»%#,ÂG3Cw2qlèhˆB(Μ0BmÙeJ´ 64FâU×-ݹ®Ýf©¥´¥m;v«©›ÚÙ¦µ]VÆŠæeâååݲ·–­÷¥”X”©¶ÖŠª’#$…€Õs@Ñ':©·[B]‡—¹)ZkÔR‚Ìk3t™[ È˜p*KH$r×ÙBÀ¦M±ë¾)6ó;÷>I„ù3ˆ‘µßåvSYtkxjá(L}ù9Ç€hf„…÷œ4¥×Œ¥%RV”¿±¹l3Ãü¶Kcƃa±-„Îj&Fk"¡`ta5gÇ£ç`ÕLH¥uÒÁFhÏ~»õð`ž0Dþº@L÷½íÈœ3(Èá¢9_Å#plíÐùœEr`Õ.H#$bAîœ~5ÞoÐ}#$«ž¢æô}­ý~ä,d ]#§Ý°[Ñ´¯ŸWªæ #$ÆV€61æØ¬6‹k¢å‹ö û/‡ú§Q4’d&ä)#-ÞÓ®”•³ä¹à¸I}jÒfÄAÓeÄP²·džª Î@$U-³ùÿwñÿ_ùÿH;ƒ™ÿM å>5/¦/É1}—IÝ­¶Mt¥„ôH~š"ÆÈ#ºBç}&$±rADDl [baxÈ*CôÊÁaÚ`ú¶ÌÌÓzü÷CÖ ë )HÛY4#$jP{À](vÇ‚ZéÛ–¯”¶¹ Ðêcï^&-#,¦ZÁô€ÜÑB)!Áú–2FwŽ¢ZtnÔÒ2èÖQùý÷5¡CL3«K`TŽÊ>O61¶g„ÒI-Lê,ÌéÎá²ÍÛöÅ·J å£ò²ТF®ð˜lÈÍÕš\ƒ¯8 ‰`Åñ ÁfW( DçRÄ:y¼lã´Ø´›m#,ãø1Daàgrôém·ÙÒi›€¡æ] ˆöÀ²R`QŸDÎ$„œëPú²– º³\ƒsúCñ·ìi é±,#rLŒÇ1¾îjyˆŠ©ÜöfbÍÓ $3SK »{hÔŒ:ÓëÞòÜ݉(3׆9*ÇC¡<סßâHùv¿ŸBź¿çÏ–~° AüDFW²"…BBÃÐNIƒÇ’™#-|û6¸á¦0óµW6º~EãXµbª6­‹j+‹kcFÙ1jKh¬–+b­¯Ûrض¹¬&Ì2—*Lף쮛x•Ò.E‘ `Fý.Ê«Ñdw$¾]{Ž'sno߆rCyRåž÷ÖQM“º½æ …ب“dg§U ‡Útcy€LߺÛ÷ÂmSõß—®áܦ> tÕÐð#,ümFm’ CEli5Ó3)„¿jó(‡çîyØ ¤ÜwÆ¿P{@*#,O5lyïÌ=@ƒ¼ÝHˆˆLî0ì`ÃÐ#'¨ï¾/w¶£ênX~û¤Ã¯Ú m&Çõ»670;o½Îbp[ ’Öp9w颗ÌôªPôÐöVâ£A'!æe×9¶“AòÕ½˜6²°XFå:IG÷3ÅÈÎ"L¦Àûtèr|™€±…üÛ­««32‡®(|°5_²Jª’C”ÖÈ×Iš+‚þð¥eG—¬ËÜ£o²(8´«±È’]½.…÷{7¤ÉN«#$Xíu•‘ëÃy”*Z¼Ù.¨UéE,×Z6LM¶­ #-d)”ƨQ`€Cífá­&Íž 42Þ¢9#-]*Š*‘‘."…Ö@‡aMEE$È6i‘¤‡…#jdÍb¡¢Ñ®ºê%¦€t%ûǼxƒtTfðÀÆÀo+Ö`1Z*QŒcLL21Ä¡C†!‘ÆàÉŽÑÇ”ÁÑ­SFD€G$Q1­1È‘÷Â#,! m¶vŠ6·ïܲz¦~©lÖ¿ƒÏyvîÈI´j¬¦ºUÍõÍÌ›%ZJÝ$ŒóuÚD•!J-+¾¿<žcÙ·}]{â‰Y{–êX<ºê_kÀ¤IkQ¤š`Á¶5–V+1ª¬¯Zóź³f¦el&¥¶SSC)ŠŒj’CJm6&Y¶i¢dme5/{·—UÉgw.mÉ›®¹mÍ;uñ.kÍçyE#-–¡ ²’pÅeˈ0c[0¥•¥M%æÅŠ"XuÓ'@`!¥¦¶aXD˜Ô`ðÙx7•l@ÁlŠ D$®Ç‰éêÌcu¨pî“#,j*Óciƒ)†À†? –Ì!U14Žù´`ø+«2¤ŸG#,'žúŽrm#,àMÈѦŠU ¢´7×#$ÓTÆ›Ž#,>bòöÞnµŠ ­{[¥DXoTÕ@¡!ªJG!c†œÙ™d°Ç>=<»Ës¤åÝ„|6¼PXÁˆµ›“$JÁk†Œ†@JB›0#-Ð…"±Xhk#-«’ÓâYXQ°†žFãÚXÉf6ZIYZôA ¤šÛÆÄ›²! fc¶¦0ÍûeÎ8Dj0ÆòÌo!”Ø6\32 jqŒÚ¡»±•Ç”­KLMb19—óÚÖÆã5a’/¥‘ègÜÕhÓ2ÝÆ°(ÑÑáèà6£ãavméL‡kò¦·ºš£¯Á=Z»–: 8‚OÔÍ(%N¯(i~°¾Ívâ|¬K$xjÍÍ­£-i\0#,XÓH¸m¬Æ-³ÛÕPbU6x`¥ˆÀ¡‘‘/˜×;Ð’8>Î3.£ØÔ°ÿCc Nn˜¸#-ˆp#,ÄpA×É#$1ì~ÿsMdNïc&`bbCv\?Ud.y“Þöéã펕ÑïEAZƒ+mó9a]ÈWÑŠÈcØBÍ×Ä€Ýq¡Ñr:Yz¼p6¢À¨b ™Ž9Ø-¤ˆœÍ.„ÙHIªä·+:>/Í>Û™ß×â`ˆr#,ùê.ˆƒEk¶Ö C»¡wúƃLe“ä98"HdÇænŽ$JosøôggEy»q¾äBÆŸ ²bCÛUY¨üœÈxddz ÝÄîf"e¢#m–B #,ÎⳆýW!Ç#,2—{~t_¿òù¾¸6s<––á·¦*ÑÕœcS Ÿ O`z‚"z¾¹C.>„9 #,`á ’ $"E 6Op Š+2™tÞ[ut·¶®×•_ΔZQˆ#-HlÙM*d"d!aœERŽFȤm!¡Ú¨è*àÒmVÛ²ÖᵺmRZ»ÇEб«Î¹tÕÖÛš»·2»^-E<3RXÚE™‘Š•6çÛwuXµ%–VZ•õ5W¯KZMÍ«š›uçžjF*,R[,“=©m (CèÐÒüXºÂŒz„rE%÷âœÏ†YȨá*Z(…Ä¢…DŒ!B4ÀV k*ÏL‘BêÜZB/K’ ¹#-VŠAXŠ0…Ì…Œ`¢H†ˆ@¸]÷¼øi”5Ãk•t»VWZøV¢5©Jj‘¤ê¢›(«13Jj*Š ±S4ThÔUE±µ”´4hÚf©6K2†dĵ4QRËHÆÆ¶Í^v«£~ QRCëðÌ>í#,ìûb*1×¶¶¼ö#,%Z6„©#,Ä»MVÇмüQßÈÚ[&h¿/Lˑȭă5¥hsÊêîù0â#-‹ü(êw!$Rý†¦›ZJehß•Íöoz…š6«îšß•ZÜ5Y?w«¨-os¨‹iÝÙ×ZîÑK[s«›QUÝ]¥+ZV²½íÙ¬´QŠ«¶›vë÷¼ßè—Ï\³A‘ˆ9Ð÷”{}Á@Ì4æ&‰ f!D@U€ˆs#,‹€‰5]×m/U"Tj#-ÒD£"2#,Am’•1A¼®©ˆh£º#$„D̸vÆtã!²Ø„ÒzCØü.ÔøŽÏ£¡¯LÝåyà±´˜=Æò®âŠ~Ûd1›½R¢,'Õšl$žŒ€}ÇŒ®‡Ï"ák·36ã²0¦f3ŸšËlc]Ì“MBbŒè?$fÉ#,Aœ©ÃeBa™-œèós‡ÙÛuKk¼àÜ%#ÏÔ÷œ|=½uÐc]y•£¨LjØÄ˜¬¶|U¿º•c‰À*w kd9“ò+¤»/«½ãxÑá4)‹iy°Ì˜’ò)˜AsÛ\Œ´šraõ0„¶´Îxf´m§×Ì…uÃÜöÊÆØž i¢ã=Œ0놙I†‘.ïg¹á¡B°Uþy;hV1"Éà¯xòˆRذ ÜzÈfAçÏhVAów¯K!Ú€£µM“•–ÏLm?‡•ø«8*&*ž“2ÊuJÚ©X¡ìÒl>Ãj(ô¡ö\}Á f‰Ê ôÄFXD­‹QªKo¥/ÂÖ·MIEŸêcý„\¯B®Q‘Q±3ˆˆKÊ „#$Y **…‚‹oô[VîÛÕÊÓðqLJ:.3çH†"ã¶æ>Ò’tíL_ÉÆ! °#-"3i®®Óã[–Û¥cWŒX€IiÕËØU+2% % Ašc;,ƒ‚FªœÃ¶daÊ ˆ1s­·»Zß|•m³$Æ6K[WB;9•KA ªJR­ Š(² L,²ŠdïŬad‘Œ„A€08‰3œix-Âf‘«EDª2¤ L0m-­jÔKƒ€«ôëZdTlm L(PIU¡P¡X¬bÀixŒ¨å€4™ˆWeX#€4dÐCƒE“CتõÄÑXE#>Âq,Œ®^Ý[¯…{™Ø…)€¢µÌÜ胞#Ç;Øv¶ƒ SŸyÀo•b±ŸVdͺhd7np¾…3hÖ™2e[#-ãŒþ}1ÎX#,°#Áí൭¯Ã5UEµ_¯xLE#$n!ÚÐG#$‘Dãù»/#,t#$úÏ‘ëC iú W*=“oññ‡ LŠ­DO+'äd–8»?x JâöóÍ‚u¸îî˜ø»Ã¡ñ•î~‰zXÓcó"<ß¿–®ºsíýNš<#,o:ýìëÇSÇŽ®À:ŸéjÔVª¹’HÅE›o½@ª&ƒ„—;wÎ7º'Bë /ËÓ!¸(ö„DÄ«#,4ØÏ‰Å4Ñ#$‚mí#,‰œÏ+„½ðëpÁòe¡)P/‚GèÌÃ0`ŠÑ# #,˜ì–,Ø9-±’E¥‚½uBÃ4¥Âasl,¯¨T/ð€m$Ô›E›—Þ퀢<<¡ÝÇ„`ж’Rz"”oFªg©TéÍF̧õ#$¢þãù°vp#$Çäý]íú²‚sUt¢5Wv²ÔLŒCšPÀ²Zâ¨]½Ã‘B¢ÔQÈîLgÑû,øÜx‚¥á*BÝWka2³²µÙ˯7–·Û2} X#-HÃFîÁ«ª¶,KH\4£®ï¨”£/+’(ÙRÈ–DFÂl©j/®²Ôš,RRl`ñ¸lFæÜ®[swÓy¼»q×]Fc1r»»tйx·&Øå6[ÉäÛ«—UÍfX°íÞZ¦ÚŒ;m®›–º[Y*¥,muŠscvY6MoѺr×vgMrF•;·AʺËf£hÑͪ-u¦Ú H D#$J?×fÈ+Á¸#-YTjzà#,ØMÈ‘P{wú¸‰@â¸;OLBÄ<à8#$ïDÿWxxBðA`Äj€ ”'s,#,ˆŠ/Æ ä¢§êÍO/ᜧ¸;Gl¨¤¨Ð$Ì!™#$PÚ.tÎpBê <´œÀúb{7zwx#-O¾…#-ÏðàŸ£¸:”7‡¹6­¾ý¶f`е™›e¸ìH`&-Aí€%B1ª ?àSýP‘Qˆ@PŒP0@Òv{úl†PÛQ«h¤±­“V+V…’1 ˆdDöطؠ$XG_W.ÑŒ›–è]Ýjq×wR͢ƻkÙkÅy&üžo"hgÀÔÜ„°âD«zþBAÈíëVåºSζùÌHǽ])2LÐlEòm­}}n¹µ|½µ¯k£Ë_ê‹$D…ñz«%ˆH¨4R4¤Y´[0BФc(9Pk€‡dýÜ12#-)éQKi^˜Q?Aí ¯Ób€Ä1„F1X¥Áñ?—ù&§º°ÄC¬6LâñAUyDE€QŒY¦Ue’´Ú¦ÖÊib@wãæf©¡ØD#$ÄtU€¢ªê¤ii“6¢ÖØjÔQÕe}'›#$↠ çËîèÍePTa’Ó13tÕ¥j3Z3[1an\1ÁAý©LÖ¦wQѵ.å}RFŒà¦Ö)SAsÿR|#$°Ô’MÁ©G¯ãÎüÍ€MÏYoUÕ;þÛýgë‡ÎlXùÀCPBŸÐf1Žý«øã·Bn]l€pƒ,8z×bm¦0d !›“'k8âƒT„ü¶‹£HÓK£äcb$ŠAÝÞ„Ý“—Ò²¢UÖ5 çx ÒÌÝ(;Éâx³^ß IRxÖÃûiñBí›1¡ D‘:É-CJ‰Ç‡WmwcuµòîÔW•ÙŽ¸›XËœá‰ìm,(d)B"`mÖ ´Øµ¸…FëZ•„Ö#,c1˜­Û(1¡¶f:ª¬(*­͹ãñ5^1zÛ•%¹|¬Õ!l„’ÆA3(¿ÚìØª¯ ˜Á12N"°AUñq'·ÚñC»;*¬õµ¤ELàµ#-‹&Q36%Ëet"ÔlË$¯_¬V£M`°Ñ¨ÛiX‘¤ñÍwšQן;ÛÅ5ky-»V6ÔVØÚµ½¶×.›”P4uŸ$4ˆ:›²LòêUÍ6,œ8s †#(ýôDþhsï)‚ƒJDY3ï=ÜžKñ­VE…D@Œ¤AB°tÝ 1QÊÀ‰j% –!b-9ú¾Êw0ú€7þ¸èüb*tEHÆ@Þ1#$:U;8A¢ ²©VF$%B¨bªt„E?42‚ÈH:É1`Qq%,U¨‹f#,‘¨-PHE:¨«;1#ZFh‚àëK,z¿JÃX{¡ß3‚Å>E?éDÚñç¿ø2ôm PæÈ9JV&-¢&”ºN3÷CA¯”«ékk‚(r8ϤvC1 ds\ÁZšãñvzï;½îõÖ„$‘¦ÖcÁ¨ñ©˜ÛvHEJêŽÿÆ…bþü†Ù¥ "­ ç¿g#QðïÎU#-²ðªÛl+ݦ¿Úv=z/.9ÒÀшÚ(¢p‘üñE #-$öPÓU¶T€ #$‚µN°…O´x@±;¢j³™sZlÏá#$qmTë ½.p'Áe"ë/ÚÁý0õ°¦nmŒz˜I’I… LѧèÖ¿šÖÙ>ÍæükVù€£©òPý¡~Î\ä'œúòë–±bþò½òÔåt©A_Àd¨Š£dõï/cá’Ì3M(«xDZ¢)#-&I}œ¡lýÕz0Ç#$ËHh•TvØ¡†[¸rÓý¦sLZºBÔjÜ]Œ."c}-LJ¤fIœSá#,Üò;š*¡É§t„ÝÙ±sLØ8ø°˜±Z)¨7\Èâ³5 E«‰AœÁÆj#$3 &4hȉi ,ÁüÞ àˆ<Zà¹ÝŸ8‡ÝøÖ×=j>ð†@]»$ ½¢e |>¶8›'[fZ?#-WU9õûUOŠÇX½³Ÿcö ‡0´U>²L#»3‰#…U9±Ì¤K«*%ø«‚PâûáÚÞ¿”}S7Þ"Ë6­¸JÚ·¸ç#ØÐõ.ßdÑüû³¦4åÐMÔšo¤Ýwg:ºÊóËÖ¹ŸL…‘Ë«FÈñŒcÉ y<@0m‰°Ô1=,"Ñ›ÄciiDšMc¦ƒ±õû†E“šAÙŸl¾™5¹~)zXDoÌÀPè„(#ÛFµ¢2ØÌÖ„ÔñrDÓËNÖt§š^ßÒõ¤ó``òWȆìW›tt€‹‘tÌoæ‚Çœ àíEÈÎá,¯ktÅq㦾ØS”#,@Ó5ÛˆãÓ´®Ä< o:ÀŒ&ï/*/å÷µ4M^)Æ@à)uk¢Iî¤7»}Ù ôh:#,ãƒËXoæšD›1œ!³ÍBuÞaHyU´˜ˆ^°Ú§=â^'YŠerDrÜ:0­ÈöÒѤ4‘²¢LÖÅË.™cÜädD%‘ßNŠtCY4¨ìÌÛ||Ï Ù?è'Õ>Ðâwóª1ý;Oö*ŒÀï Öùé“8ZT;¸f4êÕuT „x/˜»Î]È\³‚¬¯uçY¸ÈéÄ\Xñ*ºS¸‡™²hŽè‡ft”#,äÛ©¨-l§œU²#-ð ‡%ÇL,ˆ÷º`+H&ah#à †H{½)ï#$¤1S)"¤Y jN'iGOu9^4k°Ã<1±ÉîªfÅ[qn¿Fý¹Ï÷֧͇ŠM–cK±Áü­kß8’[!dš­ü~SkP6vü EGÆ¿;Õ#-·¬màåw]°²#-¢PÉ"UÀ¶íÉqãJ›2²ëw‡ÆÖóÝBÚ|k&nco–µ§¥}uûq¾uÇFºË`8ë+ÿ4^ÏLMú-÷ïÃͪNºMwG"Û]M#$É;ÕJ‹â,k¬qfDŠØCˆ—ãov6¬ÎCWÎÐB¼PRö¤dNâíA#-DÉ„!rÐíÏ_¾qZ†À±U-3ÓX.[•l½¸œ‰Ø”»…qÔÇk…½Û £„ðÁiîç[iXÖª2‚XJó׸r›ª ÔÍ¢zÄ2^DÎ$»-‘ÏÇF´Œ°ÑT¡iª°ŽƒÆ+žgËh9¢•]ç§?o\oë‘a ´}BvŒ¶ƒ¦t±‰#-§9£H¥#$3~Ø;ò€j4šIõpîøª’µ×8À1oE–ih¿ud²•½mRZ׿Úû±’=¢0èƒpò ¢$‚€²$VPðà^®#¸Óǧ"¤{kÀüuê=IÌÍh¨rDßÚrcaôâ¸ûO‚wKˆÈõjäQ2-×™n(¢:æ¬ÙšY°Øwàî>°ڲ%¸7—I0}I^_;#$'lPvµm[|âZÞã´—6ÅJgÒáv DWBì)?XÈu#À8'¥@Ò•¥çD’H:ßó×Ñ´+§-&HTÛÌ0uf0ç-Æ`#,ÁΨ1_FŒá”!(úúNùÌéʳû‘ o'ÓŽà/RDï€$PˆR0LÃË ôšÝ´áámœ„÷n˜ì$Ôv­ûÓ1ŸË3ÂØw‚Ð…€1ŒVˆ8\bÖ¼yþÇõ|•”‘{UûŠcEé‚R6„ˆ(çÜÖ5¡j Ρú8oó „hFŠ$H¨ÙCQé”ôYÄú(Lô; žCŸí8˜î»Žê…t‘ÁŒ÷¶ÿEú)£Y™ùY”Ó†x|±]ĽT0B>-4ÐÿÎé|ó!#-u9L!®&±«„[·Büºå÷^.úÝ3´Ð½!Ž(Ü\DŽ—…ßô?·º¸{ÂR·á|<rV¸¥a‰îF~>Ø×fÇâŠüø±{¡ÙXIÅË@8yÅyxÐbÆH$æZ$ÎTlírDžʴéÊJ¡!ó¶È?ÙU½#,G=·†V žC‘ôDþœüïðõBôUŒ‹ù"©ŽEÊIÈ—Û—X”Çr{oÜ;•ÎåÆš˜:tà€^sîªÜóõ¼bØ!©äÀéBL¥I=ÅjULF\9Þ…j" h ¨ý<ìŽ ªnIjšŸ^G?Bëûæ“O“É“°ÑoÁPÚMþ2ÝË>‹¥&>íVïv‰±5&t4!×—§⌀E#¥#-VÖ°Eµ@Y¾Êá!„SòÅ䪮/W%Ú­è—‚Ø…ŠY1+äyå›ò¬#%©TæžNc•Ê»§ðë¶Ù"±¶Ú#b@¤ D$UŒíáºÂðzkT’ @1ÁÜž¥Ñbéçfþ_‹¸e¨¢(mP=•*Ù"€¤XÝør˜Â?wºÖò6ŒÊ3dˆ£0h ”Ú&&¢)1 1¢Qˆ)²2lM#-L›*M… …¯ÞÛìÝ<ñ¹>qt‹ÖI#ć®/¿íÕFÞ¼ñÖ{íJI7m6eæ1†ÃA¢dÁ–H0ÃŽ“!(ŒácX¹¼sÈŠ€F˜xÖΘ²¿¾V݇ëÓ ©btêåî,¿\4i‡fOú7FØÝ;éͦ6‚Ÿƒäþ^SÑÕdÈ”%lÈ`z˜Ûo,âf7¥Âý8lµÓIª©ÈEi–0bV° ƒ!#,‡.,M†ÓWué5»–оvÀ~•|3©•Í M;’Šxß`w³åݪªO ô(îÿÈ$”~¥8r•IV6›Mð™­¬xÕ$‡óB,™Ïý™ß×o‰#-ƒ´š|°f~ä@4ZËbU1’Þ^ ÎmÎÔm훦ÂÞÔ`‚foBH¬M#$ŒNuÉMÊÛµÝ.n®ÕÒ;âó¼€$ÓQ±ÃUŒk \¸ˆa[f6%a„ âd0 ) ”0’Í¢#$†b€™uFݸÕ*ÉQ¯?½[`«E±©5¦mãn#$dbr#-0#$Œåϧ4'ÊÅÝ™Q–˜‹ïu˜D­.AÀa8:’Û#,9S²FäVxHªjÚ2áŠB{‹˜´Ñú)…~÷ð/S…‡„@Œ¬ÅkAyfx˜K€Â„#-ŽD3Ì:#á§ÜqœâGHFÞE¶ƒQ¶‰[y!³[ZM§‘V Õ­›4T[8Ž ¡6‘ZHF]6ô±á©²9!ã¼Ûw6QuÕâ×âG½Ü‰#$”óÚ˜F#, )h’iB @8òSLž"™EA0ÚiŽ9 –2ËmM6¦™LÆ0c&A³Dæy£–´#,±ŒÃtoÛ„q!Øå)ZEiÙ­YB²Hì2¥ƒÌQ¨ÔN }!˜F´ô°’ ·ÓʧF‚µ>~³D¹1ƒk«±'ÁÖ¸¸‡¢õ#,kÊéf,ŠF4mE“ÆàÍ1ÂfÍጒ.òkRPn r¨ãnbZ@Á<(Ó„}2#,*ñ4¼T`¤w QÁZáÕF`ß)Á‹Z´œj¦†D17Eº4E.ÇA¨…&lh&¤bŒ^)™x¤a¤Ô†Ø¨#,3Yó~°A¹<.9ܱ™v‚ìÆ¤cQ­*ÑEHX‚Á`oTgh”Ë•δ"L+"†##,3›„‚¬PÂBæ¹fkß:ÕãY9eå×o3dˆ Å‘ÜH;ê¨íšx¸-оR&æ#,¾dÓƒFäH`›MB¤hic±’Íâ”m½1At‡©†6ÍC›­Ò ÅÍÜ©¼i± h;MŽZñ%FŸ`Á$0ŒÅA¢‘AjãÜÊ+#,<ÆàÖé: £âO9ÛA«ÀRWÌô0ÚÄr£fÕÉÈEö:Ó{‰aQYÓ‡Óu-¨s÷lÐh3"]¦€1±#,eTET„OHÑ?8epŸT˜t3ƒ+DZ²Ø#i‘Lg‹Tƒ±x:vÜF#,UE*à‰Dc#,‹–0RP¦ÑAD—0Ò#-AD%‰*("‰Rép$CB$¢0Å­ÈaX#$8ZhûÓTŠäâúUHTdI\Ž6Ô‘V?è²i´iŒDÈIûh¥‹T5HÈ-njNß ‰j-¶(Z›6™²É‘dŠ- ¨‡æñ)OáS ËÔc!\†Òçúøõ¹D  ‚D(>9À<‰#$±ß€Sù²Å¿ê¢Ç¦5¢zè£o®J¬Ë‹¼}ABD!D‰¬Q£Aà‘Bå¶ÒörD÷Ô¤¨IØzëÞ &„æë§ÙgiPR+‚ŠÂ,Ÿt¹©Ï/1F r†“ É&TP÷‘ÙY áGüìY’6&É #,‚ D„´ŠA¡·$T3ÕHQ¡Š¨6LmÖ 1Ì¥ÆÁîFµR¦ÌP¦ ø OÞ¥—P†¦¦zÇ)ˆ‘„M%¯¨ÒT¤ óZÛÉÊÆC®«ùl°Ø ‰é3e61.ÎòX× ySœ¿^«z÷nU¹Ù!tR`6ø¼ævR<œ$Y¬Ùâjã‚?0tH~¦»1bÓЭF^¤wé‹éŽ|›s:~¾:%zз­@×ìýq$d&ä섉2w¿<÷): K–¹DLá 'hÂîN„•Ÿ#ÁGˆ¦#Gø½ý×ÍÊ¢ý#-d‚hxC •dJ ë_wÆší:j|û6íä|Šct öì»úûÌ»ÐC—?ƒè}næGq¤’xmâî¼ì²7ÑÆV$5WÙ#,"̈ò­;ü)ŒØ#,-̃±Â—£Ü£ôD¯Ÿ˜aSœj"¥Bƒ#$˜R ucÒ"ÀÍt1N#‰mP;•`@‰µ€=3©ÖÅM»Š¨TA80xV»V[ìr)#$ž?2À÷âU;gm¤b„)h°: ¼æfÂÃJM†$Å(¨²ùÚ¿§Z½×Ëx¿T„§|agO•TØËˆu†¤ ¾X“ ÊW5T†%ÛQa?&³ÈßçHœŽ°LÜØ-aB!Ñ_馃‘‡x"+2°1²D£i ×ìò¼QkâslVM´kákv&Ü»»m\¬mŠÅŽnVéªÆ®š²ÆÆ×Kk–Én\ªÜÔîøk‘Q«&£hÄQ¢¹x³º±jþÊ[Ç­|+ë¹ÂÅ­`Øoº‘sŠüCˆ#-24èÓ¢øfbVaêMÞ°ó2öGÆtÔ‘! ˆÑ#$U¦ô±?— ‘"]ƒÃ»‡®ŸÃ7ÈÖƒ¡ãU~¨½*¹iGéTsûÁä"!Òšl¿ ? ‰,}#-`]†“š#-E#$ªÁv<}VZiL) Cëh›KÙˆÂÈÜDÒ §¼;îð0ó¡Òà1ÁÅÀfX€ˆaˆ#$È H¢©€‚¥‹$‚EÞ"ÞD»uŠ…b]½Þ²¢ôD€‚ö@R MdÛdÛhÚ5™Œh‘T#dýÇ‘ó@°h(:„TÿÀ#$ž°éÛòD=V î·QF-Pí¶FÁQãHÚAð„öF0âjWl™kíÄœFD R—’ª© vLÇÞw/÷BA$ ¿ÖoJɺÎx+ê©3ÏPÍ€zâ€s ‰­|šíûjêµýÝ)ªfl’JPÁ±‹K$e£I±f†Ô”mFÊRd”’±aM­¢µlmj5jZV™¶j52¦Ñ«E±´˜$ЯÖgÊ·[’1ºÖd"Èç­]Ží®¿oÏoo|¤c#-çµe ãƒ1Á¼`DîA‰P#v˜(‚Ĩ2H*ëO V:B` ˆ£hpƒc`#,¦2KXH‚…P(Ø:0Z``˜¢P0Ñh`)#-ˆ¨Š€A» #-/‚H#-5ˆÄˆêþ­l0C>•!h€Û"P[¥¦»ºwf.©ûjíz·WµLE´@ €‰-ŒU¢$l)Ð9\T7À—Q|,pÌ!0¦Ïì&ÞjÃΟ‡„¿ÏÓ>ªÒç–Š)TR½€}! *HÂ@öíÃúö6Dw4=­Sô”<› ÁM¶~¦ë Xhh eÅ¢0%Æ LÖî̆6GG¹D± Ð<#oÃŒJˆR•ÙÓŠ˜¸D«Ǫ”™²‡#-S­Š0 Mj‘Â?«#,ôäÈx*s! O9®šáÞ;Ë·JëyeÔ©LÊé]îµsQµ±¨¶Ú¨ÔV¤…Á¥±íô8K®…ÈžeNÀ¨NïÒ¨˜©ìÅS›÷±› 1MǼu«0bªY·©|uƶݙš N˹Û4g®±ë™U©\Â|•ÇáWL ÏÝç ü9s€ZYLÄ’8œ9vÒžÛ=²ë[?1ð÷7šêç”îÙ¥º'Q#$‘ Ð)âULµHø*ÚúIv„æ¦zú½ËZá¸j“^u&à˜L„?Yç€ó„“ƒz¡›\ÏÂ$S·|Ðý‡Ê­`ˆ A‡#,ªKÑðª.H\¡TôÚŠ•#,ÄQÖ":Å’ç#-GŒµÉÓ+·þôä'ÕΉ¹¶¼9+¹‡mp™Ô|76¬×ÀBæ]@¶­KPîT,—¯ •¬µŽ~×§ørð'_/›ô+ã’É)§oáUºoŒ÷9ßFp»rýÕ*ÎH;âV›©ö˹֧ˆ=+ˆ²ÐŒ$8‡]S8¶â[€è¨„³})*^Óƒiú;ÝâF;tX½){›—Ù[=b¡C—ñé×§LãwI¤ÔɃ í1µq%1ÉïZ³qÐÚȤpo3#(!˜Úö_^óž s똮¾6•¯aMÒÜÚ. ^8’•ìeŒ ]í+Lë <(D’³LcÝÄû³yíÛJ(Ë+©âÕÔpýçÓ©:†fyœ#,Úc|›ã~:o®÷·3Ëg›Þ¿›í¿| Ó>ùšÙázÀòîóÞsß0>O„8LíÙå]Ï»b-ñ¬ë¢u¥Ï;=7î+ פ;ðt©„cw¹wÛ°4#Õ7…êšL¾Áº4²]¯=Oªó'SˆÊåAXƒ ËdaMžÍ´Ûð:¾¡Ûݤ3DH‹ØÏÇ”r»B „±ºÓŠ;ÂdcT‘,LÞcO±3µ½fÂðÁCj0½§#iäGQÌ1“aºÞÂÂntò§f¿$xj¦†GP¢PÒÞÒø×%ï€K] p2ïTÉ:yÇ;hƽ|ó0lÖ{ó¹âdaBù㱜¨îÅ#,b‚Â…¹⬰r½†åÚTN½’ðŽŽTMQ§;º6c86r8Cy1åŽäññ”1ä½0~–n8X(Å&£7²‡ nAÒ…Tnh”×1 Ò'™Ç¦#ÓJ'hÀ^D£Ì0ªsÀ9iaJ7÷{¶lVàÆÄ›a#,~þ\)ã\­¾Uë€vï …Ñ(fóG ‰$éÔ v#-ß­rÒbûyíc€n6©C¾H\¸ÔHRžpäÄ?z¨eUçÝû<økb‘áÔDv–n‘ÒŽ|œeÂN‰S¯D®Óe4:°!ašÄP.tËÜÏ\qá¢eeZ:\æfZ½&ësz!·E~J«_EM ó‚–†Pv‹¸/ëÛkm50üÝéhݦô9K7ïx“›#,:+‰%[±eb˜z+uÒ(ˆÌ'‘„ÎA…|©S8D¬çáœhFG²ƒuÒ¶6çfÛq;Ó¥—åÞsí<ÛÉ­ã¤õD2çŽc¢¥¥'£Ë°‡ß¬sQB(ª›J"ªhšr7àÄSQb§í·8•l8¶ÙöŽPà²E°yªñÂVHhm•ShÔE[´«Ç“&ŠìAÚ#,BÒGyOUIˆjû9tŽ;ÈJ‡+ ™ˆ a£þæÎD(Ù÷®€sÏ>!«…‘̺¾bª7¦­Zç#,’›­áÝÔ>º9)­i­;hÈÅF¨†#-Ç>|ÚR cüßx‘ë­ú»íp… Ï;ñ,Jë³B[­*Íj$™ß¼¹‹sN™£Ž+b¬FJƾçk"ž‹Dv:e£;F#-Ç~›•Ù¸°ëíÓÓ|!°4®²ÜUÙ*ÉOK™¢-iJÙXªqf±²¦kÔ¾æù&ÅÓ{–ߨâN»éÔ ¡¿ðÅUûç}¡“!ç»÷±Ï)}Ýø¾Ñª˜ù®+Ê]-ã9|Û`¹×‚¼ŸPhì–þõSæá¨9¸„¼Hï·– ¼§v»{ÂnT·ä3­ÔñÚ¹:Fƒ®ë´­«T+Œð2"š>>ƒÝ @9œÁ ÜÀÕ‰i PûLé¤ôqvÃZ*ÅíÍÚê/DaG<”Õp:,‘íÅé8TÎàù¡b,.Hˆ T4&°Wl ©¹fCEE»Ãšá(ª#,žg+—ÆTfd´H{L+3ÉÂL27¦i!J· `ög#-¾ ‹(¸ Ún7#$×0m4Ô ©|Ã\úQ gUL1#Ò7‹Èî0¥Í—èpòwY¬éC0±ÍÞ×íË'”“>PÀYqÎ3TQ¨óôî[*‘ªº…CC^*<ËiìâgHuȳßJ´›nyr%!)¢ðBÊqLšâPlÄ,8xb]kRA]`§ˆŒÅ’r¨öF®‡EZ–ð#©¡¶`î ‚O)`5VHšQ>‰ŽZôëmo»¢ÈédGz†7MÁ·ÝöcÓ‡hˆx™áX3¨¦ù_Nr‡`ÆN¦8Ú››íú Žø¤ß}‡žõÀüæÆD„(ÄH~I‡µÞkh°‹dÐôÛÖ“Šƒmš,¬ FËbQAP„ƒŽ4¢d SÌhXÐ‚ŽŠ®UâòNísj/]µæêàSÌ$$-#$¡ NP±$«Öõj6­Í«r¬®…ñíÄ:³)DQ°5‚ÆÓX#,E (+)°”6K¢Ë@‡B‡_㞸å}sò5Îä-­ˆfr#$2`?–ü#‰ ˆN›¡,¶Ð‘›ïïœa~nwõ^*ñ·"䨶•¶µûMh¶´j­rI@€e ,Ríõ­RcO›nc…‹3ƒî†kìºÇŽ[-L ¥/òócÆÁ¥Šºˆ2Dk—ÎÔ``÷0ÛÛm¼Êæa›­é#,?Ð:6bÑ"»#-0eq2 îšU>ìD:kN4`*ÌC<2º¶ÀJ^|-y4ÅÆBBb®ƒ)J ˆÒ+åÆž¤wŒ»¦ñ¬Â$KFe#-bÒfé¦60mÇ6C#-&£I´ÚXÔ‘K wQXØšiŽ43IÁcEo-øÔÊ4ùN±n锎’B­§ß-øÙ¼ÈQl›ª#-@Y1!†Md3N Cf;w. S5+q…‰¸ìcn½Œ½5)¦V޼sµ–or3SEh„†¢­cÓkZÕ2#,ÂcYqäl5««Ðç9«eo†L¶$1±Š Œˆo0lâDX³LÐ*±c‚ðRV Ee ºÐ ­¶&t–¿-ñ­NyÉÙ»Z`,+!™ÊÆõ«xlQ#,PPˆÂés5%”%#,A1ÇÓ1îÇŠºÆ-\ˆD#,æFÝd!jD¼ÖU8Õ̯–iY2Rµ„—zØñÂeÔ¨¯°3¬FÛ³mGÆ-8Ì7ÂÌs`œNÑKf¬5ß4Óž.º ¹T¤q²ìaf‡ŸÇ0õÓOZMN ‹ÊŽrÔº¡#,igD2V8Ôjm†RT¡VĦ‡Òi´ÄÔ ^5hÛÍÅ·“%–WoNvµ°›—O2­CdT¨­ux1 çqkd«”5¤šŒ0Ê5C7c.Ûº#-Œ~OŒÕJ1Œ¤Á±N5'ÿ€ä´¶Ù†Ô¬(~5 ŒMÔÕOË ÆŠä$¼*6q+½M4‘§“• §Ý¡%Ýk3ÓxÓpÎww;Íõ:ò]uxÆñ´C!†Øa)2,™É[ LÂl\®Í3‹jˆ™¹˜:w”kcÛÊtïŠç#-kfSŒÚ+:Ý6ÛnÈÇ4DEmþžÔþ­è£Ú#-Â>•æcèÊ(ú"‹”{‘hÂG[²Ž Á5Ïx,M#¬#,Á¬É唯iÛMAùœÑ DN^›x°¶e)Ï»$šÕŒÏÛF‹€û0­\d#3ÅÜ$d3ášlR³Æ#,‰{)Â2q' )LÚCH"¡fx¡«s2±5»Æ‰œ•4m™!‚ì• F Ö§PÓKO‰0!425¤ë¬Ä5–p‘ްgN 8ÎIÆÆÆ4sJB¬â/Mîí>‹¿Ò„…ñãJÒ¯\`O#Öz¦¾ÿ©Ðü©LŒ"0PçR‡cD’Ücz+ž¾¸ÿ8þÎÎÉ&ÒK/.ÞOÔµ~›4•߃œ¹…¡HRÆÆM¿‚Q:.²é!ù™H}G| ʧ}XÍÆÁ¶1c)»#-Hª¨“$ã=¾…*ûØSÔ×j‡äÞÊÊÅ—Nq˜>&E_!äó$sÁšq? ìw‹ºæ€Ÿ@¯ÌÐõ´ã/·ŠœA<& ÿ à*,{¾ò$‘‚&Š÷îó¶·Ú·•mþ듌¶ƒhI^‚ì@öÕ#$1P6¡‡ÌB^´c,$Ìm¾îbLKnnãæJAHi2`Ín};ué¶íÁÞ—~'iëª c„hªÝÇŠüÌàâWy×ô\:7"ŠeH(2cŸ0º)$‘`ÒYsý´a»ëØ^Úw¤Ã$jEõÅâŸëÕ¯Äç³ §Æq@ƒ™ òñH #$XR%ªKX£íkZæÔ‹e#-MFŒjÄgá»W3i‚±B T¹,'À¸Nhd'}Ù2‘Dƒ#-• É#-=¾Ì‹fn(R‡õI›ùañ\%Jˆì(µ#,3†á÷åѪƈ|œ]5JŠ82V)©#”˜ˆ¢ÛfH6 mǦ”Ô9b#-ô@‘C)v^¨[Á-ë-ŽÕ¼c¦ù/.íÐ’ìå|yÚó4QJk³o‰^/4M¦Væ(Ëõ@å¬Ôp.‹ƒÃ@+qvwˆ@ hE`¡GLŒŠÄˆÄ½Vç•tÓIâ|h5O“„ŠC’B“ºod4 Â”7R¬Bé*¤EÚ&ìŠLÄ®mq3~¿m|ï‹Ùü>×!`3gꛞ.¶î¤b•Hqb÷Àìaå:f˜„< d=@a.$›ÁMI²#,±AU° ‚A‘ ‰t#,h_¾*ÁUW#$œØ ‡·>`ÓDÀF”®Y·D´õ,$@#-õ÷6SÖþß#,Ùâ{‹w–1€û¤ÓÃQêœgñ%DôÊ3®ô=dHFBÿ†/öà˜t…„|êÙ>~'š¹¼:ÇÀhªx\°\c¡¤H§‘R KIpÈFåRKºŽÜC]ó8¦þáN?d#$úf\x¸ÌŠÅÌiE uú¸è?ûì¶×¢^¾îþ“I!!Gž ÁC»Ž’3‹ðz(pÅ}Å×~€ÀTÒXGAwœm[h›°Xw–0&k‘’ ûèý1G%’2ŠN¤Ê5J%8ºÔÆ6ÓC*CEŒBC(ˆÔ¢-7b‹ð€èé]«É›NË[B/³Ö{lÄëóWBîSu}iÒ6¹(×Ð|(Šçƒ)½ï$$@ˆ²/‰g«bãµør'…º{¤Ðõüäເ7#$éýEƒ¥J0>)QTJ¡S‘ê6ë'ûÓ6)ç0l8î°)qƒQsZàFÀ›Ú@+eϨ‡åå#-_V<±#,ùe;DZŸßò˜‡pëTš‡ÄÓÛ#$(–ðÀ°¾ÛÇÞvE EAI" TÐÓëRøj”w“û_ž·jƒ*¤<£ÃY‚ïÕòü#,d>ÍëV…ÉòtÎo¦6kŽÇNjY#-®š,0!Š£[«Œäô€‡btk?$¦ Âz¶!;;N°Ä>EÇÂÄ™!”¹Pœ“ð#$—"wnn¹ë°ˆýQkí*©1.àï—h”UÚ\zce)^5™¥#Ð'4AÏëÆæ1*)Õ8èÊg!ÆæHͼ»{7©bl±ç#,½#,¾¯µˆÏÒ-ÕŠ¤Ø—S»‡ªËÃÓÂÎxÔÏ™ƒfÌuAã6²…–#$ŒŠ‘†ë¥ïLICJLFßy›ApBœE?ãa‡lÛmŠXllÓ¼¶UXɧ #,#$q‚kŒ¦T Úr— '"8d±†éB#-í(£^vÆ Ô#6^f„#$ÆS0,½ Î gj9ïÛ¦ .>;ë GF—¡) ã%1Í·Nz5#,ÖDæé޳± „J´Ÿ,³’r`n‡“`šò¨ÆUCj•" #-#,C÷  ™™ª˜6k#¤ÌP;S¦ZÅ5 ²xTIo!¶±´ëzñjŒ &’1¤Dˆ§HX‡)Ï™suéa XÛ;×zß& "¶€ AB%0áDFP8*sNÓ5N†AB5X¨0eQçãØÎØÎû­Áøéf/GYúfáÚL#-FïpaÈL‹c97€ÁWZLÁ-$TL NI‡…ÙsZß9ý¿ê'e¾y‰l뛪§vëžJ”Ìâë‘ð –^zÛ¨¤Ç $úw#,ù%ƒ›p]«¢T³[••#,ã–RͰB i4ç­Œ,jB’ „@:î;æ­B Šg¤½ XÉ‚H¨Â!¦Ô¢Ó×N«¹š5@·Iå¥lgºë Ô¬•*RŠˆ¦ x´M¢•Õ¼ÑÛˆX- yãÐÆ;:]$>×*àM#È¢ÆA¾¹ˆç: 5‡evA[ÄP,'ÅãÑ·Ml|÷ë8D¸`ÍöáÓ6é˜Ò‹yŒH<#,¬çXN|d†Ñ–ÓÀª |t‘ÅFFi3m&#$ƒvÛm¥‚˜¹*G¡ª®u2X£2Ò’·Îí,ª4f\±Ö|öÁƒiwÙdNirLG Œ8o(½¤4šSß46@Oo]6!¶A:NÍe;m±«¦—r†F)œÀKm6™ÔÑ8’ÇdÎÒ÷.žõeÖÎbÛLžZË™C¢”¬A.›ûµª& 5÷wʼnŸRøtî$!9¸ðì÷Åõ› %vŠÚÞø©aU»ÆD<™‹&Z]Í”åÕ:N-fS‡ªŽK8˜g2ÒhÀÂç‰SX-iò†”a#-׬J™½å¨XU˼ºq5ÎìNí»§×Ç*ñs&âiÒ©Zã¤êBÄ´a#,YxB˕ʬÎÕE;²!Ø#,g5 I”y*U8²aÒëåεl.ç€úu¼ÁY±¢\ÄT8µ»C5Ht]ÊëŠë‘¡WC“]&!@ø¬E#-5³6É›!Å;ncžMK|º`iRâæu³‚0:ùƒ‘_Z­ê§&ppßB Ü’¸ˆ%‡qÒ}N‰pÔhás‚ðk2ZΉÂL„C”ä“zLõ2ƒZÆ@ÆYj€d™j:Ab,”!Q#$UXmˆ[3\nPÓ[‚dÆ,7È`õ$Ä-Y7’n¤zzo€]šÎ³X’Ø7`i¸äÖ¦8!xÂú`a$L¼¬†zSŠ0æ‡W>ù¦´mWÞ&Íç‚8í›+µžR‡f•RÃ;m“c]ò6E’é’bís- „´\hb‰|¸;èÛ-‡:‹±]™àÌ?Gíûë9›\Ø®G«o߯iaß²¢ì%f©‡8’È`H¢¦¦nhZbÔ üß.°ñ×O"#-e9åg×WM¨}1)ï¹fd\ô˜b˜ú¾\„kQóœ<“'Ùai1Lš¶pÍ´o6X™,áÊ*. lÜpóØ6h~fÍe8ì€tÂÉ“äxk›öo¥Œ#,ÙQ I#-dÞ‰mÔÉfd04UÂX®S®O¡‰LÛ 6Ñ ÃœœË4aðó-ÕÛª•h,ÊDâ„B]iC¶7ç›#,2Î ÌÑ0o àÈ)BI˜¨†%…1ˆÏwmðp׉Û‡&ʱ¹èÂbmªi ±X‹zbÛåS UÌ-9}_ŠíúÃïS”ÈHI™*ˆ¸¡”xx1°Q}‹ì¯é­R*žê2åÉúšGN\dL«3õÜǽ„>è¾ÈpÉË™ôi’ô„Q>#-CHë T¬P°Z†NŽDàÔLbHaÓØî•ï&3ULâk1S´•Ö,ŠÚU",c%vÛÍ3I(ÉzëÞ»­w^÷Ky»´#{+¢¼÷Žlu 1²„¶ÛH2G.)€°ˆ#-b Ø(ƒMY¤#Z4¬“5X¡1àÈí$Ò°©Ç*3Æ-|gº’PY¶1lÓ-M.¸ ŒÌÑÕVf1'q ñyŒŒW6Í«suïäz‹àÉYO"ÚíÁZCê@zà#Í:‚c#ÆDMO¾zÇ:QILÍîaÉ¤Ë ”ʲ#,’Z5® …Æ&\³×2ßbj~ § ¦ö.³5´±#$: B¡9ЂhÓÔ‚˜êÝ¿’ïIT*‡°ïèš#$dcsm#,sRŒïت‘Ý8Å“vÎ#-ð­6P´m­·¦0Û…ä€laoÓŠ2ÙL’C&HÂt î3v¼x`3âeF e]#-½À|×d£ó­v¤±5¯ÐÖݵD”j¥kiHJ´ß©Ô–ɰ°ˆ’*8"Œ"·`Œ¸Ðˆ†e´„˜³6I)‘HK‚0#-8`!Áž¥Ü‚õÀxO~~¼è¤)RL¿æÂªûÐßç•pð5`v0ïÛ3‹ ¤2"E‘d‹#$ë{ú.ùù¾€ëzÔ«✰˜Ìõ£*¬ˆŒ‚©  mÝóˇŽ˜©åÑG-|ï¤q»wPoF‹ÆrÙØ"`\t–}Úüaö€îßÁ[ '¥¼n^I#D©›I©)RÊÌch²š#-µ±k0Û ²"šÊ5_ÎQª®›tÒV®3¢D‘¨T,R‡ƒÄãß<“ÕXªéÈÌ#,»öFÃÅD9SœÕÌt@;Ï #,#,þ®ÎµÏб2ÝÑvà›A$YrgÈ“ìÀ¾<¹‡få-<:â‰Ü‡¼ø]3""(<™ñÏn¨I!Ôzü¸ôEí¾íÞ7=°öÓÞ>spËÁjZ^ †(‰¾~TÛE8{iòl¥-´MPèÆÄ’0¡·Më#,`0Ü´ÒwqAÆàI²’Ë™»2Ú°eà0Y«›#,™„4H:Ñ`Ã¥ð‘ƒp,a@ÈÌ&ó®®tàC·nÆÈm 6´šU&”•5Á©[AFa¯)è•ÀŠ— HM"X>)Æ6Úš#r„#,$¤a-ÀɉBUË(pC= Ò¡u¡±úvwú¢§ÅPø Å€„Eô º™3.â“Ø%˜´¥sv*5 ßçV´0X³«Xpm/ÈfPOÚ—¡XK¤©ëKB͸Æ7‰ •€ìíõ½@Ù,¢õ‡yožh@R#-ïù¢%ÅR‚Èq!2=ð‰ÃË™„ì 9õâø:˜—¼ ± Z“¨7Ò@$`D`C¨bªh ÅS{ÕwKAã ý’…F¼ù]åšÁ..xwãþ„cìIÍUùîhkˆÚÞ©¨óBãßIfÍ}öÐ×[½Žké[Gï T<¸Ò考˜í#-à Λ6X}º‚¬VÆæ@‘$’†LfG‹¨ØŒhÒKºº #,‘¦}æ§»k›G=óyç½Å³†ŠÊ)¤5Qònæ¸äl–6Н;µÍqÉwuâÞy]¥´œÕ¯$±„_Ð#,+£!ÔGõ¤SpDP¸Öøu›ø'wﻪ"0nwLoz31l$A4ùÖ4ª’1CÛÖ+ÕÜcÙLlVM²´Ñ©­–µÝ­ûê[öïË«WÏò¥Œ…E¥#chÒ³%Sf¾ïb£~Wïï°Ò#-4Cf£lUJR–Úüº§h’—żk÷ˆr ¢&l%-n=£‹© ©æ0–Hrd“•"Ä₨† Ê«›"›ÂÖjÅcfF+X©JKe‚‰ïâÊB#- (´ ZxˆPíÐ(šÿÜôh÷ûßOËö—uî$ž#-ø„mƒ’Q“Þž³Ð|÷TÇÕ™S[°×ºá 1>àM"L'‘ êX"y†ù¤ðòè`Úº…¶šä$‹·‘ëôè®^ú*¼íùï{Œ„)(¤íiƒýWBÔw˜ðnBæÊRAu¸QÈ0"É÷@èMý{{n/¶³ÌdH£$DÀ©Öžõj„‚õ#-#,ƒšêù\SZ“W7Fª›jM(ÀFRE“M‚2 ¦#,[m¸˜P£)#,B#$”+Ô¸^T§Ä¥YÛY–^ýLºöéì¥÷ÇÊù¤ueËQ”õ1s+ÓšXq1èvï¡lêÎ%¥#`:÷Ø+44~p‰2(tµ–´Éže#$>©”âÛ‚ûâ÷Ê6•|rH¿ ˆæ‰B¦C…ÂÇÁ~(zgñ«ý  `òWVU#Y(¶)M4©6EE-ŒT`¨ªM¶´o¹¶ýƯ$°ÑZ6ɦPl-º4IãÞtñí³¶Y¤„m‚†¤¸bÄ~(Ü« }!w„EŒ uTH£&z¨¦‰¡»yFÁ`¤9lë|çÙø®ýÜSJ8áÙR#$1X¤Yò¬kQh-JF„¡‹BABF$€û]ç#,/¾Ofd=ç?“<¹ÛJ¬²R@þƒŽøýÌù¡)‡ùfô׃LÔ#,A R]áF6º)¨o‚ëåB{LVÄó½6‹hHµk[á.pK%Û2Ä6(à°²1:šÎú´hn8?áÉ€ÜQD“„´²(]C-«}ë4šš)RÞæÂ»iÝ–»»+ƼÞk]Sh²kz«’¢Þnë3vfUuÍÛQW;PËdYjòîÆš%nî®î¶“eIS"Sc[ÎêÞi¯:»y Tà¥XH`àR"#,µ¨ØÕÓ½o|¸Š©M-“Re•z­uo:ºóζÞ5”e3V[)kÎî[µÝº,±•²¡……$*S“;äÐr&ñ#$ÅQb :sV„o³#,åÁ þŸ&àßбdàY|hÈ["£A2HÇ#$ª@ Sã’¡µËM&RÚ3E,üf1$fÚà¹|³Ï<"æqJAMén/%eÆ”j‰B"ÛXf»íiý1#$"H¸ˆy|·‡Ó˜z1‹t,#-ç·¿›³{™Ãn˜0)R›`‡TkÏq.$-ݯø/ÒZdbLz‘45U*qt¾£:ż5ši¦(Æú¼LÌ=rì0û6òˆ2Eq\¢‘œKÂàß^¤Æñs_º¶jtЬ*›€™eòöm‹á ½4Ó¼Ð?¯eKüDtfÁÛÂþkWÐéö’²îºTžºÞÆ?Z-Œ@òä#-:0–¨Ñ#,qR1åñ êý5X¶gp>qC¬‚Z6ŠTp7Ãm©Ö¶àVCˆ$#-ôŽÅqÒX†Ša>Ñ/Ì÷ñh]i#-þ:'Þ//ß,ÍpcoGí>ÉDâÌ ç7(6ÈÖ‚Õ¤êIl„ÏqA¥µ¢÷#,“3Ü¢n#,XDlºÆR/%©3f9›¹!¹‹¯ÕÝ狵#I¶*1ã\JSnÉœvg ò`o—ׇt4ó…!á]pý¬<Ö—ÑÙÈ#Á:»ºu`—.qµÿOÕÌ6—SCç>¨wJžªXPnnkÁ³l ïèÔ} f–iþN!D±j9 ÷úÜó*–Œ´³ç}­´C¼5HÑ“ø$¹j"+3ÛP'dìWέmðu°uX Òÿ$Xì"î#,%t¢#‹Ä¸ÝÃP¢éÞœ;¿U#-Æ7´Ô#,F![J^"ù6o5Ô7…¡Šw:©ì ½÷¦lYm!ÄáZ5Èæ$$;»±Øy¦#,'3wÔC2×VÁéÒã¢*Ü7ŸS—‘³&§ä=d(è1œ¬>¥÷«Ìì:6ƒØJ„‹”¬-Ù>}"ŠÙÛn‡J§&G™qÜ¢¨ª§&G™™rT.èrÄØ)Û3;Ðü‘Õ†wöîüÌûþÓÍÓläÚŸv#$±š0‚¨WÝ”Ѐ\Š)ù*“O§0,ªÇ ·®€¶ÊD#$k×añýec)ßܯÀú¸†ÙØT¨²¾ÁxÈ•ßÃ&#,‘–#,0®L&Ÿ®È­¢ÏýßþÃ?íÿ—ü¼oÿoû?ìöÕþûóÿ¯ÿ_öû<}ßöü~:øõýÿËÿÿ~ñÿ—üwÿíÿñ§ÿË×ÿŽ‘ÿŸôèÿË/üü¼>oùÇÿ?þŸóæ3ÿåÿ)~¿ùWÇï9>ŸùÿÏæú~Š|ŽÂ>‡oؘ’ #e ™à M†iþ@Žè†A›WóÿLˆ§pàÖ\ûçùd ¡[è8î#I Oò2¹TE%|¯bÝçè¿ÊÏÓÜîê$$ÌÍâê,Té‚ѰÓ#$mŒ„Ä;/EPg#,HAvi´C\y©‘N M#-ïGóÙãÊö#-ÂÀ‰Ú?ÙÇŽôÈtÞP\ÖçÝO@p·@gìS'Â/ö£2ˆÎdwo+€r£Ù €Ù#äaA¹ž6˜ÖxæQì#çéƒuêÿ³A†»µÇ0ð?¶_ôŒ®_@ÏÏ›ðò=íC‡“xžŸ#-Ü#.”®_øÐËJ#-,K’‚,×v!ÿ‡¹ÝÊrIEÚ÷íêÿ÷ë³åœ8ßkLs+u‘ÊÙ™üœ]¥€ojTÔ‘#,ýL{n³Llkvh$qÆ#,”¤C¿‚TÁŒ0ÙÿFˆE2ªw´o[x÷奃OÔ<×=]`8¤k³Uï®ÍÜ4ûÆcXÓC !,÷´èå7ª–4•[XjÛ´pêÓªîiª[q­Œ•AšŠŽ°mp^`ÔÊbíª“ #$8¼¤÷–l,—rA‚XÉ”ìØRÛÍçÕà«2úƒx´S»œè‚#r#uq1¥”o/x²Z1ª¸Rø! Óë/”º™¯Y†\”ƒe#-â)L|ã½I"ÑŒÚåàžøŒ6Ùm!L9ú0ý{ºåêŸå¡¶ÙM^AÈŠ¢ÚkW0ÂÊßRvo  ·ÝÒ80ÝlÆŠw€á†= ãëê~£÷ÃH/×-ê·Ê×CëvØ’ÊÉÿàªú ¿è{^ûî†BBÀCÄý»Fyÿ´ï‡~+Û7«á…¬æúÃ$tÛ%Œáÿè¿¶†ÙôŸkÛã#$½aÑŽE݃íi;Ú›ìPwÆ€·ÊK]à zG<âÉaÅêSYKˆ€üü>~c'ÑÊ)—‡±4HU4xfú<(#,³QÁa>š,5>‹…§9lÄ•º/Ae¯··ÀË––“|5».WD4[æ¶ú۲Ƽj5+~&¹¨´}nmcŶõF¯†Õ½š-Rh´WË+\µ_> ,jÐn»:+CÊØx=&Ƽˆ&±DÈs"}W ÚàÿÈ˧@N™"˜#'@¢=Îãfpë0dxŽf€aHèW‡N‚ãc£r¹]pX±ú*J:…¬H|ã=ë¿„EÕyPm$Y#$‘œ5JÿÝ<|¾æQߣÔu|‘|áq?±€W%HüµäQ#$#,Îäwú£D” Qem¦[S3]-º5We«ìX›5%½ß{U¯Û‘Fѵ)µo“#,¿«³»ËQÊìù/3i˜Bf#,<¨9Vþ Îî¥=;LËãe§ðzÎ `„‡éÅgû)îå9FÔÃðlH°ý=ß¾ƒøÿí$ðáú\ B(H¢2)"ï ùŽD¸èiã9Ÿÿ½Ç£˜Ù*¾áßIņŠÈÌ>TÑŒhgŸßl›C)”Ÿ†Gt??ï¥Ò'ä—Žº3pQkB¬a –Ó$饟/Üqÿõ»l|*³t ¢ãÆæ¦ÿÿžÒœ9p”¼ð‚^ýb!‚uŒFÊ?J?ùÑk8+·‰ÅQÎ ¿#,W Œ³˜50ï‘Ä>?§NÚ gA••åÎX›Â7 ^[ c~¢qQ-y[Na ûþýE?¾Ÿ«üöØtúnŒ˜î;Î]"Å·K*õ|.¼rX¥ë%"á…aÏŽ$Š4x±¨¯y¨nZމöïy#-ÁÕé#-q¡ã;'´ÓÝ-UŒEúBùqïGν\8§xãã—ü óok`3x‚Ä ä,+÷}?XÌ3ÿÅÜ‘N$!‰(`À #<== From 649f32a18587247c2764d4179201859c9b9e3140 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 8 Oct 2018 22:38:59 +0300 Subject: [PATCH 044/205] wscript: fix install --- mainui | 2 +- wscript | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mainui b/mainui index c8f22ee6..f6ed54a4 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit c8f22ee638d6009db1998e72956ddae26fa39724 +Subproject commit f6ed54a4b35f5aa6477f739a7cad78b4e897a747 diff --git a/wscript b/wscript index be1d8dc5..97700378 100644 --- a/wscript +++ b/wscript @@ -123,9 +123,8 @@ def configure(conf): conf.env.DEST_OS != 'darwin'): conf.env.LIBDIR = conf.env.BINDIR = '${PREFIX}/lib/xash3d' else: - # prefix is ignored - conf.env.LIBDIR = conf.env.BINDIR = '/' - + conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX + conf.start_msg('Checking git hash') git_version = get_git_version() conf.end_msg(git_version) From abda3b52ab2298d4dc23b4478cf66c7c3bc5ccb0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 00:41:18 +0300 Subject: [PATCH 045/205] game_launch: remove two different names for engine DLLs --- game_launch/game.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 2cc788a0..5ea88592 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -31,11 +31,7 @@ GNU General Public License for more details. #if !__MINGW32__ && _MSC_VER >= 1200 #define USE_WINMAIN #endif - #ifndef XASH_DEDICATED - #define XASHLIB "xash_sdl.dll" - #else - #define XASHLIB "xash_dedicated.dll" - #endif + #define XASHLIB "xash.dll" #define dlerror() GetStringLastError() #include #endif From 9e618ce3e17abf3bf5a629069a78870149fdd224 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 00:42:06 +0300 Subject: [PATCH 046/205] cmd: base_cmd: fix inconsistency between linked list and hash map --- engine/common/cmd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 2bed4ae0..3169d438 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1112,6 +1112,10 @@ void Cmd_Unlink( int group ) continue; } +#if defined(XASH_HASHED_VARS) + BaseCmd_Remove( HM_CMD, cmd->name ); +#endif + *prev = cmd->next; if( cmd->name ) Mem_Free( cmd->name ); From 844b3a39a7d2b77d9ba7c73f8100f9798e67b7e0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 20:01:54 +0300 Subject: [PATCH 047/205] vid_sdl: fix GL_UpdateContext, fix window created outside screen if positions were negative --- engine/platform/sdl/vid_sdl.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index ffc1b9f2..50209717 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -461,9 +461,9 @@ GL_UpdateContext */ qboolean GL_UpdateContext( void ) { - if( !SDL_GL_MakeCurrent( host.hWnd, glw_state.context )) + if( SDL_GL_MakeCurrent( host.hWnd, glw_state.context )) { - MsgDev(D_ERROR, "GL_UpdateContext: %s", SDL_GetError()); + MsgDev(D_ERROR, "GL_UpdateContext: %s\n", SDL_GetError()); return GL_DeleteContext(); } @@ -566,6 +566,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) rgbdata_t *icon = NULL; qboolean iconLoaded = false; char iconpath[MAX_STRING]; + int xpos, ypos; if( vid_highdpi->value ) wndFlags |= SDL_WINDOW_ALLOW_HIGHDPI; Q_strncpy( wndname, GI->title, sizeof( wndname )); @@ -573,17 +574,16 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !fullscreen ) { wndFlags |= SDL_WINDOW_RESIZABLE; - host.hWnd = SDL_CreateWindow( wndname, - Cvar_VariableInteger( "_window_xpos" ), - Cvar_VariableInteger( "_window_ypos" ), - width, height, wndFlags ); + xpos = max( 0, Cvar_VariableInteger( "_window_xpos" ) ); + ypos = max( 0, Cvar_VariableInteger( "_window_ypos" ) ); } else { wndFlags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_GRABBED; - host.hWnd = SDL_CreateWindow( wndname, 0, 0, width, height, wndFlags ); + xpos = ypos = 0; } + host.hWnd = SDL_CreateWindow( wndname, xpos, ypos, width, height, wndFlags ); if( !host.hWnd ) { @@ -669,17 +669,13 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !glw_state.initialized ) { if( !GL_CreateContext( )) - { return false; - } VID_StartupGamma(); } - else - { - if( !GL_UpdateContext( )) - return false; - } + + if( !GL_UpdateContext( )) + return false; SDL_GL_GetDrawableSize( host.hWnd, &width, &height ); R_ChangeDisplaySettingsFast( width, height ); From 4ced29ee8f0bdf9e5ad8fb4b0c574d471f9a6d31 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 20:22:11 +0300 Subject: [PATCH 048/205] vid_common: fix window_center_x/y initialization --- engine/client/vid_common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 866b987e..28432372 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -234,12 +234,12 @@ R_SaveVideoMode */ void R_SaveVideoMode( int w, int h ) { - host.window_center_x = glState.width / 2; - host.window_center_y = glState.height / 2; - glState.width = w; glState.height = h; + host.window_center_x = w / 2; + host.window_center_y = h / 2; + Cvar_SetValue( "width", w ); Cvar_SetValue( "height", h ); From 216b4f414764e57f7eee36561c45f0e6ed6550ef Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 23:36:34 +0300 Subject: [PATCH 049/205] vid_sdl: fix incorrect video modes list length --- engine/platform/sdl/vid_sdl.c | 3 ++- engine/platform/win32/win_con.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 50209717..ca335f6b 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -208,7 +208,7 @@ vidmode_t R_GetVideoMode( int num ) { static vidmode_t error = { NULL }; - if( !vidmodes || num < 0 || num > R_MaxVideoModes() ) + if( !vidmodes || num < 0 || num >= R_MaxVideoModes() ) { error.width = glState.width; error.height = glState.height; @@ -223,6 +223,7 @@ static void R_InitVideoModes( void ) int displayIndex = 0; // TODO: handle multiple displays somehow int i, modes; + num_vidmodes = 0; modes = SDL_GetNumDisplayModes( displayIndex ); if( !modes ) diff --git a/engine/platform/win32/win_con.c b/engine/platform/win32/win_con.c index 8725a720..087c7e5b 100644 --- a/engine/platform/win32/win_con.c +++ b/engine/platform/win32/win_con.c @@ -356,7 +356,7 @@ void Wcon_CreateConsole( void ) { s_wcd.SysInputLineWndProc = (WNDPROC)SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, (long)Wcon_InputLineProc ); SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM )s_wcd.hfBufferFont, 0 ); - } + } // show console if needed if( host.con_showalways ) @@ -370,7 +370,7 @@ void Wcon_CreateConsole( void ) SetFocus( s_wcd.hWnd ); else SetFocus( s_wcd.hwndInputLine ); s_wcd.status = true; - } + } else s_wcd.status = false; } From e50d22bce70551333f20c3342866efdb8d249235 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 23:38:58 +0300 Subject: [PATCH 050/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index f6ed54a4..5c5010fd 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit f6ed54a4b35f5aa6477f739a7cad78b4e897a747 +Subproject commit 5c5010fd42e4490a3a679ac2e4c1b0b8b5741344 From bff9ca8438291be9deb5de4e76941c61d428d823 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Oct 2018 23:43:03 +0300 Subject: [PATCH 051/205] console: fix double printed console messages on Win32 --- engine/common/system.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/engine/common/system.c b/engine/common/system.c index 4ca7fa51..3b02986a 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -670,7 +670,9 @@ void Sys_Print( const char *pMsg ) { #ifndef XASH_DEDICATED if( !Host_IsDedicated() ) + { Con_Print( pMsg ); + } #endif #ifdef _WIN32 @@ -682,11 +684,6 @@ void Sys_Print( const char *pMsg ) char *c = logbuf; int i = 0; -#ifndef XASH_DEDICATED - if( !Host_IsDedicated() ) - Con_Print( pMsg ); -#endif - // if the message is REALLY long, use just the last portion of it if( Q_strlen( pMsg ) > sizeof( buffer ) - 1 ) msg = pMsg + Q_strlen( pMsg ) - sizeof( buffer ) + 1; From da094fa04ed2915f243e65ad1735f2302858fc7c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 11 Oct 2018 00:04:23 +0300 Subject: [PATCH 052/205] input: provide a common function for collecting input from different sources --- engine/client/input.c | 118 ++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 67 deletions(-) diff --git a/engine/client/input.c b/engine/client/input.c index d08977ae..4795a7ac 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -493,6 +493,47 @@ void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove ) } } +void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qboolean includeSdlMouse ) +{ + if( !m_ignore->value ) + { +#if XASH_INPUT == INPUT_SDL + if( includeSdlMouse ) + { + int x, y; + SDL_GetMouseState( &x, &y ); + *pitch += y * m_pitch->value; + *yaw -= x * m_yaw->value; + } +#endif // INPUT_SDL + +#ifdef __ANDROID__ + { + float x, y; + Android_MouseMove( &x, &y ); + *pitch += y * m_pitch->value; + *yaw -= x * m_yaw->value; + } +#endif // ANDROID + } + + Joy_FinalizeMove( forward, side, yaw, pitch ); + IN_TouchMove( forward, side, yaw, pitch ); + +#ifdef USE_EVDEV + IN_EvdevMove( yaw, pitch ); +#endif + + if( look_filter->value ) + { + *pitch = ( inputstate.lastpitch + *pitch ) / 2; + *yaw = ( inputstate.lastyaw + *yaw ) / 2; + inputstate.lastpitch = *pitch; + inputstate.lastyaw = *yaw; + } + +} + /* ================ IN_EngineAppendMove @@ -502,7 +543,7 @@ Called from cl_main.c after generating command in client */ void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active ) { - float forward, side, dpitch, dyaw; + float forward, side, pitch, yaw; if( clgame.dllFuncs.pfnLookEvent ) return; @@ -510,45 +551,18 @@ void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active ) if( cls.key_dest != key_game || cl.paused || cl.intermission ) return; - forward = side = dpitch = dyaw = 0; + forward = side = pitch = yaw = 0; - if(active) + if( active ) { float sensitivity = ( (float)RI.fov_x / (float)90.0f ); -#if XASH_INPUT == INPUT_SDL - if( m_enginemouse->value && !m_ignore->value ) - { - int mouse_x, mouse_y; - SDL_GetRelativeMouseState( &mouse_x, &mouse_y ); - RI.viewangles[PITCH] += mouse_y * m_pitch->value * sensitivity; - RI.viewangles[YAW] -= mouse_x * m_yaw->value * sensitivity; - } -#endif -#ifdef __ANDROID__ - if( !m_ignore->value ) - { - float mouse_x, mouse_y; - Android_MouseMove( &mouse_x, &mouse_y ); - RI.viewangles[PITCH] += mouse_y * m_pitch->value * sensitivity; - RI.viewangles[YAW] -= mouse_x * m_yaw->value * sensitivity; - } -#endif - Joy_FinalizeMove( &forward, &side, &dyaw, &dpitch ); - IN_TouchMove( &forward, &side, &dyaw, &dpitch ); - IN_JoyAppendMove( cmd, forward, side ); -#ifdef USE_EVDEV - IN_EvdevMove( &dyaw, &dpitch ); -#endif - if( look_filter->value ) - { - dpitch = ( inputstate.lastpitch + dpitch ) / 2; - dyaw = ( inputstate.lastyaw + dyaw ) / 2; - inputstate.lastpitch = dpitch; - inputstate.lastyaw = dyaw; - } - RI.viewangles[YAW] += dyaw * sensitivity; - RI.viewangles[PITCH] += dpitch * sensitivity; + IN_CollectInput( &forward, &side, &yaw, &pitch, m_enginemouse->value ); + + IN_JoyAppendMove( cmd, forward, side ); + + RI.viewangles[YAW] += yaw * sensitivity; + RI.viewangles[PITCH] += pitch * sensitivity; RI.viewangles[PITCH] = bound( -90, RI.viewangles[PITCH], 90 ); } } @@ -573,37 +587,7 @@ void Host_InputFrame( void ) if( clgame.dllFuncs.pfnLookEvent ) { - int dx, dy; - -#ifndef __ANDROID__ - if( in_mouseinitialized && !m_ignore->value ) - { - SDL_GetRelativeMouseState( &dx, &dy ); - pitch += dy * m_pitch->value, yaw -= dx * m_yaw->value; //mouse speed - } -#endif - -#ifdef __ANDROID__ - if( !m_ignore->value ) - { - float mouse_x, mouse_y; - Android_MouseMove( &mouse_x, &mouse_y ); - pitch += mouse_y * m_pitch->value, yaw -= mouse_x * m_yaw->value; //mouse speed - } -#endif - - Joy_FinalizeMove( &forward, &side, &yaw, &pitch ); - IN_TouchMove( &forward, &side, &yaw, &pitch ); -#ifdef USE_EVDEV - IN_EvdevMove( &yaw, &pitch ); -#endif - if( look_filter->value ) - { - pitch = ( inputstate.lastpitch + pitch ) / 2; - yaw = ( inputstate.lastyaw + yaw ) / 2; - inputstate.lastpitch = pitch; - inputstate.lastyaw = yaw; - } + IN_CollectInput( &forward, &side, &yaw, &pitch, in_mouseinitialized ); if( cls.key_dest == key_game ) { From 5af7a19b408e295279e35b0c20032918266a7e3a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Oct 2018 14:17:36 +0300 Subject: [PATCH 053/205] Rename contib to contrib --- {contib => contrib}/a1batross/xash3d.config | 0 {contib => contrib}/a1batross/xash3d.creator | 0 {contib => contrib}/a1batross/xash3d.creator.user | 0 {contib => contrib}/a1batross/xash3d.files | 0 {contib => contrib}/a1batross/xash3d.includes | 0 {contib => contrib}/mittorn/Makefile.linux | 0 {contib => contrib}/mittorn/README.md | 0 {contib => contrib}/mittorn/setup.sh | 0 {contib => contrib}/mittorn/wscript | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {contib => contrib}/a1batross/xash3d.config (100%) rename {contib => contrib}/a1batross/xash3d.creator (100%) rename {contib => contrib}/a1batross/xash3d.creator.user (100%) rename {contib => contrib}/a1batross/xash3d.files (100%) rename {contib => contrib}/a1batross/xash3d.includes (100%) rename {contib => contrib}/mittorn/Makefile.linux (100%) rename {contib => contrib}/mittorn/README.md (100%) rename {contib => contrib}/mittorn/setup.sh (100%) rename {contib => contrib}/mittorn/wscript (100%) diff --git a/contib/a1batross/xash3d.config b/contrib/a1batross/xash3d.config similarity index 100% rename from contib/a1batross/xash3d.config rename to contrib/a1batross/xash3d.config diff --git a/contib/a1batross/xash3d.creator b/contrib/a1batross/xash3d.creator similarity index 100% rename from contib/a1batross/xash3d.creator rename to contrib/a1batross/xash3d.creator diff --git a/contib/a1batross/xash3d.creator.user b/contrib/a1batross/xash3d.creator.user similarity index 100% rename from contib/a1batross/xash3d.creator.user rename to contrib/a1batross/xash3d.creator.user diff --git a/contib/a1batross/xash3d.files b/contrib/a1batross/xash3d.files similarity index 100% rename from contib/a1batross/xash3d.files rename to contrib/a1batross/xash3d.files diff --git a/contib/a1batross/xash3d.includes b/contrib/a1batross/xash3d.includes similarity index 100% rename from contib/a1batross/xash3d.includes rename to contrib/a1batross/xash3d.includes diff --git a/contib/mittorn/Makefile.linux b/contrib/mittorn/Makefile.linux similarity index 100% rename from contib/mittorn/Makefile.linux rename to contrib/mittorn/Makefile.linux diff --git a/contib/mittorn/README.md b/contrib/mittorn/README.md similarity index 100% rename from contib/mittorn/README.md rename to contrib/mittorn/README.md diff --git a/contib/mittorn/setup.sh b/contrib/mittorn/setup.sh similarity index 100% rename from contib/mittorn/setup.sh rename to contrib/mittorn/setup.sh diff --git a/contib/mittorn/wscript b/contrib/mittorn/wscript similarity index 100% rename from contib/mittorn/wscript rename to contrib/mittorn/wscript From 880d3de53a4d43f95eba584ff9a6db2941b99318 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Oct 2018 22:04:24 +0300 Subject: [PATCH 054/205] input: move evdev under m_ignore --- engine/client/input.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/client/input.c b/engine/client/input.c index 4795a7ac..2ef4b990 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -155,7 +155,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) SDL_WarpMouseInWindow( host.hWnd, host.window_center_x, host.window_center_y ); SDL_SetWindowGrab( host.hWnd, SDL_TRUE ); if( clgame.dllFuncs.pfnLookEvent ) - SDL_SetRelativeMouseMode( SDL_TRUE ); + SDL_SetRelativeMouseMode( SDL_FALSE ); } #endif // XASH_SDL if( cls.initialized ) @@ -515,15 +515,15 @@ void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qbo *yaw -= x * m_yaw->value; } #endif // ANDROID + +#ifdef USE_EVDEV + IN_EvdevMove( yaw, pitch ); +#endif } Joy_FinalizeMove( forward, side, yaw, pitch ); IN_TouchMove( forward, side, yaw, pitch ); -#ifdef USE_EVDEV - IN_EvdevMove( yaw, pitch ); -#endif - if( look_filter->value ) { *pitch = ( inputstate.lastpitch + *pitch ) / 2; From 23a7dce3bed47253e21fc44dfb51437b6af534e0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Oct 2018 22:06:17 +0300 Subject: [PATCH 055/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 5c5010fd..dac66977 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 5c5010fd42e4490a3a679ac2e4c1b0b8b5741344 +Subproject commit dac66977620487260c7351c2a2c845f081a5dc71 From 6ba7781a613bb95e19fd57bf55b892e71d428034 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Oct 2018 22:13:51 +0300 Subject: [PATCH 056/205] input: various fixes: fix mistyped pitch/yaw, replace SDL_GetMouseState by SDL_GetRelativeMouseState, fix disabling mouse with -nomouse argument --- engine/client/input.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/client/input.c b/engine/client/input.c index 2ef4b990..03a5dfd9 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -155,7 +155,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) SDL_WarpMouseInWindow( host.hWnd, host.window_center_x, host.window_center_y ); SDL_SetWindowGrab( host.hWnd, SDL_TRUE ); if( clgame.dllFuncs.pfnLookEvent ) - SDL_SetRelativeMouseMode( SDL_FALSE ); + SDL_SetRelativeMouseMode( SDL_TRUE ); } #endif // XASH_SDL if( cls.initialized ) @@ -493,15 +493,15 @@ void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove ) } } -void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qboolean includeSdlMouse ) +void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qboolean includeMouse, qboolean includeSdlMouse ) { - if( !m_ignore->value ) + if( !m_ignore->value || includeMouse ) { #if XASH_INPUT == INPUT_SDL if( includeSdlMouse ) { int x, y; - SDL_GetMouseState( &x, &y ); + SDL_GetRelativeMouseState( &x, &y ); *pitch += y * m_pitch->value; *yaw -= x * m_yaw->value; } @@ -557,7 +557,7 @@ void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active ) { float sensitivity = ( (float)RI.fov_x / (float)90.0f ); - IN_CollectInput( &forward, &side, &yaw, &pitch, m_enginemouse->value ); + IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized, m_enginemouse->value ); IN_JoyAppendMove( cmd, forward, side ); @@ -587,7 +587,7 @@ void Host_InputFrame( void ) if( clgame.dllFuncs.pfnLookEvent ) { - IN_CollectInput( &forward, &side, &yaw, &pitch, in_mouseinitialized ); + IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized, true ); if( cls.key_dest == key_game ) { From 25f07ddb97552cc7576c2cb7354770eb894e5904 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Oct 2018 23:52:14 +0300 Subject: [PATCH 057/205] platform: introduce common header for platform-dependent functions. To keep clean code and engine platform-agnostic, now including headers from platform folder, except this one, is strictly prohibited. --- engine/platform/platform.h | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 engine/platform/platform.h diff --git a/engine/platform/platform.h b/engine/platform/platform.h new file mode 100644 index 00000000..dd81ca8a --- /dev/null +++ b/engine/platform/platform.h @@ -0,0 +1,71 @@ +/* +platform.h - common platform-dependent function defines +Copyright (C) 2018 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#ifndef PLATFORM_H +#define PLATFORM_H + +/* +============================================================================== + + MOBILE API + +============================================================================== +*/ +void Platform_Vibrate( float life, char flags ); + +/* +============================================================================== + + INPUT + +============================================================================== +*/ +// Gamepad support +int Platform_JoyInit( int numjoy ); // returns number of connected gamepads, negative if error +// Text input +void Platform_EnableTextInput( qboolean enable ); +// System events +void Platform_RunEvents( void ); + +/* +============================================================================== + + WINDOW MANAGEMENT + +============================================================================== +*/ +typedef enum +{ + rserr_ok, + rserr_invalid_fullscreen, + rserr_invalid_mode, + rserr_unknown +} rserr_t; + +typedef struct vidmode_s vidmode_t; + +// Window +qboolean R_Init_Video( void ); +void R_Free_Video( void ); +qboolean VID_SetMode( void ); +rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ); +int R_MaxVideoModes(); +vidmode_t*R_GetVideoMode( int num ); +void* GL_GetProcAddress( const char *name ); // RenderAPI requirement + + + +#endif // PLATFORM_H From a5258bea650b8d817005cbedc33b55c4b41dd1bb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:13:56 +0300 Subject: [PATCH 058/205] library: move win32 definitions to win_lib.c --- engine/common/library.h | 122 +------------------------------- engine/platform/win32/win_lib.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 121 deletions(-) diff --git a/engine/common/library.h b/engine/common/library.h index b328efb7..9c2ec15c 100644 --- a/engine/common/library.h +++ b/engine/common/library.h @@ -16,128 +16,8 @@ GNU General Public License for more details. #ifndef LIBRARY_H #define LIBRARY_H -#define DOS_SIGNATURE 0x5A4D // MZ -#define NT_SIGNATURE 0x00004550 // PE00 -#define NUMBER_OF_DIRECTORY_ENTRIES 16 #define MAX_LIBRARY_EXPORTS 4096 -#ifndef IMAGE_SIZEOF_BASE_RELOCATION -#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION )) -#endif - -typedef struct -{ - // dos .exe header - word e_magic; // magic number - word e_cblp; // bytes on last page of file - word e_cp; // pages in file - word e_crlc; // relocations - word e_cparhdr; // size of header in paragraphs - word e_minalloc; // minimum extra paragraphs needed - word e_maxalloc; // maximum extra paragraphs needed - word e_ss; // initial (relative) SS value - word e_sp; // initial SP value - word e_csum; // checksum - word e_ip; // initial IP value - word e_cs; // initial (relative) CS value - word e_lfarlc; // file address of relocation table - word e_ovno; // overlay number - word e_res[4]; // reserved words - word e_oemid; // OEM identifier (for e_oeminfo) - word e_oeminfo; // OEM information; e_oemid specific - word e_res2[10]; // reserved words - long e_lfanew; // file address of new exe header -} DOS_HEADER; - -typedef struct -{ - // win .exe header - word Machine; - word NumberOfSections; - dword TimeDateStamp; - dword PointerToSymbolTable; - dword NumberOfSymbols; - word SizeOfOptionalHeader; - word Characteristics; -} PE_HEADER; - -typedef struct -{ - byte Name[8]; // dos name length - - union - { - dword PhysicalAddress; - dword VirtualSize; - } Misc; - - dword VirtualAddress; - dword SizeOfRawData; - dword PointerToRawData; - dword PointerToRelocations; - dword PointerToLinenumbers; - word NumberOfRelocations; - word NumberOfLinenumbers; - dword Characteristics; -} SECTION_HEADER; - -typedef struct -{ - dword VirtualAddress; - dword Size; -} DATA_DIRECTORY; - -typedef struct -{ - word Magic; - byte MajorLinkerVersion; - byte MinorLinkerVersion; - dword SizeOfCode; - dword SizeOfInitializedData; - dword SizeOfUninitializedData; - dword AddressOfEntryPoint; - dword BaseOfCode; - dword BaseOfData; - dword ImageBase; - dword SectionAlignment; - dword FileAlignment; - word MajorOperatingSystemVersion; - word MinorOperatingSystemVersion; - word MajorImageVersion; - word MinorImageVersion; - word MajorSubsystemVersion; - word MinorSubsystemVersion; - dword Win32VersionValue; - dword SizeOfImage; - dword SizeOfHeaders; - dword CheckSum; - word Subsystem; - word DllCharacteristics; - dword SizeOfStackReserve; - dword SizeOfStackCommit; - dword SizeOfHeapReserve; - dword SizeOfHeapCommit; - dword LoaderFlags; - dword NumberOfRvaAndSizes; - - DATA_DIRECTORY DataDirectory[NUMBER_OF_DIRECTORY_ENTRIES]; -} OPTIONAL_HEADER; - -typedef struct -{ - dword Characteristics; - dword TimeDateStamp; - word MajorVersion; - word MinorVersion; - dword Name; - dword Base; - dword NumberOfFunctions; - dword NumberOfNames; - dword AddressOfFunctions; // RVA from base of image - dword AddressOfNames; // RVA from base of image - dword AddressOfNameOrdinals; // RVA from base of image -} EXPORT_DIRECTORY; - typedef struct dll_user_s { void *hInstance; // instance handle @@ -146,7 +26,7 @@ typedef struct dll_user_s char dllName[32]; // for debug messages string fullPath, shortPath; // actual dll paths - // ordinals stuff + // ordinals stuff, valid only on Win32 word *ordinals; dword *funcs; char *names[MAX_LIBRARY_EXPORTS]; // max 4096 exports supported diff --git a/engine/platform/win32/win_lib.c b/engine/platform/win32/win_lib.c index 5d2fe289..c8705ce6 100644 --- a/engine/platform/win32/win_lib.c +++ b/engine/platform/win32/win_lib.c @@ -94,6 +94,106 @@ const char *COM_NameForFunction( void *hInstance, void *function ) --------------------------------------------------------------- */ +#define DOS_SIGNATURE 0x5A4D // MZ +#define NT_SIGNATURE 0x00004550 // PE00 +#define NUMBER_OF_DIRECTORY_ENTRIES 16 +#ifndef IMAGE_SIZEOF_BASE_RELOCATION +#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION )) +#endif + +typedef struct +{ + // dos .exe header + word e_magic; // magic number + word e_cblp; // bytes on last page of file + word e_cp; // pages in file + word e_crlc; // relocations + word e_cparhdr; // size of header in paragraphs + word e_minalloc; // minimum extra paragraphs needed + word e_maxalloc; // maximum extra paragraphs needed + word e_ss; // initial (relative) SS value + word e_sp; // initial SP value + word e_csum; // checksum + word e_ip; // initial IP value + word e_cs; // initial (relative) CS value + word e_lfarlc; // file address of relocation table + word e_ovno; // overlay number + word e_res[4]; // reserved words + word e_oemid; // OEM identifier (for e_oeminfo) + word e_oeminfo; // OEM information; e_oemid specific + word e_res2[10]; // reserved words + long e_lfanew; // file address of new exe header +} DOS_HEADER; + +typedef struct +{ + // win .exe header + word Machine; + word NumberOfSections; + dword TimeDateStamp; + dword PointerToSymbolTable; + dword NumberOfSymbols; + word SizeOfOptionalHeader; + word Characteristics; +} PE_HEADER; + +typedef struct +{ + dword VirtualAddress; + dword Size; +} DATA_DIRECTORY; + +typedef struct +{ + word Magic; + byte MajorLinkerVersion; + byte MinorLinkerVersion; + dword SizeOfCode; + dword SizeOfInitializedData; + dword SizeOfUninitializedData; + dword AddressOfEntryPoint; + dword BaseOfCode; + dword BaseOfData; + dword ImageBase; + dword SectionAlignment; + dword FileAlignment; + word MajorOperatingSystemVersion; + word MinorOperatingSystemVersion; + word MajorImageVersion; + word MinorImageVersion; + word MajorSubsystemVersion; + word MinorSubsystemVersion; + dword Win32VersionValue; + dword SizeOfImage; + dword SizeOfHeaders; + dword CheckSum; + word Subsystem; + word DllCharacteristics; + dword SizeOfStackReserve; + dword SizeOfStackCommit; + dword SizeOfHeapReserve; + dword SizeOfHeapCommit; + dword LoaderFlags; + dword NumberOfRvaAndSizes; + + DATA_DIRECTORY DataDirectory[NUMBER_OF_DIRECTORY_ENTRIES]; +} OPTIONAL_HEADER; + +typedef struct +{ + dword Characteristics; + dword TimeDateStamp; + word MajorVersion; + word MinorVersion; + dword Name; + dword Base; + dword NumberOfFunctions; + dword NumberOfNames; + dword AddressOfFunctions; // RVA from base of image + dword AddressOfNames; // RVA from base of image + dword AddressOfNameOrdinals; // RVA from base of image +} EXPORT_DIRECTORY; + typedef struct { PIMAGE_NT_HEADERS headers; @@ -103,6 +203,26 @@ typedef struct int initialized; } MEMORYMODULE, *PMEMORYMODULE; +typedef struct +{ + byte Name[8]; // dos name length + + union + { + dword PhysicalAddress; + dword VirtualSize; + } Misc; + + dword VirtualAddress; + dword SizeOfRawData; + dword PointerToRawData; + dword PointerToRelocations; + dword PointerToLinenumbers; + word NumberOfRelocations; + word NumberOfLinenumbers; + dword Characteristics; +} SECTION_HEADER; + // Protection flags for memory pages (Executable, Readable, Writeable) static int ProtectionFlags[2][2][2] = { From 265f79fc7262b24fb2cd0bd72ce552c4e9d89a09 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:21:05 +0300 Subject: [PATCH 059/205] platform: add GetNativeObject call --- engine/platform/platform.h | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/platform/platform.h b/engine/platform/platform.h index dd81ca8a..d0cdff30 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -25,6 +25,7 @@ GNU General Public License for more details. ============================================================================== */ void Platform_Vibrate( float life, char flags ); +void*Platform_GetNativeObject( const char *name ); /* ============================================================================== From 960e46c564a3ff045e4c86fe5f82cb2b3c7aab4f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:27:52 +0300 Subject: [PATCH 060/205] engine: adapt engine code to new platform backends system --- engine/client/cl_mobile.c | 16 +++------------- engine/client/gl_rmain.c | 1 + engine/client/keys.c | 18 +++--------------- engine/client/vid_common.c | 25 +++++++++++++++++-------- engine/client/vid_common.h | 27 --------------------------- engine/common/host_state.c | 11 ++--------- 6 files changed, 26 insertions(+), 72 deletions(-) diff --git a/engine/client/cl_mobile.c b/engine/client/cl_mobile.c index c58598a5..d94eac2e 100644 --- a/engine/client/cl_mobile.c +++ b/engine/client/cl_mobile.c @@ -21,10 +21,7 @@ GNU General Public License for more details. #include "library.h" #include "gl_local.h" #include "input.h" - -#if defined(__ANDROID__) -#include "platform/android/android-main.h" -#endif +#include "platform/platform.h" mobile_engfuncs_t *gMobileEngfuncs; @@ -45,9 +42,7 @@ static void pfnVibrate( float life, char flags ) //MsgDev( D_NOTE, "Vibrate: %f %d\n", life, flags ); // here goes platform-specific backends -#ifdef __ANDROID__ - Android_Vibrate( life * vibration_length->value, flags ); -#endif + Platform_Vibrate( life * vibration_length->value, flags ); } static void Vibrate_f() @@ -97,13 +92,8 @@ static void *pfnGetNativeObject( const char *obj ) if( !obj ) return NULL; - // Backend should handle NULL // Backend should consider that obj is case-sensitive -#ifdef __ANDROID__ - return Android_GetNativeObject( obj ); -#else - return NULL; -#endif + return Platform_GetNativeObject( obj ); } static mobile_engfuncs_t gpMobileEngfuncs = diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index e21d591e..585f82a2 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -21,6 +21,7 @@ GNU General Public License for more details. #include "beamdef.h" #include "particledef.h" #include "entity_types.h" +#include "platform/platform.h" #define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) diff --git a/engine/client/keys.c b/engine/client/keys.c index 093ce4c8..ed421a4f 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -17,9 +17,7 @@ GNU General Public License for more details. #include "input.h" #include "client.h" #include "vgui_draw.h" -#ifdef XASH_SDL -#include "platform/sdl/events.h" -#endif // XASH_SDL +#include "platform/platform.h" typedef struct { @@ -702,20 +700,10 @@ Key_EnableTextInput */ void Key_EnableTextInput( qboolean enable, qboolean force ) { - void (*pfnEnableTextInput)( qboolean enable ); - -#if XASH_INPUT == INPUT_SDL - pfnEnableTextInput = SDLash_EnableTextInput; -#elif XASH_INPUT == INPUT_ANDROID - pfnEnableTextInput = Android_EnableTextInput; -#else -#error "Here must be a text input for your platform" - return; -#endif if( enable && ( !host.textmode || force ) ) - pfnEnableTextInput( true ); + Platform_EnableTextInput( true ); else if( !enable ) - pfnEnableTextInput( false ); + Platform_EnableTextInput( false ); if( !force ) host.textmode = enable; diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 28432372..c390429d 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -19,6 +19,7 @@ GNU General Public License for more details. #include "mod_local.h" #include "input.h" #include "vid_common.h" +#include "platform/platform.h" #define WINDOW_NAME XASH_ENGINE_NAME " Window" // Half-Life @@ -256,10 +257,14 @@ VID_GetModeString */ const char *VID_GetModeString( int vid_mode ) { + vidmode_t *vidmode; if( vid_mode < 0 || vid_mode > R_MaxVideoModes() ) return NULL; - return R_GetVideoMode( vid_mode ).desc; + if( !( vidmode = R_GetVideoMode( vid_mode ) ) ) + return NULL; + + return vidmode->desc; } /* @@ -390,12 +395,17 @@ static void VID_Mode_f( void ) { case 2: { - vidmode_t vidmode; + vidmode_t *vidmode; vidmode = R_GetVideoMode( Q_atoi( Cmd_Argv( 1 )) ); + if( !vidmode ) + { + Con_Print( S_ERROR "unable to set mode, backend returned null" ); + return; + } - w = vidmode.width; - h = vidmode.height; + w = vidmode->width; + h = vidmode->height; break; } case 3: @@ -553,10 +563,10 @@ qboolean R_Init( void ) GL_SetDefaultState(); // create the window and set up the context - if( !R_Init_OpenGL( )) + if( !R_Init_Video( )) { GL_RemoveCommands(); - R_Free_OpenGL(); + R_Free_Video(); Sys_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); return false; @@ -565,7 +575,6 @@ qboolean R_Init( void ) host.renderinfo_changed = false; r_temppool = Mem_AllocPool( "Render Zone" ); - GL_InitExtensions(); GL_SetDefaults(); R_InitImages(); R_SpriteInit(); @@ -607,7 +616,7 @@ void R_Shutdown( void ) Mem_FreePool( &r_temppool ); // shut down OS specific OpenGL stuff like contexts, etc. - R_Free_OpenGL(); + R_Free_Video(); } /* diff --git a/engine/client/vid_common.h b/engine/client/vid_common.h index 63b6812f..3047f584 100644 --- a/engine/client/vid_common.h +++ b/engine/client/vid_common.h @@ -12,14 +12,6 @@ typedef struct vidmode_s int height; } vidmode_t; -typedef enum -{ - rserr_ok, - rserr_invalid_fullscreen, - rserr_invalid_mode, - rserr_unknown -} rserr_t; - // minimal recommended resolution #define VID_MIN_WIDTH 640 #define VID_MIN_HEIGHT 480 @@ -43,23 +35,4 @@ void VID_StartupGamma( void ); void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cvarname, int r_ext ); void GL_SetExtension( int r_ext, int enable ); -// -// platform-defined calls -// -void GL_InitExtensions( void ); -void VID_RestoreScreenResolution( void ); -qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ); -void VID_DestroyWindow( void ); -qboolean R_Init_OpenGL( void ); -void R_Free_OpenGL( void ); -void *GL_GetProcAddress( const char *name ); -qboolean GL_CreateContext( void ); -qboolean GL_UpdateContext( void ); -qboolean GL_DeleteContext( void ); -int R_MaxVideoModes(); -vidmode_t R_GetVideoMode( int num ); -rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ); -void R_ChangeDisplaySettingsFast( int width, int height ); // for fast resizing -qboolean VID_SetMode( void ); - #endif // VID_COMMON diff --git a/engine/common/host_state.c b/engine/common/host_state.c index 12a51446..3d60ea09 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -14,10 +14,7 @@ GNU General Public License for more details. */ #include "common.h" - -#ifdef XASH_SDL -#include "platform/sdl/events.h" -#endif +#include "platform/platform.h" void COM_InitHostState( void ) { @@ -138,11 +135,7 @@ void Host_ShutdownGame( void ) void Host_RunFrame( float time ) { -#if XASH_INPUT == INPUT_SDL - SDLash_RunEvents(); -#elif XASH_INPUT == INPUT_ANDROID - Android_RunEvents(); -#endif + Platform_RunEvents(); // engine main frame Host_Frame( time ); From 7390d11505566346d8872ae4dca7d001bf15aa13 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:28:24 +0300 Subject: [PATCH 061/205] platform_sdl: adapt SDL backend code to new platform backends system --- engine/platform/sdl/events.c | 168 +--------------------------- engine/platform/sdl/events.h | 20 +++- engine/platform/sdl/in_sdl.c | 202 ++++++++++++++++++++++++++++++++++ engine/platform/sdl/vid_sdl.c | 66 +++++------ 4 files changed, 253 insertions(+), 203 deletions(-) create mode 100644 engine/platform/sdl/in_sdl.c diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index fb4c645d..2d070dc1 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -26,13 +26,7 @@ GNU General Public License for more details. #include "vid_common.h" #include "gl_local.h" -extern convar_t *vid_fullscreen; -extern convar_t *snd_mute_losefocus; static int wheelbutton; -static SDL_Joystick *joy; -static SDL_GameController *gamecontroller; - -void R_ChangeDisplaySettingsFast( int w, int h ); /* ============= @@ -194,17 +188,6 @@ static void SDLash_InputEvent( SDL_TextInputEvent input ) } } -/* -============= -SDLash_EnableTextInput - -============= -*/ -void SDLash_EnableTextInput( qboolean enable ) -{ - enable ? SDL_StartTextInput() : SDL_StopTextInput(); -} - /* ============= SDLash_EventFilter @@ -479,7 +462,7 @@ SDLash_RunEvents ============= */ -void SDLash_RunEvents( void ) +void Platform_RunEvents( void ) { SDL_Event event; @@ -487,154 +470,9 @@ void SDLash_RunEvents( void ) SDLash_EventFilter( &event ); } -/* -============= -SDLash_JoyInit_Old - -============= -*/ -static int SDLash_JoyInit_Old( int numjoy ) +void* Platform_GetNativeObject( void ) { - int num; - int i; - - MsgDev( D_INFO, "Joystick: SDL\n" ); - - if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK && - SDL_InitSubSystem( SDL_INIT_JOYSTICK ) ) - { - MsgDev( D_INFO, "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); - return 0; - } - - if( joy ) - { - SDL_JoystickClose( joy ); - } - - num = SDL_NumJoysticks(); - - if( num > 0 ) - MsgDev( D_INFO, "%i joysticks found:\n", num ); - else - { - MsgDev( D_INFO, "No joystick found.\n" ); - return 0; - } - - for( i = 0; i < num; i++ ) - MsgDev( D_INFO, "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); - - MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); - - joy = SDL_JoystickOpen( numjoy ); - - if( !joy ) - { - MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); - return 0; - } - - MsgDev( D_INFO, "Selected joystick: %s\n" - "\tAxes: %i\n" - "\tHats: %i\n" - "\tButtons: %i\n" - "\tBalls: %i\n", - SDL_JoystickName( joy ), SDL_JoystickNumAxes( joy ), SDL_JoystickNumHats( joy ), - SDL_JoystickNumButtons( joy ), SDL_JoystickNumBalls( joy ) ); - - SDL_GameControllerEventState( SDL_DISABLE ); - SDL_JoystickEventState( SDL_ENABLE ); - - return num; + return NULL; // SDL don't have it } -/* -============= -SDLash_JoyInit_New - -============= -*/ -static int SDLash_JoyInit_New( int numjoy ) -{ - int temp, num; - int i; - - MsgDev( D_INFO, "Joystick: SDL GameController API\n" ); - - if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && - SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) ) - { - MsgDev( D_INFO, "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); - return 0; - } - - // chance to add mappings from file - SDL_GameControllerAddMappingsFromFile( "controllermappings.txt" ); - - if( gamecontroller ) - { - SDL_GameControllerClose( gamecontroller ); - } - - temp = SDL_NumJoysticks(); - num = 0; - - for( i = 0; i < temp; i++ ) - { - if( SDL_IsGameController( i )) - num++; - } - - if( num > 0 ) - MsgDev( D_INFO, "%i joysticks found:\n", num ); - else - { - MsgDev( D_INFO, "No joystick found.\n" ); - return 0; - } - - for( i = 0; i < num; i++ ) - MsgDev( D_INFO, "%i\t: %s\n", i, SDL_GameControllerNameForIndex( i ) ); - - MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); - - gamecontroller = SDL_GameControllerOpen( numjoy ); - - if( !gamecontroller ) - { - MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); - return 0; - } -// was added in SDL2-2.0.6, allow build with earlier versions just in case -#if SDL_MAJOR_VERSION > 2 || SDL_MINOR_VERSION > 0 || SDL_PATCHLEVEL >= 6 - MsgDev( D_INFO, "Selected joystick: %s (%i:%i:%i)\n", - SDL_GameControllerName( gamecontroller ), - SDL_GameControllerGetVendor( gamecontroller ), - SDL_GameControllerGetProduct( gamecontroller ), - SDL_GameControllerGetProductVersion( gamecontroller )); -#endif - SDL_GameControllerEventState( SDL_ENABLE ); - SDL_JoystickEventState( SDL_DISABLE ); - - return num; -} - -/* -============= -SDLash_JoyInit - -============= -*/ -int SDLash_JoyInit( int numjoy ) -{ - // SDL_Joystick is now an old API - // SDL_GameController is preferred - if( Sys_CheckParm( "-sdl_joy_old_api" ) ) - return SDLash_JoyInit_Old(numjoy); - - return SDLash_JoyInit_New(numjoy); -} - - #endif // defined( XASH_SDL ) && !defined( XASH_DEDICATED ) diff --git a/engine/platform/sdl/events.h b/engine/platform/sdl/events.h index 57c5cfd3..a1ab29b2 100644 --- a/engine/platform/sdl/events.h +++ b/engine/platform/sdl/events.h @@ -1,6 +1,6 @@ /* -events.h - SDL event system handlers -Copyright (C) 2015-2017 a1batross +events.h - SDL backend internal header +Copyright (C) 2015-2018 a1batross This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,12 +16,20 @@ GNU General Public License for more details. #pragma once #ifndef KEYWRAPPER_H #define KEYWRAPPER_H +#ifdef XASH_SDL -#ifdef XASH_SDL +#include "platform/platform.h" + +// window management +void VID_RestoreScreenResolution( void ); +void R_ChangeDisplaySettingsFast( int width, int height ); // for fast resizing +qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ); +void VID_DestroyWindow( void ); +void GL_InitExtensions( void ); +qboolean GL_CreateContext( void ); +qboolean GL_UpdateContext( void ); +qboolean GL_DeleteContext( void ); -void SDLash_RunEvents( void ); -void SDLash_EnableTextInput( qboolean enable ); -int SDLash_JoyInit( int numjoy ); // pass -1 to init every joystick #endif // XASH_SDL #endif // KEYWRAPPER_H diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c new file mode 100644 index 00000000..78ed366b --- /dev/null +++ b/engine/platform/sdl/in_sdl.c @@ -0,0 +1,202 @@ +/* +vid_sdl.c - SDL input component +Copyright (C) 2018 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef XASH_DEDICATED +#include + +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" +#include "vgui_draw.h" +#include "events.h" +#include "sound.h" +#include "vid_common.h" +#include "gl_local.h" + +static SDL_Joystick *joy; +static SDL_GameController *gamecontroller; + +/* +============= +Platform_Vibrate + +============= +*/ +void Platform_Vibrate( float time, char flags ) +{ + // stub +} + +/* +============= +SDLash_EnableTextInput + +============= +*/ +void Platform_EnableTextInput( qboolean enable ) +{ + enable ? SDL_StartTextInput() : SDL_StopTextInput(); +} + +/* +============= +SDLash_JoyInit_Old + +============= +*/ +static int SDLash_JoyInit_Old( int numjoy ) +{ + int num; + int i; + + MsgDev( D_INFO, "Joystick: SDL\n" ); + + if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK && + SDL_InitSubSystem( SDL_INIT_JOYSTICK ) ) + { + MsgDev( D_INFO, "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); + return 0; + } + + if( joy ) + { + SDL_JoystickClose( joy ); + } + + num = SDL_NumJoysticks(); + + if( num > 0 ) + MsgDev( D_INFO, "%i joysticks found:\n", num ); + else + { + MsgDev( D_INFO, "No joystick found.\n" ); + return 0; + } + + for( i = 0; i < num; i++ ) + MsgDev( D_INFO, "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); + + MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + + joy = SDL_JoystickOpen( numjoy ); + + if( !joy ) + { + MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + return 0; + } + + MsgDev( D_INFO, "Selected joystick: %s\n" + "\tAxes: %i\n" + "\tHats: %i\n" + "\tButtons: %i\n" + "\tBalls: %i\n", + SDL_JoystickName( joy ), SDL_JoystickNumAxes( joy ), SDL_JoystickNumHats( joy ), + SDL_JoystickNumButtons( joy ), SDL_JoystickNumBalls( joy ) ); + + SDL_GameControllerEventState( SDL_DISABLE ); + SDL_JoystickEventState( SDL_ENABLE ); + + return num; +} + +/* +============= +SDLash_JoyInit_New + +============= +*/ +static int SDLash_JoyInit_New( int numjoy ) +{ + int temp, num; + int i; + + MsgDev( D_INFO, "Joystick: SDL GameController API\n" ); + + if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && + SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) ) + { + MsgDev( D_INFO, "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); + return 0; + } + + // chance to add mappings from file + SDL_GameControllerAddMappingsFromFile( "controllermappings.txt" ); + + if( gamecontroller ) + { + SDL_GameControllerClose( gamecontroller ); + } + + temp = SDL_NumJoysticks(); + num = 0; + + for( i = 0; i < temp; i++ ) + { + if( SDL_IsGameController( i )) + num++; + } + + if( num > 0 ) + MsgDev( D_INFO, "%i joysticks found:\n", num ); + else + { + MsgDev( D_INFO, "No joystick found.\n" ); + return 0; + } + + for( i = 0; i < num; i++ ) + MsgDev( D_INFO, "%i\t: %s\n", i, SDL_GameControllerNameForIndex( i ) ); + + MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + + gamecontroller = SDL_GameControllerOpen( numjoy ); + + if( !gamecontroller ) + { + MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + return 0; + } +// was added in SDL2-2.0.6, allow build with earlier versions just in case +#if SDL_MAJOR_VERSION > 2 || SDL_MINOR_VERSION > 0 || SDL_PATCHLEVEL >= 6 + MsgDev( D_INFO, "Selected joystick: %s (%i:%i:%i)\n", + SDL_GameControllerName( gamecontroller ), + SDL_GameControllerGetVendor( gamecontroller ), + SDL_GameControllerGetProduct( gamecontroller ), + SDL_GameControllerGetProductVersion( gamecontroller )); +#endif + SDL_GameControllerEventState( SDL_ENABLE ); + SDL_JoystickEventState( SDL_DISABLE ); + + return num; +} + +/* +============= +Platform_JoyInit + +============= +*/ +int Platform_JoyInit( int numjoy ) +{ + // SDL_Joystick is now an old API + // SDL_GameController is preferred + if( Sys_CheckParm( "-sdl_joy_old_api" ) ) + return SDLash_JoyInit_Old(numjoy); + + return SDLash_JoyInit_New(numjoy); +} + +#endif // XASH_DEDICATED diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index ca335f6b..2ceb5c8b 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -13,14 +13,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #ifndef XASH_DEDICATED - +#include #include "common.h" #include "client.h" #include "gl_local.h" #include "mod_local.h" #include "input.h" #include "vid_common.h" -#include +#include "platform/sdl/events.h" static vidmode_t *vidmodes = NULL; static int num_vidmodes = 0; @@ -204,18 +204,14 @@ int R_MaxVideoModes( void ) return num_vidmodes; } -vidmode_t R_GetVideoMode( int num ) +vidmode_t *R_GetVideoMode( int num ) { - static vidmode_t error = { NULL }; - if( !vidmodes || num < 0 || num >= R_MaxVideoModes() ) { - error.width = glState.width; - error.height = glState.height; - return error; + return NULL; } - return vidmodes[num]; + return vidmodes + num; } static void R_InitVideoModes( void ) @@ -417,6 +413,24 @@ void GL_UpdateSwapInterval( void ) } } +/* +================= +GL_DeleteContext + +always return false +================= +*/ +qboolean GL_DeleteContext( void ) +{ + if( glw_state.context ) + { + SDL_GL_DeleteContext(glw_state.context); + glw_state.context = NULL; + } + + return false; +} + /* ================= GL_CreateContext @@ -471,24 +485,6 @@ qboolean GL_UpdateContext( void ) return true; } -/* -================= -GL_DeleteContext - -always return false -================= -*/ -qboolean GL_DeleteContext( void ) -{ - if( glw_state.context ) - { - SDL_GL_DeleteContext(glw_state.context); - glw_state.context = NULL; - } - - return false; -} - qboolean VID_SetScreenResolution( int width, int height ) { SDL_DisplayMode want, got; @@ -837,13 +833,14 @@ static void GL_SetupAttributes( void ) /* ================== -R_Init_OpenGL +R_Init_Video ================== */ -qboolean R_Init_OpenGL( void ) +qboolean R_Init_Video( void ) { SDL_DisplayMode displayMode; string safe; + qboolean retval; SDL_GetCurrentDisplayMode(0, &displayMode); glw_state.desktopBitsPixel = SDL_BITSPERPIXEL(displayMode.format); @@ -875,7 +872,12 @@ qboolean R_Init_OpenGL( void ) WIN_SetDPIAwareness(); #endif - return VID_SetMode(); + if( !(retval = VID_SetMode()) ) + { + return retval; + } + + GL_InitExtensions(); } #ifdef XASH_GLES @@ -1253,10 +1255,10 @@ qboolean VID_SetMode( void ) /* ================== -R_Free_OpenGL +R_Free_Video ================== */ -void R_Free_OpenGL( void ) +void R_Free_Video( void ) { GL_DeleteContext (); From f4567b2c9deaa34aedd5c68e12266e4d908a2f91 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:36:38 +0300 Subject: [PATCH 062/205] sound: make snd_mute_losefocus accessible outside --- engine/client/in_joy.c | 75 +++++++++++++++--------------------------- engine/client/sound.h | 1 + 2 files changed, 27 insertions(+), 49 deletions(-) diff --git a/engine/client/in_joy.c b/engine/client/in_joy.c index ab6d620e..fc439d71 100644 --- a/engine/client/in_joy.c +++ b/engine/client/in_joy.c @@ -21,10 +21,7 @@ GNU General Public License for more details. #include "keydefs.h" #include "client.h" #include "gl_local.h" - -#if defined(XASH_SDL) -#include "platform/sdl/events.h" -#endif +#include "platform/platform.h" #ifndef SHRT_MAX #define SHRT_MAX 0x7FFF @@ -62,28 +59,24 @@ static struct joy_axis_s short val; short prevval; } joyaxis[MAX_AXES] = { 0 }; -static qboolean initialized = false, forcedisable = false; -static convar_t *joy_enable; +static qboolean forcedisable = false; static byte currentbinding; // add posibility to remap keys, to place it in joykeys[] - -float IN_TouchDrawText( float x1, float y1, float x2, float y2, const char *s, byte *color, float size ); -float IN_TouchDrawCharacter( float x, float y, int number, float size ); - -convar_t *joy_pitch; -convar_t *joy_yaw; -convar_t *joy_forward; -convar_t *joy_side; -convar_t *joy_found; -convar_t *joy_index; -convar_t *joy_lt_threshold; -convar_t *joy_rt_threshold; -convar_t *joy_side_deadzone; -convar_t *joy_forward_deadzone; -convar_t *joy_side_key_threshold; -convar_t *joy_forward_key_threshold; -convar_t *joy_pitch_deadzone; -convar_t *joy_yaw_deadzone; -convar_t *joy_axis_binding; +static convar_t *joy_enable; +static convar_t *joy_pitch; +static convar_t *joy_yaw; +static convar_t *joy_forward; +static convar_t *joy_side; +static convar_t *joy_found; +static convar_t *joy_index; +static convar_t *joy_lt_threshold; +static convar_t *joy_rt_threshold; +static convar_t *joy_side_deadzone; +static convar_t *joy_forward_deadzone; +static convar_t *joy_side_key_threshold; +static convar_t *joy_forward_key_threshold; +static convar_t *joy_pitch_deadzone; +static convar_t *joy_yaw_deadzone; +static convar_t *joy_axis_binding; /* ============ @@ -92,7 +85,7 @@ Joy_IsActive */ qboolean Joy_IsActive( void ) { - return !forcedisable && initialized; + return !forcedisable && joy_found->value; } /* @@ -117,7 +110,7 @@ void Joy_HatMotionEvent( int id, byte hat, byte value ) }; int i; - if( !initialized ) + if( !joy_found->value ) return; for( i = 0; i < ARRAYSIZE( keys ); i++ ) @@ -261,7 +254,7 @@ void Joy_AxisMotionEvent( int id, byte axis, short value ) { byte engineAxis; - if( !initialized ) + if( !joy_found->value ) return; if( axis >= MAX_AXES ) @@ -293,7 +286,7 @@ Trackball events. UNDONE */ void Joy_BallMotionEvent( int id, byte ball, short xrel, short yrel ) { - //if( !initialized ) + //if( !joy_found->value ) // return; } @@ -306,7 +299,7 @@ Button events */ void Joy_ButtonEvent( int id, byte button, byte down ) { - if( !initialized ) + if( !joy_found->value ) return; // generic game button code. @@ -331,7 +324,7 @@ Called when joystick is removed. For future expansion */ void Joy_RemoveEvent( int id ) { - if( !forcedisable && initialized && joy_found->value ) + if( !forcedisable && joy_found->value ) Cvar_SetValue("joy_found", joy_found->value - 1.0f); } @@ -347,8 +340,6 @@ void Joy_AddEvent( int id ) if( forcedisable ) return; - initialized = true; - Cvar_SetValue("joy_found", joy_found->value + 1.0f); } @@ -361,7 +352,7 @@ Append movement from axis. Called everyframe */ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw ) { - if( !initialized || !joy_enable->value ) + if( !joy_found->value || !joy_enable->value ) return; if( FBitSet( joy_axis_binding->flags, FCVAR_CHANGED ) ) @@ -440,19 +431,7 @@ void Joy_Init( void ) return; } -#if defined(XASH_SDL) - // SDL can tell us about connected joysticks - Cvar_SetValue( "joy_found", SDLash_JoyInit( joy_index->value ) ); -#elif defined(ANDROID) - // Initalized after first Joy_AddEvent -#else -#warning "Any platform must implement platform-dependent JoyInit, start event system. Otherwise no joystick support" -#endif - - if( joy_found->value > 0 ) - initialized = true; - else - initialized = false; + Cvar_SetValue( "joy_found", Platform_JoyInit( joy_index->value ) ); } /* @@ -465,8 +444,6 @@ Shutdown joystick code void Joy_Shutdown( void ) { Cvar_SetValue( "joy_found", 0 ); - - initialized = false; } #endif // XASH_DEDICATED diff --git a/engine/client/sound.h b/engine/client/sound.h index 09f49bf9..1b83fc00 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -275,6 +275,7 @@ extern convar_t *s_lerping; extern convar_t *dsp_off; extern convar_t *s_test; // cvar to testify new effects extern convar_t *s_samplecount; +extern convar_t *snd_mute_losefocus; void S_InitScaletable( void ); wavdata_t *S_LoadSound( sfx_t *sfx ); From 7c9af896207f458f5d611c44e8824735841b720d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 00:36:58 +0300 Subject: [PATCH 063/205] input: remove unneeded joy_found extern --- engine/client/input.h | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/client/input.h b/engine/client/input.h index 2dba7c2c..dbfd0a8a 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -87,7 +87,6 @@ enum JOY_HAT_LEFTUP = JOY_HAT_LEFT | JOY_HAT_UP, JOY_HAT_LEFTDOWN = JOY_HAT_LEFT | JOY_HAT_DOWN }; -extern convar_t *joy_found; qboolean Joy_IsActive( void ); void Joy_HatMotionEvent( int id, byte hat, byte value ); From e9e364f054851ae6b7a75876c2ed80dae1fa537c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:01:22 +0300 Subject: [PATCH 064/205] wscript: move sdl path to root wscript --- engine/wscript | 3 --- wscript | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/wscript b/engine/wscript index a05bf2c7..c42f1967 100644 --- a/engine/wscript +++ b/engine/wscript @@ -8,9 +8,6 @@ import os top = '.' def options(opt): - opt.add_option( - '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, - help = 'SDL2 path to build(required for Windows)') opt.add_option( '--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, help = 'build engine with BSP2 map support(recommended for Quake, breaks compability!)') diff --git a/wscript b/wscript index 97700378..88cdfca1 100644 --- a/wscript +++ b/wscript @@ -55,6 +55,10 @@ def options(opt): '--no-gcc-colors', action = 'store_false', dest = 'GCC_COLORS', default = True, help = 'do not enable gcc colors') + opt.add_option( + '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, + help = 'SDL2 path to build(required for Windows)') + opt.recurse(SUBDIRS) def configure(conf): From ca501c03783b7888dbc264ef318301f4610e63cc Mon Sep 17 00:00:00 2001 From: a1batross Date: Wed, 24 Oct 2018 20:12:32 +0300 Subject: [PATCH 065/205] wscript: add possibility to select windows subsystem during link, add define to use WinXP SDK. This commit forces WinXP compability on latest Visual Studio --- engine/wscript | 6 ++++-- game_launch/wscript | 3 ++- mainui | 2 +- wscript | 26 +++++++++++++++----------- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/engine/wscript b/engine/wscript index c42f1967..7b848601 100644 --- a/engine/wscript +++ b/engine/wscript @@ -94,7 +94,8 @@ def build(bld): features = 'c cprogram', includes = includes, use = libs, - install_path = bld.env.BINDIR + install_path = bld.env.BINDIR, + subsystem = bld.env.MSVC_SUBSYSTEM ) else: bld.shlib( @@ -103,5 +104,6 @@ def build(bld): features = 'c', includes = includes, use = libs, - install_path = bld.env.LIBDIR + install_path = bld.env.LIBDIR, + subsystem = bld.env.MSVC_SUBSYSTEM ) diff --git a/game_launch/wscript b/game_launch/wscript index db762e4d..116a13ed 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -63,5 +63,6 @@ def build(bld): features = 'c cprogram', includes = includes, use = libs, - install_path = bld.env.BINDIR + install_path = bld.env.BINDIR, + subsystem = bld.env.MSVC_SUBSYSTEM ) diff --git a/mainui b/mainui index dac66977..72233d02 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit dac66977620487260c7351c2a2c845f081a5dc71 +Subproject commit 72233d02c1a0912e8667fab1ce4561b470ce4293 diff --git a/wscript b/wscript index 88cdfca1..cbcff9e6 100644 --- a/wscript +++ b/wscript @@ -71,18 +71,16 @@ def configure(conf): conf.env.DEST_64BIT = False # predict state try: conf.check_cc( - fragment=''' - int main( void ) - { - int check[sizeof(void*) == 4 ? 1: -1]; - return 0; - } - ''', + fragment='''int main( void ) + { + int check[sizeof(void*) == 4 ? 1: -1]; + return 0; + }''', msg = 'Checking if compiler create 32 bit code') except conf.errors.ConfigurationError: # Program not compiled, we have 64 bit conf.env.DEST_64BIT = True - + if(conf.env.DEST_64BIT): if(not conf.options.ALLOW64): conf.env.append_value('LINKFLAGS', ['-m32']) @@ -91,7 +89,7 @@ def configure(conf): Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') else: Logs.warn('WARNING: 64-bit engine may be unstable') - + if(conf.env.COMPILER_CC != 'msvc'): if(conf.env.COMPILER_CC == 'gcc'): conf.env.append_unique('LINKFLAGS', ['-Wl,--no-undefined']) @@ -112,15 +110,21 @@ def configure(conf): conf.env.append_unique('CFLAGS', ['/Z7']) conf.env.append_unique('CXXFLAGS', ['/Z7']) conf.env.append_unique('LINKFLAGS', ['/DEBUG']) + conf.env.append_unique('DEFINES', '_USING_V110_SDK71_') # Force XP compability + + # Force XP compability, all build targets should add + # subsystem=bld.env.MSVC_SUBSYSTEM + # TODO: wrapper around bld.stlib, bld.shlib and so on? + conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' if(conf.env.DEST_OS != 'win32'): conf.check( lib='dl' ) conf.check( lib='m' ) conf.check( lib='pthread' ) - + conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated - + # indicate if we are packaging for Linux/BSD if(not conf.options.WIN_INSTALL and conf.env.DEST_OS != 'win32' and From 85960b2c904fc94cf710706f0e59603e44d8558f Mon Sep 17 00:00:00 2001 From: a1batross Date: Wed, 24 Oct 2018 20:17:44 +0300 Subject: [PATCH 066/205] wscript: enable DBGHELP for Win32 --- engine/wscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/wscript b/engine/wscript index 7b848601..5b5b4846 100644 --- a/engine/wscript +++ b/engine/wscript @@ -48,6 +48,8 @@ def configure(conf): conf.check( lib='SHELL32' ) conf.check( lib='GDI32' ) conf.check( lib='ADVAPI32' ) + conf.check( lib='DBGHELP' ) + conf.env.append_unique('DEFINES', 'DBGHELP') def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) @@ -63,7 +65,7 @@ def build(bld): if bld.env.DEST_OS != 'win32': libs += [ 'DL', 'M', 'PTHREAD' ] else: - libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32'] + libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP'] source += bld.path.ant_glob(['platform/win32/*.c']) source += bld.path.ant_glob([ From c603abfebf607bd46e977366b8a08e7dc13c8f8c Mon Sep 17 00:00:00 2001 From: a1batross Date: Wed, 24 Oct 2018 20:18:06 +0300 Subject: [PATCH 067/205] crashhandler: fix developer mode check --- engine/common/crashhandler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 5ce4b938..3f2826ba 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -202,7 +202,7 @@ long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) CL_Crashed(); // tell client about crash else host.status = HOST_CRASHED; - if( host.developer <= 0 ) + if( host_developer.value <= 0 ) { // no reason to call debugger in release build - just exit Sys_Quit(); From 68ed7329284531ff6de9428e9ee353e7cddb76f1 Mon Sep 17 00:00:00 2001 From: a1batross Date: Wed, 24 Oct 2018 20:55:00 +0300 Subject: [PATCH 068/205] win_con: fix version in title, change title for dedicated server --- engine/platform/win32/win_con.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/win32/win_con.c b/engine/platform/win32/win_con.c index 087c7e5b..836917c7 100644 --- a/engine/platform/win32/win_con.c +++ b/engine/platform/win32/win_con.c @@ -290,7 +290,7 @@ void Wcon_CreateConsole( void ) rect.top = 0; rect.bottom = 364; Q_strncpy( FontName, "Fixedsys", sizeof( FontName )); - Q_strncpy( s_wcd.title, va( "Xash3D %g", XASH_VERSION ), sizeof( s_wcd.title )); + Q_strncpy( s_wcd.title, va( "Xash3D %s", XASH_VERSION ), sizeof( s_wcd.title )); Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path )); fontsize = 8; } @@ -301,7 +301,7 @@ void Wcon_CreateConsole( void ) rect.top = 0; rect.bottom = 392; Q_strncpy( FontName, "System", sizeof( FontName )); - Q_strncpy( s_wcd.title, "Xash Dedicated Server", sizeof( s_wcd.title )); + Q_strncpy( s_wcd.title, va( "XashDS %s", XASH_VERSION ), sizeof( s_wcd.title )); Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path )); s_wcd.log_active = true; // always make log fontsize = 14; From 96e0167e479c78f4d644d8c0913cc266adc2c6c7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:09:43 +0300 Subject: [PATCH 069/205] platform: add GetMousePos, SetMousePos calls, fix typo --- engine/client/cl_game.c | 39 ++++------------------------------ engine/client/in_touch.c | 2 +- engine/client/input.c | 18 ++++++---------- engine/client/vgui/vgui_draw.c | 2 +- engine/platform/platform.h | 3 +++ engine/platform/sdl/events.c | 2 +- engine/platform/sdl/in_sdl.c | 22 +++++++++++++++++++ 7 files changed, 38 insertions(+), 50 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 500aab82..abf3b487 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -29,9 +29,7 @@ GNU General Public License for more details. #include "library.h" #include "vgui_draw.h" #include "sound.h" // SND_STOP_LOOPING -#ifdef XASH_SDL -#include -#endif +#include "platform/platform.h" #define MAX_LINELENGTH 80 #define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels) @@ -2021,22 +2019,6 @@ static float pfnGetClientMaxspeed( void ) return cl.local.maxspeed; } -/* -============= -CL_GetMousePosition - -============= -*/ -void CL_GetMousePosition( int *mx, int *my ) -{ -#ifdef XASH_SDL - SDL_GetMouseState( mx, my ); -#else - if( mx ) *mx = 0; - if( my ) *my = 0; -#endif -} - /* ============= pfnIsNoClipping @@ -2747,20 +2729,7 @@ void pfnGetMousePos( struct tagPOINT *ppt ) if( !ppt ) return; - CL_GetMousePosition( &ppt->x, &ppt->y ); -} - -/* -============= -pfnSetMousePos - -============= -*/ -void pfnSetMousePos( int mx, int my ) -{ -#ifdef XASH_SDL - SDL_WarpMouseInWindow( host.hWnd, mx, my ); -#endif + Platform_GetMousePos( &ppt->x, &ppt->y ); } /* @@ -4002,7 +3971,7 @@ static cl_enginefunc_t gEngfuncs = pfnGetClientMaxspeed, COM_CheckParm, Key_Event, - CL_GetMousePosition, + Platform_GetMousePos, pfnIsNoClipping, CL_GetLocalPlayer, pfnGetViewModel, @@ -4052,7 +4021,7 @@ static cl_enginefunc_t gEngfuncs = pfnGetPlayerForTrackerID, pfnServerCmdUnreliable, pfnGetMousePos, - pfnSetMousePos, + Platform_SetMousePos, pfnSetMouseEnable, Cvar_GetList, (void*)Cmd_GetFirstFunctionHandle, diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 74f36120..4c219a63 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1728,7 +1728,7 @@ void IN_TouchKeyEvent( int key, int down ) if( !touch.clientonly ) return; - CL_GetMousePosition( &xi, &yi ); + Platform_GetMousePos( &xi, &yi ); x = xi/SCR_W; y = yi/SCR_H; diff --git a/engine/client/input.c b/engine/client/input.c index 03a5dfd9..cade8019 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -101,10 +101,8 @@ void IN_MouseSavePos( void ) if( !in_mouseactive ) return; -#ifdef XASH_SDL - SDL_GetMouseState( &in_lastvalidpos.x, &in_lastvalidpos.y ); + Platform_GetMousePos( &in_lastvalidpos.x, &in_lastvalidpos.y ); in_mouse_savedpos = true; -#endif } /* @@ -119,9 +117,7 @@ void IN_MouseRestorePos( void ) if( !in_mouse_savedpos ) return; -#ifdef XASH_SDL - SDL_WarpMouseInWindow( host.hWnd, in_lastvalidpos.x, in_lastvalidpos.y ); -#endif + Platform_SetMousePos( in_lastvalidpos.x, in_lastvalidpos.y ); in_mouse_savedpos = false; } @@ -152,7 +148,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) } else { - SDL_WarpMouseInWindow( host.hWnd, host.window_center_x, host.window_center_y ); + Platform_SetMousePos( host.window_center_x, host.window_center_y ); SDL_SetWindowGrab( host.hWnd, SDL_TRUE ); if( clgame.dllFuncs.pfnLookEvent ) SDL_SetRelativeMouseMode( SDL_TRUE ); @@ -282,9 +278,7 @@ void IN_MouseMove( void ) return; // find mouse movement -#ifdef XASH_SDL - SDL_GetMouseState( ¤t_pos.x, ¤t_pos.y ); -#endif + Platform_GetMousePos( ¤t_pos.x, ¤t_pos.y ); VGui_MouseMove( current_pos.x, current_pos.y ); @@ -321,7 +315,7 @@ void IN_MouseEvent( void ) #if defined( XASH_SDL ) static qboolean ignore; // igonre mouse warp event int x, y; - SDL_GetMouseState(&x, &y); + Platform_GetMousePos(&x, &y); if( host.mouse_visible ) SDL_ShowCursor( SDL_TRUE ); else @@ -332,7 +326,7 @@ void IN_MouseEvent( void ) x > host.window_center_x + host.window_center_x / 2 || y > host.window_center_y + host.window_center_y / 2 ) { - SDL_WarpMouseInWindow(host.hWnd, host.window_center_x, host.window_center_y); + Platform_SetMousePos( host.window_center_x, host.window_center_y ); ignore = 1; // next mouse event will be mouse warp return; } diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index a70dacb0..88ee44bf 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -81,7 +81,7 @@ void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) float yscale = (float)glState.height / (float)clgame.scrInfo.iHeight; int x, y; - CL_GetMousePosition( &x, &y ); + Platform_GetMousePos( &x, &y ); *_x = x / xscale, *_y = y / yscale; } diff --git a/engine/platform/platform.h b/engine/platform/platform.h index d0cdff30..69362bd4 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -40,6 +40,9 @@ int Platform_JoyInit( int numjoy ); // returns number of connected gamepads, neg void Platform_EnableTextInput( qboolean enable ); // System events void Platform_RunEvents( void ); +// Mouse +void Platform_GetMousePos( int *x, int *y ); +void Platform_SetMousePos( int x, int y ); /* ============================================================================== diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 2d070dc1..491d7d50 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -470,7 +470,7 @@ void Platform_RunEvents( void ) SDLash_EventFilter( &event ); } -void* Platform_GetNativeObject( void ) +void* Platform_GetNativeObject( const char *name ) { return NULL; // SDL don't have it } diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 78ed366b..ad075838 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -28,6 +28,28 @@ GNU General Public License for more details. static SDL_Joystick *joy; static SDL_GameController *gamecontroller; +/* +============= +Platform_GetMousePos + +============= +*/ +void Platform_GetMousePos( int *x, int *y ) +{ + SDL_GetMouseState( x, y ); +} + +/* +============= +Platform_SetMousePos + +============ +*/ +void Platform_SetMousePos( int x, int y ) +{ + SDL_WarpMouseInWindow( host.hWnd, x, y ); +} + /* ============= Platform_Vibrate From 90d2434bb08be8f7af9e95935329bd10c9e56b47 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:25:29 +0300 Subject: [PATCH 070/205] platform: add Set/GetClipboardText calls. Remove unneeded SDL_SetHint call on Android, because Android does not use SDL anymore --- engine/client/in_touch.c | 3 --- engine/common/system.c | 15 +++------------ engine/platform/platform.h | 3 +++ engine/platform/sdl/in_sdl.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 4c219a63..fe58049d 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -907,9 +907,6 @@ void IN_TouchInit( void ) // input devices cvar touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE, "enable touch controls" ); -#if defined(XASH_SDL) && defined(__ANDROID__) - SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); -#endif touch.initialized = true; } diff --git a/engine/common/system.c b/engine/common/system.c index 3b02986a..f9072534 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -15,6 +15,7 @@ GNU General Public License for more details. #include "common.h" #include "mathlib.h" +#include "platform/platform.h" #include #include @@ -157,18 +158,10 @@ create buffer, that contain clipboard char *Sys_GetClipboardData( void ) { static char data[1024]; - char *cliptext; data[0] = '\0'; -#ifdef XASH_SDL - cliptext = SDL_GetClipboardText(); - if( cliptext ) - { - Q_strncpy( data, cliptext, sizeof( data ) ); - SDL_free( cliptext ); - } -#endif // XASH_SDL + Platform_GetClipboardText( data, sizeof( data )); return data; } @@ -182,9 +175,7 @@ write screenshot into clipboard */ void Sys_SetClipboardData( const byte *buffer, size_t size ) { -#ifdef XASH_SDL - SDL_SetClipboardText((char *)buffer); -#endif + Platform_SetClipboardText( (char *)buffer, size ); } /* diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 69362bd4..fc941952 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -43,6 +43,9 @@ void Platform_RunEvents( void ); // Mouse void Platform_GetMousePos( int *x, int *y ); void Platform_SetMousePos( int x, int y ); +// Clipboard +void Platform_GetClipboardText( char *buffer, size_t size ); +void Platform_SetClipboardText( char *buffer, size_t size ); /* ============================================================================== diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index ad075838..b6d7d431 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -50,6 +50,34 @@ void Platform_SetMousePos( int x, int y ) SDL_WarpMouseInWindow( host.hWnd, x, y ); } +/* +============= +Platform_GetClipobardText + +============= +*/ +void Platform_GetClipboardText( char *buffer, size_t size ) +{ + char *sdlbuffer = SDL_GetClipboardText(); + + if( !sdlbuffer ) + return; + + Q_strncpy( buffer, sdlbuffer, size ); + SDL_free( sdlbuffer ); +} + +/* +============= +Platform_SetClipobardText + +============= +*/ +void Platform_SetClipboardText( char *buffer, size_t size ) +{ + SDL_SetClipboardText( buffer ); +} + /* ============= Platform_Vibrate From de838ec5e8d2392ce6b54c7f07d5c81d2812d196 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:46:50 +0300 Subject: [PATCH 071/205] rename backend files for great justice and consistency --- engine/platform/android/{android_lib.c => lib_android.c} | 0 engine/platform/android/{android_lib.h => lib_android.h} | 0 engine/platform/apple/{ios_lib.c => lib_ios.c} | 0 engine/platform/apple/{ios_lib.h => lib_ios.h} | 0 engine/platform/emscripten/{em_lib.c => lib_em.c} | 0 engine/platform/emscripten/{em_lib.h => lib_em.h} | 0 engine/{common => platform/posix}/lib_posix.c | 0 engine/platform/sdl/{s_backend.c => s_sdl.c} | 0 engine/platform/win32/{win_con.c => con_win.c} | 0 engine/platform/win32/{win_lib.c => lib_win.c} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename engine/platform/android/{android_lib.c => lib_android.c} (100%) rename engine/platform/android/{android_lib.h => lib_android.h} (100%) rename engine/platform/apple/{ios_lib.c => lib_ios.c} (100%) rename engine/platform/apple/{ios_lib.h => lib_ios.h} (100%) rename engine/platform/emscripten/{em_lib.c => lib_em.c} (100%) rename engine/platform/emscripten/{em_lib.h => lib_em.h} (100%) rename engine/{common => platform/posix}/lib_posix.c (100%) rename engine/platform/sdl/{s_backend.c => s_sdl.c} (100%) rename engine/platform/win32/{win_con.c => con_win.c} (100%) rename engine/platform/win32/{win_lib.c => lib_win.c} (100%) diff --git a/engine/platform/android/android_lib.c b/engine/platform/android/lib_android.c similarity index 100% rename from engine/platform/android/android_lib.c rename to engine/platform/android/lib_android.c diff --git a/engine/platform/android/android_lib.h b/engine/platform/android/lib_android.h similarity index 100% rename from engine/platform/android/android_lib.h rename to engine/platform/android/lib_android.h diff --git a/engine/platform/apple/ios_lib.c b/engine/platform/apple/lib_ios.c similarity index 100% rename from engine/platform/apple/ios_lib.c rename to engine/platform/apple/lib_ios.c diff --git a/engine/platform/apple/ios_lib.h b/engine/platform/apple/lib_ios.h similarity index 100% rename from engine/platform/apple/ios_lib.h rename to engine/platform/apple/lib_ios.h diff --git a/engine/platform/emscripten/em_lib.c b/engine/platform/emscripten/lib_em.c similarity index 100% rename from engine/platform/emscripten/em_lib.c rename to engine/platform/emscripten/lib_em.c diff --git a/engine/platform/emscripten/em_lib.h b/engine/platform/emscripten/lib_em.h similarity index 100% rename from engine/platform/emscripten/em_lib.h rename to engine/platform/emscripten/lib_em.h diff --git a/engine/common/lib_posix.c b/engine/platform/posix/lib_posix.c similarity index 100% rename from engine/common/lib_posix.c rename to engine/platform/posix/lib_posix.c diff --git a/engine/platform/sdl/s_backend.c b/engine/platform/sdl/s_sdl.c similarity index 100% rename from engine/platform/sdl/s_backend.c rename to engine/platform/sdl/s_sdl.c diff --git a/engine/platform/win32/win_con.c b/engine/platform/win32/con_win.c similarity index 100% rename from engine/platform/win32/win_con.c rename to engine/platform/win32/con_win.c diff --git a/engine/platform/win32/win_lib.c b/engine/platform/win32/lib_win.c similarity index 100% rename from engine/platform/win32/win_lib.c rename to engine/platform/win32/lib_win.c From 2fab2d9f7ec93dd90f3f00a73c36e557dc406638 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:47:12 +0300 Subject: [PATCH 072/205] wscript: update --- engine/wscript | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/engine/wscript b/engine/wscript index 5b5b4846..83ae7a00 100644 --- a/engine/wscript +++ b/engine/wscript @@ -59,22 +59,21 @@ def build(bld): bld.env = bld.all_envs[get_subproject_name(bld)] libs = [] - source = [] - - # basic build: dedicated only, no dependencies - if bld.env.DEST_OS != 'win32': - libs += [ 'DL', 'M', 'PTHREAD' ] - else: - libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP'] - source += bld.path.ant_glob(['platform/win32/*.c']) - - source += bld.path.ant_glob([ - 'common/*.c', + source = bld.path.ant_glob([ + 'common/*.c', 'common/imagelib/*.c', 'common/soundlib/*.c', 'common/soundlib/libmpg/*.c', 'server/*.c']) + # basic build: dedicated only, no dependencies + if bld.env.DEST_OS != 'win32': + libs += [ 'DL', 'M', 'PTHREAD' ] + source += bld.path.ant_glob(['platform/posix/*.c']) + else: + libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP'] + source += bld.path.ant_glob(['platform/win32/*.c']) + # add client files and sdl2 library if not bld.env.DEDICATED: libs.append( 'SDL2' ) From 95e64f3997cf10b38cacc96e00696624841ffcb9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:49:59 +0300 Subject: [PATCH 073/205] vgui: remove dead vgui_surf.cpp file --- engine/client/vgui/vgui_surf.cpp | 461 ------------------------------- 1 file changed, 461 deletions(-) delete mode 100644 engine/client/vgui/vgui_surf.cpp diff --git a/engine/client/vgui/vgui_surf.cpp b/engine/client/vgui/vgui_surf.cpp deleted file mode 100644 index 30e106a6..00000000 --- a/engine/client/vgui/vgui_surf.cpp +++ /dev/null @@ -1,461 +0,0 @@ -/* -vgui_surf.cpp - main vgui layer -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - -#include "common.h" -#include "client.h" -#include "vgui_draw.h" -#include "vgui_main.h" - -#define MAXVERTEXBUFFERS 1024 -#define MAX_PAINT_STACK 8 -#define FONT_SIZE 512 -#define FONT_PAGES 8 - -static char staticRGBA[FONT_SIZE * FONT_SIZE * 4]; -static vpoint_t g_VertexBuffer[MAXVERTEXBUFFERS]; -static int g_iVertexBufferEntriesUsed = 0; -static int staticContextCount = 0; - -struct FontInfo -{ - int id; - int pageCount; - int pageForChar[256]; - int bindIndex[FONT_PAGES]; - float texCoord[256][FONT_PAGES]; - int contextCount; -}; - -static Font* staticFont = NULL; -static FontInfo* staticFontInfo; -static Dar staticFontInfoDar; -static PaintStack paintStack[MAX_PAINT_STACK]; -static int staticPaintStackPos = 0; - -CEngineSurface :: CEngineSurface( Panel *embeddedPanel ):SurfaceBase( embeddedPanel ) -{ - _drawTextColor[0] = _drawTextColor[1] = _drawTextColor[2] = _drawTextColor[3] = 255; - _drawColor[0] = _drawColor[1] = _drawColor[2] = _drawColor[3] = 255; - _drawTextPos[0] = _drawTextPos[1] = _currentTexture = 0; - - staticFont = NULL; - staticFontInfo = NULL; - staticFontInfoDar.setCount( 0 ); - staticPaintStackPos = 0; - staticContextCount++; - - VGUI_InitCursors (); -} - -CEngineSurface :: ~CEngineSurface( void ) -{ - VGUI_DrawShutdown (); -} - -void CEngineSurface :: setCursor( Cursor *cursor ) -{ - _currentCursor = cursor; - VGUI_CursorSelect( cursor ); -} - -void CEngineSurface :: SetupPaintState( const PaintStack *paintState ) -{ - _translateX = paintState->iTranslateX; - _translateY = paintState->iTranslateY; - SetScissorRect( paintState->iScissorLeft, paintState->iScissorTop, paintState->iScissorRight, paintState->iScissorBottom ); - currentPanel = paintState->m_pPanel; -} - -void CEngineSurface :: InitVertex( vpoint_t &vertex, int x, int y, float u, float v ) -{ - vertex.point[0] = x + _translateX; - vertex.point[1] = y + _translateY; - vertex.coord[0] = u; - vertex.coord[1] = v; -} - -int CEngineSurface :: createNewTextureID( void ) -{ - return VGUI_GenerateTexture(); -} - -void CEngineSurface :: drawSetColor( int r, int g, int b, int a ) -{ - _drawColor[0] = r; - _drawColor[1] = g; - _drawColor[2] = b; - _drawColor[3] = a; -} - -void CEngineSurface :: drawSetTextColor( int r, int g, int b, int a ) -{ - _drawTextColor[0] = r; - _drawTextColor[1] = g; - _drawTextColor[2] = b; - _drawTextColor[3] = a; -} - -void CEngineSurface :: drawFilledRect( int x0, int y0, int x1, int y1 ) -{ - vpoint_t rect[2]; - vpoint_t clippedRect[2]; - - if( _drawColor[3] >= 255 ) return; - - InitVertex( rect[0], x0, y0, 0, 0 ); - InitVertex( rect[1], x1, y1, 0, 0 ); - - // fully clipped? - if( !ClipRect( rect[0], rect[1], &clippedRect[0], &clippedRect[1] )) - return; - - VGUI_SetupDrawingRect( _drawColor ); - VGUI_EnableTexture( false ); - VGUI_DrawQuad( &clippedRect[0], &clippedRect[1] ); - VGUI_EnableTexture( true ); -} - -void CEngineSurface :: drawOutlinedRect( int x0, int y0, int x1, int y1 ) -{ - if( _drawColor[3] >= 255 ) return; - - drawFilledRect( x0, y0, x1, y0 + 1 ); // top - drawFilledRect( x0, y1 - 1, x1, y1 ); // bottom - drawFilledRect( x0, y0 + 1, x0 + 1, y1 - 1 ); // left - drawFilledRect( x1 - 1, y0 + 1, x1, y1 - 1 ); // right -} - -void CEngineSurface :: drawSetTextFont( Font *font ) -{ - staticFont = font; - - if( font ) - { - bool buildFont = false; - - staticFontInfo = NULL; - - for( int i = 0; i < staticFontInfoDar.getCount(); i++ ) - { - if( staticFontInfoDar[i]->id == font->getId( )) - { - staticFontInfo = staticFontInfoDar[i]; - if( staticFontInfo->contextCount != staticContextCount ) - buildFont = true; - } - } - - if( !staticFontInfo || buildFont ) - { - staticFontInfo = new FontInfo; - staticFontInfo->id = 0; - staticFontInfo->pageCount = 0; - staticFontInfo->bindIndex[0] = 0; - staticFontInfo->bindIndex[1] = 0; - staticFontInfo->bindIndex[2] = 0; - staticFontInfo->bindIndex[3] = 0; - memset( staticFontInfo->pageForChar, 0, sizeof( staticFontInfo->pageForChar )); - staticFontInfo->contextCount = -1; - staticFontInfo->id = staticFont->getId(); - staticFontInfoDar.putElement( staticFontInfo ); - staticFontInfo->contextCount = staticContextCount; - - int currentPage = 0; - int x = 0, y = 0; - - memset( staticRGBA, 0, sizeof( staticRGBA )); - - for( int i = 0; i < 256; i++ ) - { - int abcA, abcB, abcC; - staticFont->getCharABCwide( i, abcA, abcB, abcC ); - - int wide = abcB; - - if( isspace( i )) continue; - - int tall = staticFont->getTall(); - - if( x + wide + 1 > FONT_SIZE ) - { - x = 0; - y += tall + 1; - } - - if( y + tall + 1 > FONT_SIZE ) - { - if( !staticFontInfo->bindIndex[currentPage] ) - staticFontInfo->bindIndex[currentPage] = createNewTextureID(); - drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); - currentPage++; - - if( currentPage == FONT_PAGES ) - break; - - memset( staticRGBA, 0, sizeof( staticRGBA )); - x = y = 0; - } - - staticFont->getCharRGBA( i, x, y, FONT_SIZE, FONT_SIZE, (byte *)staticRGBA ); - staticFontInfo->pageForChar[i] = currentPage; - staticFontInfo->texCoord[i][0] = (float)((double)x / (double)FONT_SIZE ); - staticFontInfo->texCoord[i][1] = (float)((double)y / (double)FONT_SIZE ); - staticFontInfo->texCoord[i][2] = (float)((double)(x + wide)/(double)FONT_SIZE ); - staticFontInfo->texCoord[i][3] = (float)((double)(y + tall)/(double)FONT_SIZE ); - x += wide + 1; - } - - if( currentPage != FONT_PAGES ) - { - if( !staticFontInfo->bindIndex[currentPage] ) - staticFontInfo->bindIndex[currentPage] = createNewTextureID(); - drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); - } - staticFontInfo->pageCount = currentPage + 1; - } - } -} - -void CEngineSurface :: drawSetTextPos( int x, int y ) -{ - _drawTextPos[0] = x; - _drawTextPos[1] = y; -} - -void CEngineSurface :: addCharToBuffer( const vpoint_t *ul, const vpoint_t *lr, int color[4] ) -{ - if( g_iVertexBufferEntriesUsed >= MAXVERTEXBUFFERS ) - flushBuffer(); - - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].coord[0] = ul->coord[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].coord[1] = ul->coord[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].point[0] = ul->point[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].point[1] = ul->point[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].color[0] = color[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].color[1] = color[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].color[2] = color[2]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 0].color[3] = 255 - color[3]; - - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].coord[0] = lr->coord[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].coord[1] = ul->coord[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].point[0] = lr->point[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].point[1] = ul->point[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].color[0] = color[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].color[1] = color[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].color[2] = color[2]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 1].color[3] = 255 - color[3]; - - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].coord[0] = lr->coord[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].coord[1] = lr->coord[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].point[0] = lr->point[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].point[1] = lr->point[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].color[0] = color[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].color[1] = color[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].color[2] = color[2]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 2].color[3] = 255 - color[3]; - - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].coord[0] = ul->coord[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].coord[1] = lr->coord[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].point[0] = ul->point[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].point[1] = lr->point[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].color[0] = color[0]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].color[1] = color[1]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].color[2] = color[2]; - g_VertexBuffer[g_iVertexBufferEntriesUsed + 3].color[3] = 255 - color[3]; - - g_iVertexBufferEntriesUsed += 4; -} - -void CEngineSurface :: flushBuffer( void ) -{ - if( g_iVertexBufferEntriesUsed <= 0 ) - return; - - VGUI_DrawBuffer( g_VertexBuffer, g_iVertexBufferEntriesUsed ); - g_iVertexBufferEntriesUsed = 0; -} - -void CEngineSurface :: drawPrintChar( int x, int y, int wide, int tall, float s0, float t0, float s1, float t1, int color[4] ) -{ - vpoint_t ul, lr; - - ul.point[0] = x; - ul.point[1] = y; - lr.point[0] = x + wide; - lr.point[1] = y + tall; - - // gets at the texture coords for this character in its texture page - ul.coord[0] = s0; - ul.coord[1] = t0; - lr.coord[0] = s1; - lr.coord[1] = t1; - - vpoint_t clippedRect[2]; - - if( !ClipRect( ul, lr, &clippedRect[0], &clippedRect[1] )) - return; -#if 1 - // TESTTEST: needs to be more tested - addCharToBuffer( &clippedRect[0], &clippedRect[1], color ); -#else - VGUI_SetupDrawingImage( color ); - VGUI_DrawQuad( &clippedRect[0], &clippedRect[1] ); // draw the letter -#endif -} - -void CEngineSurface :: drawPrintText( const char *text, int textLen ) -{ - static bool hasColor = 0; - static int numColor = 7; - - if( !COM_CheckString( text ) || !staticFont || !staticFontInfo ) - return; - - int x = _drawTextPos[0] + _translateX; - int y = _drawTextPos[1] + _translateY; - int tall = staticFont->getTall(); - int curTextColor[4]; - int iTotalWidth = 0; - - // HACKHACK: allow color strings in VGUI - if( numColor != 7 && vgui_colorstrings->value ) - { - for( int j = 0; j < 3; j++ ) // grab predefined color - curTextColor[j] = g_color_table[numColor][j]; - } - else - { - for( int j = 0; j < 3; j++ ) // revert default color - curTextColor[j] = _drawTextColor[j]; - } - curTextColor[3] = _drawTextColor[3]; // copy alpha - - if( textLen == 1 && vgui_colorstrings->value ) - { - if( *text == '^' ) - { - hasColor = true; - return; // skip '^' - } - else if( hasColor && isdigit( *text )) - { - numColor = ColorIndex( *text ); - hasColor = false; // handled - return; // skip colornum - } - else hasColor = false; - } - - for( int i = 0; i < textLen; i++ ) - { - int abcA, abcB, abcC; - int curCh = (byte)text[i]; - - staticFont->getCharABCwide( curCh, abcA, abcB, abcC ); - - float s0 = staticFontInfo->texCoord[curCh][0]; - float t0 = staticFontInfo->texCoord[curCh][1]; - float s1 = staticFontInfo->texCoord[curCh][2]; - float t1 = staticFontInfo->texCoord[curCh][3]; - int wide = abcB; - - iTotalWidth += abcA; - drawSetTexture( staticFontInfo->bindIndex[staticFontInfo->pageForChar[curCh]] ); - drawPrintChar( x + iTotalWidth, y, wide, tall, s0, t0, s1, t1, curTextColor ); - iTotalWidth += wide + abcC; - } - - _drawTextPos[0] += iTotalWidth; -} - -void CEngineSurface :: drawSetTextureRGBA( int id, const char* rgba, int wide, int tall ) -{ - VGUI_UploadTexture( id, rgba, wide, tall ); - _currentTexture = id; -} - -void CEngineSurface :: drawSetTexture( int id ) -{ - if( _currentTexture != id ) - { - _currentTexture = id; - flushBuffer(); - } - VGUI_BindTexture( id ); -} - -void CEngineSurface :: drawTexturedRect( int x0, int y0, int x1, int y1 ) -{ - vpoint_t rect[2]; - vpoint_t clippedRect[2]; - - // it's not a vertex, just fill rectangle - InitVertex( rect[0], x0, y0, 0, 0 ); - InitVertex( rect[1], x1, y1, 1, 1 ); - - // fully clipped? - if( !ClipRect( rect[0], rect[1], &clippedRect[0], &clippedRect[1] )) - return; - - VGUI_SetupDrawingImage( _drawColor ); - VGUI_DrawQuad( &clippedRect[0], &clippedRect[1] ); -} - -void CEngineSurface :: pushMakeCurrent( Panel* panel, bool useInsets ) -{ - int insets[4] = { 0, 0, 0, 0 }; - int absExtents[4]; - int clipRect[4]; - - if( useInsets ) - panel->getInset( insets[0], insets[1], insets[2], insets[3] ); - panel->getAbsExtents( absExtents[0], absExtents[1], absExtents[2], absExtents[3] ); - panel->getClipRect( clipRect[0], clipRect[1], clipRect[2], clipRect[3] ); - - PaintStack *paintState = &paintStack[staticPaintStackPos]; - - ASSERT( staticPaintStackPos < MAX_PAINT_STACK ); - - paintState->m_pPanel = panel; - - // determine corrected top left origin - paintState->iTranslateX = insets[0] + absExtents[0]; - paintState->iTranslateY = insets[1] + absExtents[1]; - // setup clipping rectangle for scissoring - paintState->iScissorLeft = clipRect[0]; - paintState->iScissorTop = clipRect[1]; - paintState->iScissorRight = clipRect[2]; - paintState->iScissorBottom = clipRect[3]; - - SetupPaintState( paintState ); - staticPaintStackPos++; -} - -void CEngineSurface :: popMakeCurrent( Panel *panel ) -{ - flushBuffer(); - - int top = staticPaintStackPos - 1; - - // more pops that pushes? - Assert( top >= 0 ); - - // didn't pop in reverse order of push? - Assert( paintStack[top].m_pPanel == panel ); - - staticPaintStackPos--; - - if( top > 0 ) SetupPaintState( &paintStack[top-1] ); -} \ No newline at end of file From 70518a873cff3195e4d2ba759f27fca86bbdf8fc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Oct 2018 01:53:04 +0300 Subject: [PATCH 074/205] update my Qt Creator project --- contrib/a1batross/xash3d.config | 1 + contrib/a1batross/xash3d.creator.user | 10 ++++--- contrib/a1batross/xash3d.files | 42 +++++++++++++++++---------- contrib/a1batross/xash3d.includes | 5 +--- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/contrib/a1batross/xash3d.config b/contrib/a1batross/xash3d.config index e0284f42..c69ac0ac 100644 --- a/contrib/a1batross/xash3d.config +++ b/contrib/a1batross/xash3d.config @@ -1,2 +1,3 @@ // Add predefined macros for your project here. For example: // #define THE_ANSWER 42 +#define XASH_SDL diff --git a/contrib/a1batross/xash3d.creator.user b/contrib/a1batross/xash3d.creator.user index 262b4a06..d4368eae 100644 --- a/contrib/a1batross/xash3d.creator.user +++ b/contrib/a1batross/xash3d.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -70,7 +70,7 @@ true - configure --vgui=vgui-dev --win-style-install + configure --vgui=vgui-dev --win-style-install --prefix=/home/a1ba/projects/builtXash ./waf %{buildDir} ОÑобый @@ -112,7 +112,9 @@ 2 false - + + PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + По умолчанию По умолчанию GenericProjectManager.GenericBuildConfiguration @@ -122,7 +124,7 @@ true - install --destdir=/home/a1ba/projects/builtXash + install ./waf %{buildDir} ОÑобый diff --git a/contrib/a1batross/xash3d.files b/contrib/a1batross/xash3d.files index 6f233e0b..03ccce6d 100644 --- a/contrib/a1batross/xash3d.files +++ b/contrib/a1batross/xash3d.files @@ -1,5 +1,6 @@ common/backends.h common/beamdef.h +common/boneinfo.h common/bspfile.h common/cl_entity.h common/com_model.h @@ -48,8 +49,8 @@ engine/client/avi/avi_stub.c engine/client/avi/avi_win.c engine/client/cl_cmds.c engine/client/cl_custom.c -engine/client/cl_demo.c engine/client/cl_debug.c +engine/client/cl_demo.c engine/client/cl_events.c engine/client/cl_frame.c engine/client/cl_game.c @@ -59,13 +60,13 @@ engine/client/cl_mobile.c engine/client/cl_netgraph.c engine/client/cl_parse.c engine/client/cl_pmove.c +engine/client/cl_qparse.c engine/client/cl_remap.c engine/client/cl_scrn.c engine/client/cl_tent.c engine/client/cl_tent.h engine/client/cl_video.c engine/client/cl_view.c -engine/client/cl_qparse.c engine/client/client.h engine/client/console.c engine/client/gl_alias.c @@ -95,6 +96,7 @@ engine/client/in_touch.c engine/client/input.c engine/client/input.h engine/client/keys.c +engine/client/mod_dbghulls.c engine/client/s_dsp.c engine/client/s_load.c engine/client/s_main.c @@ -107,6 +109,7 @@ engine/client/sound.h engine/client/titles.c engine/client/vgui/vgui_draw.c engine/client/vgui/vgui_draw.h +engine/client/vgui/vgui_main.h engine/client/vid_common.c engine/client/vid_common.h engine/client/vox.h @@ -145,14 +148,12 @@ engine/common/imagelib/img_wad.c engine/common/infostring.c engine/common/launcher.c engine/common/lib_common.c -engine/common/lib_posix.c engine/common/library.h +engine/common/masterlist.c engine/common/mathlib.c engine/common/mathlib.h engine/common/matrixlib.c -engine/common/masterlist.c engine/common/mod_bmodel.c -engine/common/mod_dbghulls.c engine/common/mod_local.h engine/common/mod_studio.c engine/common/model.c @@ -212,18 +213,21 @@ engine/keydefs.h engine/menu_int.h engine/mobility_int.h engine/physint.h -engine/platform/android/android_lib.c -engine/platform/android/android_lib.h -engine/platform/apple/ios_lib.c -engine/platform/apple/ios_lib.h -engine/platform/emscripten/em_lib.c -engine/platform/emscripten/em_lib.h +engine/platform/android/lib_android.c +engine/platform/android/lib_android.h +engine/platform/apple/lib_ios.c +engine/platform/apple/lib_ios.h +engine/platform/emscripten/lib_em.c +engine/platform/emscripten/lib_em.h +engine/platform/platform.h +engine/platform/posix/lib_posix.c engine/platform/sdl/events.c engine/platform/sdl/events.h -engine/platform/sdl/s_backend.c +engine/platform/sdl/in_sdl.c +engine/platform/sdl/s_sdl.c engine/platform/sdl/vid_sdl.c -engine/platform/win32/win_con.c -engine/platform/win32/win_lib.c +engine/platform/win32/con_win.c +engine/platform/win32/lib_win.c engine/progdefs.h engine/sequence.h engine/server/server.h @@ -247,8 +251,10 @@ engine/vgui_api.h engine/warpsin.h engine/wscript game_launch/game.cpp +game_launch/game.rc game_launch/wscript -mainui/wscript +mainui/Android.mk +mainui/BMPUtils.h mainui/BaseMenu.cpp mainui/BaseMenu.h mainui/Btns.cpp @@ -272,6 +278,8 @@ mainui/controls/Action.cpp mainui/controls/Action.h mainui/controls/BackgroundBitmap.cpp mainui/controls/BackgroundBitmap.h +mainui/controls/BaseClientWindow.cpp +mainui/controls/BaseClientWindow.h mainui/controls/BaseItem.cpp mainui/controls/BaseItem.h mainui/controls/BaseWindow.cpp @@ -318,6 +326,7 @@ mainui/font/BitmapFont.cpp mainui/font/BitmapFont.h mainui/font/FontManager.cpp mainui/font/FontManager.h +mainui/font/FontRenderer.h mainui/font/FreeTypeFont.cpp mainui/font/FreeTypeFont.h mainui/font/StbFont.cpp @@ -366,9 +375,12 @@ mainui/model/BaseArrayModel.h mainui/model/BaseModel.h mainui/model/StringArrayModel.h mainui/udll_int.cpp +mainui/utl/unicode_strtools.cpp +mainui/utl/unicode_strtools.h mainui/utl/utlmemory.h mainui/utl/utlrbtree.h mainui/utl/utlvector.h +mainui/wscript pm_shared/pm_defs.h pm_shared/pm_info.h pm_shared/pm_movevars.h diff --git a/contrib/a1batross/xash3d.includes b/contrib/a1batross/xash3d.includes index 04720c01..347c1e9b 100644 --- a/contrib/a1batross/xash3d.includes +++ b/contrib/a1batross/xash3d.includes @@ -6,10 +6,6 @@ engine/common engine/common/imagelib engine/common/soundlib engine/common/soundlib/libmpg -engine/platform/android -engine/platform/apple -engine/platform/emscripten -engine/platform/sdl engine/server mainui mainui/controls @@ -20,3 +16,4 @@ mainui/utl pm_shared vgui_support common +/usr/include/SDL2 From c09aff8bd72a3f510fa4a7221ff59a4c97f2204b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 27 Oct 2018 23:29:10 +0300 Subject: [PATCH 075/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 72233d02..0b89d609 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 72233d02c1a0912e8667fab1ce4561b470ce4293 +Subproject commit 0b89d6090f9c5f655c5fb5ca441b70a6bc25a45e From aae3510763732dbb26ac8f084bd1a203aeed4e24 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 27 Oct 2018 23:31:55 +0300 Subject: [PATCH 076/205] Apply 4281 update --- common/gameinfo.h | 1 + common/render_api.h | 6 +- engine/client/cl_demo.c | 55 +++++---- engine/client/cl_frame.c | 62 +++++++++-- engine/client/cl_gameui.c | 4 +- engine/client/cl_main.c | 41 +++---- engine/client/cl_pmove.c | 2 +- engine/client/cl_qparse.c | 130 ++++++++++++++++++++-- engine/client/cl_remap.c | 4 +- engine/client/cl_scrn.c | 20 ++-- engine/client/cl_tent.c | 31 ++++-- engine/client/cl_view.c | 4 +- engine/client/client.h | 2 + engine/client/gl_alias.c | 46 +++----- engine/client/gl_backend.c | 4 +- engine/client/gl_image.c | 35 ++++-- engine/client/gl_local.h | 10 +- engine/client/gl_rmain.c | 18 +-- engine/client/gl_rmisc.c | 117 +------------------ engine/client/gl_sprite.c | 15 +-- engine/client/gl_studio.c | 9 +- engine/client/gl_vidnt.c | 26 +++-- engine/client/gl_warp.c | 2 +- engine/client/s_load.c | 14 ++- engine/client/s_main.c | 94 ++++++++++++++-- engine/client/sound.h | 5 + engine/client/vgui/vgui_int.cpp | 2 +- engine/common/avikit.c | 24 +++- engine/common/build.c | 2 +- engine/common/cfgscript.c | 30 ++--- engine/common/cmd.c | 16 +-- engine/common/common.h | 37 ++---- engine/common/con_utils.c | 11 +- engine/common/console.c | 20 ++-- engine/common/crtlib.c | 1 - engine/common/cvar.c | 12 ++ engine/common/cvar.h | 1 + engine/common/filesystem.c | 118 ++++++++++++-------- engine/common/host.c | 15 +-- engine/common/hpak.c | 92 +++++++-------- engine/common/imagelib/img_bmp.c | 10 +- engine/common/imagelib/img_dds.c | 11 +- engine/common/imagelib/img_tga.c | 13 +-- engine/common/imagelib/img_utils.c | 99 +++++------------ engine/common/imagelib/img_wad.c | 28 +---- engine/common/input.c | 2 +- engine/common/keys.c | 15 ++- engine/common/mod_bmodel.c | 10 +- engine/common/model.c | 11 +- engine/common/net_ws.c | 32 +++--- engine/common/soundlib/snd_main.c | 2 +- engine/common/soundlib/snd_mp3.c | 16 +-- engine/common/soundlib/snd_wav.c | 26 ++--- engine/common/sys_win.c | 35 ------ engine/common/system.h | 1 - engine/keydefs.h | 1 + engine/server/server.h | 1 + engine/server/sv_client.c | 15 ++- engine/server/sv_cmds.c | 43 ++++++- engine/server/sv_game.c | 173 ++++++++++++++++------------- engine/server/sv_init.c | 1 + engine/server/sv_phys.c | 7 +- 62 files changed, 910 insertions(+), 780 deletions(-) diff --git a/common/gameinfo.h b/common/gameinfo.h index 511b3718..aacc74d3 100644 --- a/common/gameinfo.h +++ b/common/gameinfo.h @@ -17,6 +17,7 @@ GNU General Public License for more details. #define GAMEINFO_H #define GFL_NOMODELS (1<<0) +#define GFL_NOSKILLS (1<<1) /* ======================================================================== diff --git a/common/render_api.h b/common/render_api.h index b6b8f462..77c8bedf 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -79,7 +79,7 @@ typedef enum TF_KEEP_SOURCE = (1<<1), // some images keep source TF_NOFLIP_TGA = (1<<2), // Steam background completely ignore tga attribute 0x20 TF_EXPAND_SOURCE = (1<<3), // Don't keep source as 8-bit expand to RGBA -// reserved + TF_ALLOW_EMBOSS = (1<<4), // Allow emboss-mapping for this image TF_RECTANGLE = (1<<5), // this is GL_TEXTURE_RECTANGLE TF_CUBEMAP = (1<<6), // it's cubemap texture TF_DEPTHMAP = (1<<7), // custom texture filter used @@ -183,16 +183,16 @@ typedef struct render_api_s void (*R_EntityRemoveDecals)( struct model_s *mod ); // remove all the decals from specified entity (BSP only) // AVIkit support - void *(*AVI_LoadVideo)( const char *filename ); + void *(*AVI_LoadVideo)( const char *filename, qboolean load_audio ); int (*AVI_GetVideoInfo)( void *Avi, long *xres, long *yres, float *duration ); long (*AVI_GetVideoFrameNumber)( void *Avi, float time ); byte *(*AVI_GetVideoFrame)( void *Avi, long frame ); void (*AVI_UploadRawFrame)( int texture, int cols, int rows, int width, int height, const byte *data ); void (*AVI_FreeVideo)( void *Avi ); int (*AVI_IsActive)( void *Avi ); + void (*AVI_StreamSound)( void *Avi, int entnum, float fvol, float attn, float synctime ); void (*AVI_Reserved0)( void ); // for potential interface expansion without broken compatibility void (*AVI_Reserved1)( void ); - void (*AVI_Reserved2)( void ); // glState related calls (must use this instead of normal gl-calls to prevent de-synchornize local states between engine and the client) void (*GL_Bind)( int tmu, unsigned int texnum ); diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index c37cba7e..f1ffbc27 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -725,7 +725,7 @@ void CL_DemoCompleted( void ) CL_StopPlayback(); - if( !CL_NextDemo() && host_developer.value <= DEV_NONE ) + if( !CL_NextDemo() && !cls.changedemo ) UI_SetActiveMenu( true ); Cvar_SetValue( "v_dark", 0.0f ); @@ -795,6 +795,8 @@ qboolean CL_ReadRawNetworkData( byte *buffer, size_t *length ) } } + cls.netchan.last_received = host.realtime; + cls.netchan.total_received += msglen; *length = msglen; if( cls.state != ca_active ) @@ -812,6 +814,7 @@ reads demo data and write it to client */ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) { + vec3_t viewangles; int msglen = 0; demoangle_t *a; @@ -841,10 +844,12 @@ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) // get the next message FS_Read( cls.demofile, &msglen, sizeof( int )); - FS_Read( cls.demofile, &cl.viewangles[0], sizeof( float )); - FS_Read( cls.demofile, &cl.viewangles[1], sizeof( float )); - FS_Read( cls.demofile, &cl.viewangles[2], sizeof( float )); + FS_Read( cls.demofile, &viewangles[0], sizeof( float )); + FS_Read( cls.demofile, &viewangles[1], sizeof( float )); + FS_Read( cls.demofile, &viewangles[2], sizeof( float )); cls.netchan.incoming_sequence++; + demo.timestamp = cl.mtime[0]; + cl.skip_interp = false; // make sure what interp info contain angles from different frames // or lerping will stop working @@ -856,7 +861,7 @@ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) // record update a->starttime = demo.timestamp; - VectorCopy( cl.viewangles, a->viewangles ); + VectorCopy( viewangles, a->viewangles ); demo.lasttime = demo.timestamp; } @@ -884,6 +889,8 @@ qboolean CL_DemoReadMessageQuake( byte *buffer, size_t *length ) } } + cls.netchan.last_received = host.realtime; + cls.netchan.total_received += msglen; *length = msglen; if( cls.state != ca_active ) @@ -1073,14 +1080,26 @@ but viewangles interpolate here */ void CL_DemoInterpolateAngles( void ) { - float curtime = (CL_GetDemoPlaybackClock() - demo.starttime) - host.frametime; demoangle_t *prev = NULL, *next = NULL; float frac = 0.0f; + float curtime; - if( curtime > demo.timestamp ) - curtime = demo.timestamp; // don't run too far + if( cls.demoplayback == DEMO_QUAKE1 ) + { + // manually select next & prev states + next = &demo.cmds[(demo.angle_position - 0) & ANGLE_MASK]; + prev = &demo.cmds[(demo.angle_position - 1) & ANGLE_MASK]; + if( cl.skip_interp ) *prev = *next; // camera was teleported + frac = cl.lerpFrac; + } + else + { + curtime = (CL_GetDemoPlaybackClock() - demo.starttime) - host.frametime; + if( curtime > demo.timestamp ) + curtime = demo.timestamp; // don't run too far - CL_DemoFindInterpolatedViewAngles( curtime, &frac, &prev, &next ); + CL_DemoFindInterpolatedViewAngles( curtime, &frac, &prev, &next ); + } if( prev && next ) { @@ -1091,7 +1110,7 @@ void CL_DemoInterpolateAngles( void ) QuaternionSlerp( q2, q1, frac, q ); QuaternionAngle( q, cl.viewangles ); } - else if( cls.demoplayback != DEMO_QUAKE1 ) + else if( cl.cmd != NULL ) VectorCopy( cl.cmd->viewangles, cl.viewangles ); } @@ -1295,6 +1314,7 @@ void CL_CheckStartupDemos( void ) // run demos loop in background mode Cvar_SetValue( "v_dark", 1.0f ); + cls.demos_pending = false; cls.demonum = 0; CL_NextDemo (); } @@ -1304,11 +1324,10 @@ void CL_CheckStartupDemos( void ) CL_DemoGetName ================== */ -void CL_DemoGetName( int lastnum, char *filename ) +static void CL_DemoGetName( int lastnum, char *filename ) { int a, b, c, d; - if( !filename ) return; if( lastnum < 0 || lastnum > 9999 ) { // bound @@ -1584,17 +1603,15 @@ void CL_Demos_f( void ) return; } + // demos loop are not running + if( cls.olddemonum == -1 ) + return; + cls.demonum = cls.olddemonum; - if( cls.demonum == -1 ) - cls.demonum = 0; - + // run demos loop in background mode if( !SV_Active() && !cls.demoplayback ) - { - // run demos loop in background mode - cls.changedemo = true; CL_NextDemo (); - } } diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 661059fc..a968d75b 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -96,7 +96,7 @@ we don't want interpolate this */ qboolean CL_EntityTeleported( cl_entity_t *ent ) { - int len, maxlen; + float len, maxlen; vec3_t delta; VectorSubtract( ent->curstate.origin, ent->prevstate.origin, delta ); @@ -407,7 +407,21 @@ int CL_InterpolateModel( cl_entity_t *e ) VectorCopy( e->curstate.origin, e->origin ); VectorCopy( e->curstate.angles, e->angles ); - if( cls.timedemo || !e->model || cl.maxclients <= 1 ) + if( cls.timedemo || !e->model ) + return 1; + + if( cls.demoplayback == DEMO_QUAKE1 ) + { + // quake lerping is easy + VectorLerp( e->prevstate.origin, cl.lerpFrac, e->curstate.origin, e->origin ); + AngleQuaternion( e->prevstate.angles, q1, false ); + AngleQuaternion( e->curstate.angles, q2, false ); + QuaternionSlerp( q1, q2, cl.lerpFrac, q ); + QuaternionAngle( q, e->angles ); + return 1; + } + + if( cl.maxclients <= 1 ) return 1; if( e->model->type == mod_brush && !cl_bmodelinterp->value ) @@ -474,12 +488,24 @@ interpolate non-local clients void CL_ComputePlayerOrigin( cl_entity_t *ent ) { float targettime; + vec4_t q, q1, q2; vec3_t origin; vec3_t angles; if( !ent->player || ent->index == ( cl.playernum + 1 )) return; + if( cls.demoplayback == DEMO_QUAKE1 ) + { + // quake lerping is easy + VectorLerp( ent->prevstate.origin, cl.lerpFrac, ent->curstate.origin, ent->origin ); + AngleQuaternion( ent->prevstate.angles, q1, false ); + AngleQuaternion( ent->curstate.angles, q2, false ); + QuaternionSlerp( q1, q2, cl.lerpFrac, q ); + QuaternionAngle( q, ent->angles ); + return; + } + targettime = cl.time - cl_interp->value; CL_PureOrigin( ent, targettime, origin, angles ); @@ -985,9 +1011,12 @@ void CL_LinkPlayers( frame_t *frame ) if( i == cl.playernum ) { - VectorCopy( state->origin, ent->origin ); - VectorCopy( state->origin, ent->prevstate.origin ); - VectorCopy( state->origin, ent->curstate.origin ); + if( cls.demoplayback != DEMO_QUAKE1 ) + { + VectorCopy( state->origin, ent->origin ); + VectorCopy( state->origin, ent->prevstate.origin ); + VectorCopy( state->origin, ent->curstate.origin ); + } VectorCopy( ent->curstate.angles, ent->angles ); } @@ -1003,6 +1032,8 @@ void CL_LinkPlayers( frame_t *frame ) if ( i == cl.playernum ) { + if( cls.demoplayback == DEMO_QUAKE1 ) + VectorLerp( ent->prevstate.origin, cl.lerpFrac, ent->curstate.origin, cl.simorg ); VectorCopy( cl.simorg, ent->origin ); } else @@ -1321,8 +1352,25 @@ qboolean CL_GetEntitySpatialization( channel_t *ch ) qboolean CL_GetMovieSpatialization( rawchan_t *ch ) { - // UNDONE - return false; + cl_entity_t *ent; + qboolean valid_origin; + + valid_origin = VectorIsNull( ch->origin ) ? false : true; + ent = CL_GetEntityByIndex( ch->entnum ); + + // entity is not present on the client but has valid origin + if( !ent || !ent->index || ent->curstate.messagenum == 0 ) + return valid_origin; + + // setup origin + VectorAverage( ent->curstate.mins, ent->curstate.maxs, ch->origin ); + VectorAdd( ch->origin, ent->curstate.origin, ch->origin ); + + // setup radius + if( ent->model != NULL && ent->model->radius ) ch->radius = ent->model->radius; + else ch->radius = RadiusFromBounds( ent->curstate.mins, ent->curstate.maxs ); + + return true; } void CL_ExtraUpdate( void ) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index dce138b9..7bfa0a31 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -260,6 +260,8 @@ static void UI_ConvertGameInfo( GAMEINFO *out, gameinfo_t *in ) if( in->nomodels ) out->flags |= GFL_NOMODELS; + if( in->noskills ) + out->flags |= GFL_NOSKILLS; } static qboolean PIC_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ) @@ -386,7 +388,7 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long im SetBits( flags, TF_IMAGE ); Image_SetForceFlags( IL_LOAD_DECAL ); // allow decal images for menu - tx = GL_LoadTexture( szPicName, image_buf, image_size, flags, NULL ); + tx = GL_LoadTexture( szPicName, image_buf, image_size, flags ); Image_ClearForceFlags(); return tx; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index a156a8b4..73dd1c41 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -913,16 +913,10 @@ void CL_BeginUpload_f( void ) name = Cmd_Argv( 1 ); if( !COM_CheckString( name )) - { - MsgDev( D_ERROR, "upload without filename\n" ); return; - } if( !cl_allow_upload.value ) - { - MsgDev( D_WARN, "ingoring decal upload ( cl_allow_upload is 0 )\n" ); return; - } if( Q_strlen( name ) != 36 || Q_strnicmp( name, "!MD5", 4 )) { @@ -937,7 +931,7 @@ void CL_BeginUpload_f( void ) { if( memcmp( md5, custResource.rgucMD5_hash, 16 )) { - MsgDev( D_REPORT, "Bogus data retrieved from %s, attempting to delete entry\n", CUSTOM_RES_PATH ); + Con_Reportf( "Bogus data retrieved from %s, attempting to delete entry\n", CUSTOM_RES_PATH ); HPAK_RemoveLump( CUSTOM_RES_PATH, &custResource ); return; } @@ -954,26 +948,22 @@ void CL_BeginUpload_f( void ) if( memcmp( custResource.rgucMD5_hash, md5, 16 )) { - MsgDev( D_REPORT, "HPAK_AddLump called with bogus lump, md5 mismatch\n" ); - MsgDev( D_REPORT, "Purported: %s\n", MD5_Print( custResource.rgucMD5_hash ) ); - MsgDev( D_REPORT, "Actual : %s\n", MD5_Print( md5 ) ); - MsgDev( D_REPORT, "Removing conflicting lump\n" ); + Con_Reportf( "HPAK_AddLump called with bogus lump, md5 mismatch\n" ); + Con_Reportf( "Purported: %s\n", MD5_Print( custResource.rgucMD5_hash ) ); + Con_Reportf( "Actual : %s\n", MD5_Print( md5 ) ); + Con_Reportf( "Removing conflicting lump\n" ); HPAK_RemoveLump( CUSTOM_RES_PATH, &custResource ); return; } } } - if( buf && size ) + if( buf && size > 0 ) { Netchan_CreateFileFragmentsFromBuffer( &cls.netchan, name, buf, size ); Netchan_FragSend( &cls.netchan ); Mem_Free( buf ); } - else - { - MsgDev( D_REPORT, "ingoring customization upload, couldn't find decal locally\n" ); - } } /* @@ -1103,7 +1093,6 @@ void CL_CheckForResend( void ) if( !NET_StringToAdr( cls.servername, &adr )) { - MsgDev( D_ERROR, "CL_CheckForResend: bad server address\n" ); CL_Disconnect(); return; } @@ -1111,7 +1100,7 @@ void CL_CheckForResend( void ) // only retry so many times before failure. if( cls.connect_retry >= CL_CONNECTION_RETRIES ) { - MsgDev( D_ERROR, "CL_CheckForResend: couldn't connected\n" ); + Con_DPrintf( S_ERROR "CL_CheckForResend: couldn't connected\n" ); CL_Disconnect(); return; } @@ -1493,12 +1482,15 @@ void CL_InternetServers_f( void ) int remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST ); netadr_t adr; - Con_Printf( "Scanning for servers on the internet area...\n" ); NET_Config( true ); // allow remote if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) ) - MsgDev( D_ERROR, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); + { + Con_DPrintf( S_ERROR "Can't resolve adr: %s\n", MASTERSERVER_ADR ); + return; + } + Con_Printf( "Scanning for servers on the internet area...\n" ); Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version @@ -1788,7 +1780,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { if( cls.state == ca_connected ) { - MsgDev( D_ERROR, "dup connect received. ignored\n"); + Con_DPrintf( S_ERROR "dup connect received. ignored\n"); return; } @@ -1972,7 +1964,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // user out of band message (must be handled in CL_ConnectionlessPacket) if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf ); } - else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); + else Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } /* @@ -2021,14 +2013,14 @@ void CL_ReadNetMessage( void ) if( !cls.demoplayback && MSG_GetMaxBytes( &net_message ) < 8 ) { - MsgDev( D_WARN, "%s: runt packet\n", NET_AdrToString( net_from )); + Con_Printf( S_WARN "CL_ReadPackets: %s:runt packet\n", NET_AdrToString( net_from )); continue; } // packet from server if( !cls.demoplayback && !NET_CompareAdr( net_from, cls.netchan.remote_address )) { - MsgDev( D_ERROR, "CL_ReadPackets: %s:sequenced packet without connection\n", NET_AdrToString( net_from )); + Con_DPrintf( S_ERROR "CL_ReadPackets: %s:sequenced packet without connection\n", NET_AdrToString( net_from )); continue; } @@ -2516,7 +2508,6 @@ qboolean CL_PrecacheResources( void ) CL_SetEventIndex( cl.event_precache[pRes->nIndex], pRes->nIndex ); break; default: - MsgDev( D_REPORT, "unknown resource type\n" ); break; } diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index dc76da16..a5618af1 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -1239,7 +1239,7 @@ void CL_PredictMovement( qboolean repredicting ) if( cls.state != ca_active || cls.spectator ) return; - if( cls.demoplayback && cl.cmd != NULL && !repredicting ) + if( cls.demoplayback && !repredicting ) CL_DemoInterpolateAngles(); CL_SetUpPlayerPrediction( false, false ); diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c index 912a9b70..f669ff2c 100644 --- a/engine/client/cl_qparse.c +++ b/engine/client/cl_qparse.c @@ -40,6 +40,7 @@ GNU General Public License for more details. #define STAT_MONSTERS 14 // bumped by svc_killedmonster #define MAX_STATS 32 +static char cmd_buf[8192]; static char msg_buf[8192]; static sizebuf_t msg_demo; @@ -69,6 +70,28 @@ static void CL_ParseQuakeStats( sizebuf_t *msg ) CL_DispatchQuakeMessage( "Stats" ); } +/* +================== +CL_EntityTeleported + +check for instant movement in case +we don't want interpolate this +================== +*/ +static qboolean CL_QuakeEntityTeleported( cl_entity_t *ent, entity_state_t *newstate ) +{ + float len, maxlen; + vec3_t delta; + + VectorSubtract( newstate->origin, ent->prevstate.origin, delta ); + + // compute potential max movement in units per frame and compare with entity movement + maxlen = ( clgame.movevars.maxvelocity * ( 1.0 / GAME_FPS )); + len = VectorLength( delta ); + + return (len > maxlen); +} + /* ================== CL_ParseQuakeStats @@ -304,6 +327,7 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) clgame.movevars.waveHeight = 0.0f; clgame.movevars.zmax = 14172.0f; // 8192 * 1.74 clgame.movevars.gravity = 800.0f; // quake doesn't write gravity in demos + clgame.movevars.maxvelocity = 2000.0f; memcpy( &clgame.oldmovevars, &clgame.movevars, sizeof( movevars_t )); } @@ -501,7 +525,16 @@ void CL_ParseQuakeEntityData( sizebuf_t *msg, int bits ) } if( FBitSet( bits, U_NOLERP )) + state->movetype = MOVETYPE_STEP; + else state->movetype = MOVETYPE_NOCLIP; + + if( CL_QuakeEntityTeleported( ent, state )) + { + // remove smooth stepping + if( cl.viewentity == ent->index ) + cl.skip_interp = true; forcelink = true; + } if( FBitSet( state->effects, 16 )) SetBits( state->effects, EF_NODRAW ); @@ -511,6 +544,10 @@ void CL_ParseQuakeEntityData( sizebuf_t *msg, int bits ) if( forcelink ) { + VectorCopy( state->origin, ent->baseline.vuser1 ); + + SetBits( state->effects, EF_NOINTERP ); + // interpolation must be reset SETVISBIT( frame->flags, pack ); @@ -697,6 +734,9 @@ static void CL_ParseQuakeTempEntity( sizebuf_t *msg ) MSG_WriteByte( &msg_demo, type ); + if( type == 17 ) + MSG_WriteString( &msg_demo, MSG_ReadString( msg )); + // TE_LIGHTNING1, TE_LIGHTNING2, TE_LIGHTNING3, TE_BEAM, TE_LIGHTNING4 if( type == 5 || type == 6 || type == 9 || type == 13 || type == 17 ) MSG_WriteWord( &msg_demo, MSG_ReadWord( msg )); @@ -722,9 +762,6 @@ static void CL_ParseQuakeTempEntity( sizebuf_t *msg ) MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); } - if( type == 17 ) - MSG_WriteString( &msg_demo, MSG_ReadString( msg )); - // TE_SMOKE (nehahra) if( type == 18 ) MSG_WriteByte( &msg_demo, MSG_ReadByte( msg )); @@ -744,7 +781,7 @@ static void CL_ParseQuakeSignon( sizebuf_t *msg ) int i = MSG_ReadByte( msg ); if( i == 3 ) cls.signon = SIGNONS - 1; - Con_Printf( "CL_Signon: %d\n", i ); + Con_Reportf( "CL_Signon: %d\n", i ); } /* @@ -776,6 +813,75 @@ static void CL_ParseNehahraHideLMP( sizebuf_t *msg ) CL_DispatchQuakeMessage( "Stats" ); } +/* +================== +CL_QuakeStuffText + +================== +*/ +void CL_QuakeStuffText( const char *text ) +{ + Q_strncat( cmd_buf, text, sizeof( cmd_buf )); + Cbuf_AddText( text ); +} + +/* +================== +CL_QuakeExecStuff + +================== +*/ +void CL_QuakeExecStuff( void ) +{ + char *text = cmd_buf; + char token[256]; + int argc = 0; + + // check if no commands this frame + if( !COM_CheckString( text )) + return; + + while( 1 ) + { + // skip whitespace up to a /n + while( *text && ((byte)*text) <= ' ' && *text != '\r' && *text != '\n' ) + text++; + + if( *text == '\n' || *text == '\r' ) + { + // a newline seperates commands in the buffer + if( *text == '\r' && text[1] == '\n' ) + text++; + argc = 0; + text++; + } + + if( !*text ) break; + + host.com_ignorebracket = true; + text = COM_ParseFile( text, token ); + host.com_ignorebracket = false; + + if( !text ) break; + + if( argc == 0 ) + { + // debug: find all missed commands and cvars to add them into QWrap + if( !Cvar_Exists( token ) && !Cmd_Exists( token )) + Con_Printf( S_WARN "'%s' is not exist\n", token ); +// else Msg( "cmd: %s\n", token ); + + // process some special commands + if( !Q_stricmp( token, "playdemo" )) + cls.changedemo = true; + argc++; + } + } + + // reset the buffer + cmd_buf[0] = '\0'; +} + /* ================== CL_ParseQuakeMessage @@ -861,16 +967,15 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; break; case svc_time: + Cbuf_AddText( "\n" ); // new frame was started CL_ParseServerTime( msg ); break; case svc_print: - Con_Printf( "%s", MSG_ReadString( msg )); + str = MSG_ReadString( msg ); + Con_Printf( "%s%s", str, *str == 2 ? "\n" : "" ); break; case svc_stufftext: - // FIXME: do revision for all Quake and Nehahra console commands - str = MSG_ReadString( msg ); - Msg( "%s\n", str ); - Cbuf_AddText( str ); + CL_QuakeStuffText( MSG_ReadString( msg )); break; case svc_setangle: cl.viewangles[0] = MSG_ReadAngle( msg ); @@ -909,8 +1014,8 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) case svc_updatecolors: param1 = MSG_ReadByte( msg ); param2 = MSG_ReadByte( msg ); - cl.players[param1].topcolor = param2 & 0xF0; - cl.players[param1].bottomcolor = (param2 & 15) << 4; + cl.players[param1].topcolor = param2 & 0xF; + cl.players[param1].bottomcolor = (param2 & 0xF0) >> 4; break; case svc_particle: CL_ParseQuakeParticle( msg ); @@ -1018,4 +1123,7 @@ void CL_ParseQuakeMessage( sizebuf_t *msg, qboolean normal_message ) // add new entities into physic lists CL_SetSolidEntities(); + + // check deferred cmds + CL_QuakeExecStuff(); } \ No newline at end of file diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c index f445ef7a..dfa7af55 100644 --- a/engine/client/cl_remap.c +++ b/engine/client/cl_remap.c @@ -126,7 +126,7 @@ void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomco memcpy( paletteBackup, pal, 768 ); raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor ); - ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR, NULL ); // do copy + ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR ); // do copy // restore original palette memcpy( pal, paletteBackup, 768 ); @@ -213,7 +213,7 @@ void CL_UpdateAliasTexture( unsigned short *texture, int skinnum, int topcolor, if( *texture == 0 ) { - Q_snprintf( texname, sizeof( texname ), "%s:remap%i", RI.currentmodel->name, skinnum ); + Q_snprintf( texname, sizeof( texname ), "%s:remap%i_%i", RI.currentmodel->name, skinnum, RI.currententity->index ); skin.width = tx->width; skin.height = tx->height; skin.depth = skin.numMips = 1; diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index cc600be3..c5b89bc6 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -296,7 +296,7 @@ void SCR_DrawPlaque( void ) { if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background ) { - int levelshot = GL_LoadTexture( cl_levelshot_name->string, NULL, 0, TF_IMAGE, NULL ); + int levelshot = GL_LoadTexture( cl_levelshot_name->string, NULL, 0, TF_IMAGE ); GL_SetRenderMode( kRenderNormal ); R_DrawStretchPic( 0, 0, glState.width, glState.height, 0, 0, 1, 1, levelshot ); if( !cl.background ) CL_DrawHUD( CL_LOADING ); @@ -496,7 +496,7 @@ qboolean SCR_LoadFixedWidthFont( const char *fontname ) if( !FS_FileExists( fontname, false )) return false; - cls.creditsFont.hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE, NULL ); + cls.creditsFont.hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE ); R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = fontWidth / 16; cls.creditsFont.type = FONT_FIXED; @@ -528,7 +528,7 @@ qboolean SCR_LoadVariableWidthFont( const char *fontname ) if( !FS_FileExists( fontname, false )) return false; - cls.creditsFont.hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_IMAGE, NULL ); + cls.creditsFont.hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_IMAGE ); R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); // half-life font with variable chars witdh @@ -626,24 +626,24 @@ void SCR_RegisterTextures( void ) // register gfx.wad images if( FS_FileExists( "gfx/paused.lmp", false )) - cls.pauseIcon = GL_LoadTexture( "gfx/paused.lmp", NULL, 0, TF_IMAGE, NULL ); + cls.pauseIcon = GL_LoadTexture( "gfx/paused.lmp", NULL, 0, TF_IMAGE ); else if( FS_FileExists( "gfx/pause.lmp", false )) - cls.pauseIcon = GL_LoadTexture( "gfx/pause.lmp", NULL, 0, TF_IMAGE, NULL ); + cls.pauseIcon = GL_LoadTexture( "gfx/pause.lmp", NULL, 0, TF_IMAGE ); if( FS_FileExists( "gfx/lambda.lmp", false )) { if( cl_allow_levelshots->value ) - cls.loadingBar = GL_LoadTexture( "gfx/lambda.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE, NULL ); - else cls.loadingBar = GL_LoadTexture( "gfx/lambda.lmp", NULL, 0, TF_IMAGE, NULL ); + cls.loadingBar = GL_LoadTexture( "gfx/lambda.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE ); + else cls.loadingBar = GL_LoadTexture( "gfx/lambda.lmp", NULL, 0, TF_IMAGE ); } else if( FS_FileExists( "gfx/loading.lmp", false )) { if( cl_allow_levelshots->value ) - cls.loadingBar = GL_LoadTexture( "gfx/loading.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE, NULL ); - else cls.loadingBar = GL_LoadTexture( "gfx/loading.lmp", NULL, 0, TF_IMAGE, NULL ); + cls.loadingBar = GL_LoadTexture( "gfx/loading.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE ); + else cls.loadingBar = GL_LoadTexture( "gfx/loading.lmp", NULL, 0, TF_IMAGE ); } - cls.tileImage = GL_LoadTexture( "gfx/backtile.lmp", NULL, 0, TF_NOMIPMAP, NULL ); + cls.tileImage = GL_LoadTexture( "gfx/backtile.lmp", NULL, 0, TF_NOMIPMAP ); } /* diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 6202f33f..b23ad52c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2838,7 +2838,7 @@ void CL_AddEntityEffects( cl_entity_t *ent ) } // studio models are handle muzzleflashes difference - if( FBitSet( ent->curstate.effects, EF_MUZZLEFLASH ) && ent->model->type == mod_alias ) + if( FBitSet( ent->curstate.effects, EF_MUZZLEFLASH ) && Mod_AliasExtradata( ent->model )) { dlight_t *dl = CL_AllocDlight( ent->index ); vec3_t fv; @@ -2864,6 +2864,7 @@ these effects will be enable by flag in model header */ void CL_AddModelEffects( cl_entity_t *ent ) { + vec3_t neworigin; vec3_t oldorigin; if( !ent->model ) return; @@ -2876,23 +2877,33 @@ void CL_AddModelEffects( cl_entity_t *ent ) default: return; } - VectorCopy( ent->prevstate.origin, oldorigin ); + if( cls.demoplayback == DEMO_QUAKE1 ) + { + VectorCopy( ent->baseline.vuser1, oldorigin ); + VectorCopy( ent->origin, ent->baseline.vuser1 ); + VectorCopy( ent->origin, neworigin ); + } + else + { + VectorCopy( ent->prevstate.origin, oldorigin ); + VectorCopy( ent->curstate.origin, neworigin ); + } // NOTE: this completely over control about angles and don't broke interpolation if( FBitSet( ent->model->flags, STUDIO_ROTATE )) ent->angles[1] = anglemod( 100.0f * cl.time ); if( FBitSet( ent->model->flags, STUDIO_GIB )) - R_RocketTrail( oldorigin, ent->curstate.origin, 2 ); + R_RocketTrail( oldorigin, neworigin, 2 ); if( FBitSet( ent->model->flags, STUDIO_ZOMGIB )) - R_RocketTrail( oldorigin, ent->curstate.origin, 4 ); + R_RocketTrail( oldorigin, neworigin, 4 ); if( FBitSet( ent->model->flags, STUDIO_TRACER )) - R_RocketTrail( oldorigin, ent->curstate.origin, 3 ); + R_RocketTrail( oldorigin, neworigin, 3 ); if( FBitSet( ent->model->flags, STUDIO_TRACER2 )) - R_RocketTrail( oldorigin, ent->curstate.origin, 5 ); + R_RocketTrail( oldorigin, neworigin, 5 ); if( FBitSet( ent->model->flags, STUDIO_ROCKET )) { @@ -2908,14 +2919,14 @@ void CL_AddModelEffects( cl_entity_t *ent ) dl->die = cl.time + 0.01f; - R_RocketTrail( oldorigin, ent->curstate.origin, 0 ); + R_RocketTrail( oldorigin, neworigin, 0 ); } if( FBitSet( ent->model->flags, STUDIO_GRENADE )) - R_RocketTrail( oldorigin, ent->curstate.origin, 1 ); + R_RocketTrail( oldorigin, neworigin, 1 ); if( FBitSet( ent->model->flags, STUDIO_TRACER3 )) - R_RocketTrail( oldorigin, ent->curstate.origin, 6 ); + R_RocketTrail( oldorigin, neworigin, 6 ); } /* @@ -3053,7 +3064,7 @@ int CL_DecalIndex( int id ) if( cl.decal_index[id] == 0 ) { Image_SetForceFlags( IL_LOAD_DECAL ); - cl.decal_index[id] = GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL, NULL ); + cl.decal_index[id] = GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL ); Image_ClearForceFlags(); } diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 78ab8722..7db22f67 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -19,6 +19,7 @@ GNU General Public License for more details. #include "entity_types.h" #include "gl_local.h" #include "vgui_draw.h" +#include "sound.h" /* =============== @@ -143,7 +144,7 @@ void V_SetRefParams( ref_params_t *fd ) fd->demoplayback = cls.demoplayback; fd->hardware = 1; // OpenGL - if( cl.first_frame ) + if( cl.first_frame || cl.skip_interp ) { cl.first_frame = false; // now can be unlocked fd->smoothing = true; // NOTE: currently this used to prevent ugly un-duck effect while level is changed @@ -334,6 +335,7 @@ void V_RenderView( void ) } R_RenderFrame( &rvp ); + S_UpdateFrame( &rvp ); viewnum++; } while( rp.nextView ); diff --git a/engine/client/client.h b/engine/client/client.h index a5b3ad0f..385e18dc 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -221,6 +221,7 @@ typedef struct qboolean background; // not real game, just a background qboolean first_frame; // first rendering frame qboolean proxy_redirect; // spectator stuff + qboolean skip_interp; // skip interpolation this frame uint checksum; // for catching cheater maps @@ -1034,6 +1035,7 @@ void Con_FastClose( void ); // s_main.c // void S_StreamRawSamples( int samples, int rate, int width, int channels, const byte *data ); +void S_StreamAviSamples( void *Avi, int entnum, float fvol, float attn, float synctime ); void S_StartBackgroundTrack( const char *intro, const char *loop, long position, qboolean fullpath ); void S_StopBackgroundTrack( void ); void S_StreamSetPause( int pause ); diff --git a/engine/client/gl_alias.c b/engine/client/gl_alias.c index e7df65d8..35cfe0dc 100644 --- a/engine/client/gl_alias.c +++ b/engine/client/gl_alias.c @@ -618,10 +618,9 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) daliashdr_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; - int numframes, size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; - int i, j; + int i, j, size; if( loaded ) *loaded = false; pinmodel = (daliashdr_t *)buffer; @@ -629,10 +628,13 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) if( i != ALIAS_VERSION ) { - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", mod->name, i, ALIAS_VERSION ); + Con_DPrintf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, ALIAS_VERSION ); return; } + if( pinmodel->numverts <= 0 || pinmodel->numtris <= 0 || pinmodel->numframes <= 0 ) + return; // how to possible is make that? + mod->mempool = Mem_AllocPool( va( "^2%s^7", mod->name )); // allocate space for a working header, plus all the data except the frames, @@ -648,33 +650,12 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) m_pAliasHeader->skinwidth = pinmodel->skinwidth; m_pAliasHeader->skinheight = pinmodel->skinheight; m_pAliasHeader->numverts = pinmodel->numverts; - - if( m_pAliasHeader->numverts <= 0 ) - { - MsgDev( D_ERROR, "model %s has no vertices\n", mod->name ); - return; - } + m_pAliasHeader->numtris = pinmodel->numtris; + m_pAliasHeader->numframes = pinmodel->numframes; if( m_pAliasHeader->numverts > MAXALIASVERTS ) { - MsgDev( D_ERROR, "model %s has too many vertices\n", mod->name ); - return; - } - - m_pAliasHeader->numtris = pinmodel->numtris; - - if( m_pAliasHeader->numtris <= 0 ) - { - MsgDev( D_ERROR, "model %s has no triangles\n", mod->name ); - return; - } - - m_pAliasHeader->numframes = pinmodel->numframes; - numframes = m_pAliasHeader->numframes; - - if( numframes < 1 ) - { - MsgDev( D_ERROR, "Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes ); + Con_DPrintf( S_ERROR "model %s has too many vertices\n", mod->name ); return; } @@ -718,7 +699,7 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded ) pframetype = (daliasframetype_t *)&pintriangles[m_pAliasHeader->numtris]; g_posenum = 0; - for( i = 0; i < numframes; i++ ) + for( i = 0; i < m_pAliasHeader->numframes; i++ ) { aliasframetype_t frametype = pframetype->type; @@ -1193,15 +1174,18 @@ void R_AliasLerpMovement( cl_entity_t *e ) if( g_alias.interpolate && ( g_alias.time < e->curstate.animtime + 1.0f ) && ( e->curstate.animtime != e->latched.prevanimtime )) f = ( g_alias.time - e->curstate.animtime ) / ( e->curstate.animtime - e->latched.prevanimtime ); + if( cls.demoplayback == DEMO_QUAKE1 ) + f = f + 1.0f; + g_alias.lerpfrac = bound( 0.0f, f, 1.0f ); if( e->player || e->curstate.movetype != MOVETYPE_STEP ) return; // monsters only - // Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_studio.time ); + // Con_Printf( "%4.2f %.2f %.2f\n", f, e->curstate.animtime, g_alias.time ); VectorLerp( e->latched.prevorigin, f, e->curstate.origin, e->origin ); - if( !VectorCompare( e->curstate.angles, e->latched.prevangles )) + if( !VectorCompareEpsilon( e->curstate.angles, e->latched.prevangles, ON_EPSILON )) { vec4_t q, q1, q2; @@ -1240,7 +1224,7 @@ void R_SetupAliasFrame( cl_entity_t *e, aliashdr_t *paliashdr ) else if( newframe >= paliashdr->numframes ) { if( newframe > paliashdr->numframes ) - MsgDev( D_WARN, "R_GetAliasFrame: no such frame %d (%s)\n", newframe, e->model->name ); + Con_Reportf( S_WARN "R_GetAliasFrame: no such frame %d (%s)\n", newframe, e->model->name ); newframe = paliashdr->numframes - 1; } diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index 003c4683..a4f267df 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -536,7 +536,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) break; } - Image_Process( &r_shot, width, height, flags, NULL ); + Image_Process( &r_shot, width, height, flags, 0.0f ); // write image result = FS_SaveImage( filename, r_shot ); @@ -605,7 +605,7 @@ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qbo r_side->size = r_side->width * r_side->height * 3; r_side->buffer = temp; - if( flags ) Image_Process( &r_side, 0, 0, flags, NULL ); + if( flags ) Image_Process( &r_side, 0, 0, flags, 0.0f ); memcpy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 ); } diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 7006b0dc..1c1436e5 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -684,8 +684,8 @@ static void GL_SetTextureFormat( gl_texture_t *tex, pixformat_t format, int chan else if( haveAlpha ) { if( FBitSet( tex->flags, TF_ARB_16BIT ) || glw_state.desktopBitsPixel == 16 ) - tex->format = GL_LUMINANCE_ALPHA16F_ARB; - else tex->format = GL_LUMINANCE_ALPHA32F_ARB; + tex->format = GL_RG16F; + else tex->format = GL_RG32F; } else { @@ -1208,7 +1208,7 @@ GL_ProcessImage do specified actions on pixels =============== */ -static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic, imgfilter_t *filter ) +static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic ) { uint img_flags = 0; @@ -1242,6 +1242,12 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic, imgfilter_t *fil tex->flags &= ~TF_MAKELUMA; } + if( tex->flags & TF_ALLOW_EMBOSS ) + { + img_flags |= IMAGE_EMBOSS; + tex->flags &= ~TF_ALLOW_EMBOSS; + } + if( !FBitSet( tex->flags, TF_IMG_UPLOADED ) && FBitSet( tex->flags, TF_KEEP_SOURCE )) tex->original = FS_CopyImage( pic ); // because current pic will be expanded to rgba @@ -1250,7 +1256,7 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic, imgfilter_t *fil img_flags |= IMAGE_FORCE_RGBA; // processing image before uploading (force to rgba, make luma etc) - if( pic->buffer ) Image_Process( &pic, 0, 0, img_flags, filter ); + if( pic->buffer ) Image_Process( &pic, 0, 0, img_flags, gl_emboss_scale->value ); if( FBitSet( tex->flags, TF_LUMINANCE )) ClearBits( pic->flags, IMAGE_HAS_COLOR ); @@ -1421,7 +1427,7 @@ void GL_UpdateTexSize( int texnum, int width, int height, int depth ) GL_LoadTexture ================ */ -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ) +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) { gl_texture_t *tex; rgbdata_t *pic; @@ -1448,7 +1454,7 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i // allocate the new one tex = GL_AllocTexture( name, flags ); - GL_ProcessImage( tex, pic, filter ); + GL_ProcessImage( tex, pic ); if( !GL_UploadTexture( tex, pic )) { @@ -1469,7 +1475,7 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i GL_LoadTextureArray ================ */ -int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) +int GL_LoadTextureArray( const char **names, int flags ) { rgbdata_t *pic, *src; char basename[256]; @@ -1538,7 +1544,7 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) // but allow to rescale raw images if( ImageRAW( pic->type ) && ImageRAW( src->type ) && ( pic->width != src->width || pic->height != src->height )) - Image_Process( &src, pic->width, pic->height, IMAGE_RESAMPLE, NULL ); + Image_Process( &src, pic->width, pic->height, IMAGE_RESAMPLE, 0.0f ); if( pic->size != src->size ) { @@ -1589,7 +1595,7 @@ int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ) // allocate the new one tex = GL_AllocTexture( name, flags ); - GL_ProcessImage( tex, pic, filter ); + GL_ProcessImage( tex, pic ); if( !GL_UploadTexture( tex, pic )) { @@ -1636,7 +1642,7 @@ int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags tex = GL_AllocTexture( name, flags ); } - GL_ProcessImage( tex, pic, NULL ); + GL_ProcessImage( tex, pic ); if( !GL_UploadTexture( tex, pic )) { memset( tex, 0, sizeof( gl_texture_t )); @@ -1828,7 +1834,7 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) // all the operations makes over the image copy not an original pic = FS_CopyImage( image->original ); - Image_Process( &pic, topColor, bottomColor, flags, NULL ); + Image_Process( &pic, topColor, bottomColor, flags, 0.0f ); GL_UploadTexture( image, pic ); GL_ApplyTextureParams( image ); // update texture filter, wrap etc @@ -2077,6 +2083,12 @@ void R_TextureList_f( void ) case GL_LUMINANCE_ALPHA32F_ARB: Con_Printf( "LA32F " ); break; + case GL_RG16F: + Con_Printf( "RG16F " ); + break; + case GL_RG32F: + Con_Printf( "RG32F " ); + break; case GL_RGB16F_ARB: Con_Printf( "RGB16F" ); break; @@ -2185,7 +2197,6 @@ void R_InitImages( void ) R_SetTextureParameters(); GL_CreateInternalTextures(); - R_ParseTexFilters( "scripts/texfilter.txt" ); Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); } diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 3ec2c89d..c8edd17c 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -309,8 +309,8 @@ void R_SetTextureParameters( void ); gl_texture_t *R_GetTexture( GLenum texnum ); #define GL_LoadTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, false ) #define GL_UpdateTextureInternal( name, pic, flags ) GL_LoadTextureFromBuffer( name, pic, flags, true ) -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ); -int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +int GL_LoadTextureArray( const char **names, int flags ); int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap ); int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ); @@ -387,8 +387,7 @@ void Matrix4x4_CreateModelview( matrix4x4 out ); // // gl_rmisc. // -void R_ParseTexFilters( const char *filename ); -imgfilter_t *R_FindTexFilter( const char *texname ); +void R_ClearStaticEntities( void ); // // gl_rsurf.c @@ -469,7 +468,7 @@ void R_Shutdown( void ); qboolean R_Init( void ); void R_Shutdown( void ); void VID_CheckChanges( void ); -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); void GL_FreeImage( const char *name ); qboolean VID_ScreenShot( const char *filename, int shot_type ); qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ); @@ -642,6 +641,7 @@ extern convar_t *gl_texture_lodbias; extern convar_t *gl_texture_nearest; extern convar_t *gl_lightmap_nearest; extern convar_t *gl_keeptjunctions; +extern convar_t *gl_emboss_scale; extern convar_t *gl_round_down; extern convar_t *gl_detailscale; extern convar_t *gl_wireframe; diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index c4b64023..1b82f595 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1381,16 +1381,6 @@ const byte *GL_TextureData( unsigned int texnum ) return NULL; } -static int GL_LoadTextureNoFilter( const char *name, const byte *buf, size_t size, int flags ) -{ - return GL_LoadTexture( name, buf, size, flags, NULL ); -} - -static int GL_LoadTextureArrayNoFilter( const char **names, int flags ) -{ - return GL_LoadTextureArray( names, flags, NULL ); -} - static const ref_overview_t *GL_GetOverviewParms( void ) { return &clgame.overView; @@ -1473,22 +1463,22 @@ static render_api_t gRenderAPI = GL_FindTexture, GL_TextureName, GL_TextureData, - GL_LoadTextureNoFilter, + GL_LoadTexture, GL_CreateTexture, - GL_LoadTextureArrayNoFilter, + GL_LoadTextureArray, GL_CreateTextureArray, GL_FreeTexture, DrawSingleDecal, R_DecalSetupVerts, R_EntityRemoveDecals, - AVI_LoadVideoNoSound, + AVI_LoadVideo, AVI_GetVideoInfo, AVI_GetVideoFrameNumber, AVI_GetVideoFrame, R_UploadStretchRaw, AVI_FreeVideo, AVI_IsActive, - NULL, + S_StreamAviSamples, NULL, NULL, GL_Bind, diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index ee8cd302..727f3fa4 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -19,16 +19,7 @@ GNU General Public License for more details. #include "mod_local.h" #include "shake.h" -typedef struct -{ - char texname[64]; // shortname - imgfilter_t filter; -} dfilter_t; - -dfilter_t *tex_filters[MAX_TEXTURES]; -int num_texfilters; - -void R_ParseDetailTextures( const char *filename ) +static void R_ParseDetailTextures( const char *filename ) { char *afile, *pfile; string token, texname; @@ -95,7 +86,7 @@ void R_ParseDetailTextures( const char *filename ) if( Q_stricmp( tex->name, texname )) continue; - tex->dt_texturenum = GL_LoadTexture( detail_path, NULL, 0, TF_FORCE_COLOR, NULL ); + tex->dt_texturenum = GL_LoadTexture( detail_path, NULL, 0, TF_FORCE_COLOR ); // texture is loaded if( tex->dt_texturenum ) @@ -113,110 +104,6 @@ void R_ParseDetailTextures( const char *filename ) Mem_Free( afile ); } -void R_ParseTexFilters( const char *filename ) -{ - char *afile, *pfile; - string token, texname; - dfilter_t *tf; - int i; - - afile = FS_LoadFile( filename, NULL, false ); - if( !afile ) return; - - pfile = afile; - - // format: 'texturename' 'filtername' 'factor' 'bias' 'blendmode' 'grayscale' - while(( pfile = COM_ParseFile( pfile, token )) != NULL ) - { - imgfilter_t filter; - - memset( &filter, 0, sizeof( filter )); - Q_strncpy( texname, token, sizeof( texname )); - - // parse filter - pfile = COM_ParseFile( pfile, token ); - if( !Q_stricmp( token, "blur" )) - filter.filter = BLUR_FILTER; - else if( !Q_stricmp( token, "blur2" )) - filter.filter = BLUR_FILTER2; - else if( !Q_stricmp( token, "edge" )) - filter.filter = EDGE_FILTER; - else if( !Q_stricmp( token, "emboss" )) - filter.filter = EMBOSS_FILTER; - - // reading factor - pfile = COM_ParseFile( pfile, token ); - filter.factor = Q_atof( token ); - - // reading bias - pfile = COM_ParseFile( pfile, token ); - filter.bias = Q_atof( token ); - - // reading blendFunc - pfile = COM_ParseFile( pfile, token ); - if( !Q_stricmp( token, "modulate" ) || !Q_stricmp( token, "GL_MODULATE" )) - filter.blendFunc = GL_MODULATE; - else if( !Q_stricmp( token, "replace" ) || !Q_stricmp( token, "GL_REPLACE" )) - filter.blendFunc = GL_REPLACE; - else if( !Q_stricmp( token, "add" ) || !Q_stricmp( token, "GL_ADD" )) - filter.blendFunc = GL_ADD; - else if( !Q_stricmp( token, "decal" ) || !Q_stricmp( token, "GL_DECAL" )) - filter.blendFunc = GL_DECAL; - else if( !Q_stricmp( token, "blend" ) || !Q_stricmp( token, "GL_BLEND" )) - filter.blendFunc = GL_BLEND; - else if( !Q_stricmp( token, "add_signed" ) || !Q_stricmp( token, "GL_ADD_SIGNED" )) - filter.blendFunc = GL_ADD_SIGNED; - else filter.blendFunc = GL_REPLACE; // defaulting to replace - - // reading flags - pfile = COM_ParseFile( pfile, token ); - filter.flags = Q_atoi( token ); - - // make sure what factor is not zeroed - if( filter.factor == 0.0f ) - continue; - - // check if already existed - for( i = 0; i < num_texfilters; i++ ) - { - tf = tex_filters[i]; - - if( !Q_stricmp( tf->texname, texname )) - break; - } - - if( i != num_texfilters ) - continue; // already specified - - // allocate new texfilter - tf = Z_Malloc( sizeof( dfilter_t )); - tex_filters[num_texfilters++] = tf; - - Q_strncpy( tf->texname, texname, sizeof( tf->texname )); - tf->filter = filter; - } - - Con_Reportf( "%i texture filters parsed\n", num_texfilters ); - - Mem_Free( afile ); -} - -imgfilter_t *R_FindTexFilter( const char *texname ) -{ - dfilter_t *tf; - int i; - - for( i = 0; i < num_texfilters; i++ ) - { - tf = tex_filters[i]; - - if( !Q_stricmp( tf->texname, texname )) - return &tf->filter; - } - - return NULL; -} - /* ======================= R_ClearStaticEntities diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index 23b05534..67b5fa9e 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -69,12 +69,12 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite { Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); - gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags, NULL ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags ); } else { Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); - gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags, NULL ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height * bytes, r_texFlags ); } // setup frame description @@ -162,13 +162,13 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui if( pin->ident != IDSPRITEHEADER ) { - MsgDev( D_ERROR, "%s has wrong id (%x should be %x)\n", mod->name, pin->ident, IDSPRITEHEADER ); + Con_DPrintf( S_ERROR "%s has wrong id (%x should be %x)\n", mod->name, pin->ident, IDSPRITEHEADER ); return; } if( i != SPRITE_VERSION_Q1 && i != SPRITE_VERSION_HL && i != SPRITE_VERSION_32 ) { - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i or %i)\n", mod->name, i, SPRITE_VERSION_Q1, SPRITE_VERSION_HL ); + Con_DPrintf( S_ERROR "%s has wrong version number (%i should be %i or %i)\n", mod->name, i, SPRITE_VERSION_Q1, SPRITE_VERSION_HL ); return; } @@ -259,15 +259,12 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui } else { - MsgDev( D_ERROR, "%s has wrong number of palette colors %i (should be 256)\n", mod->name, *numi ); + Con_DPrintf( S_ERROR "%s has wrong number of palette colors %i (should be 256)\n", mod->name, *numi ); return; } if( mod->numframes < 1 ) - { - MsgDev( D_ERROR, "%s has invalid # of frames: %d\n", mod->name, mod->numframes ); return; - } for( i = 0; i < mod->numframes; i++ ) { @@ -336,7 +333,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean if( h < MAPSPRITE_SIZE ) h = MAPSPRITE_SIZE; // resample image if needed - Image_Process( &pix, w, h, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE, NULL ); + Image_Process( &pix, w, h, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE, 0.0f ); w = h = MAPSPRITE_SIZE; diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 38e4ee70..675a99c5 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -1680,7 +1680,7 @@ void R_StudioDynamicLight( cl_entity_t *ent, alight_t *plight ) } } - if(( light.r + light.g + light.b ) == 0 ) + if(( light.r + light.g + light.b ) < 16 ) // TESTTEST { colorVec gcolor; float grad[4]; @@ -3740,7 +3740,6 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture size_t size; int flags = 0; char texname[128], name[128], mdlname[128]; - imgfilter_t *filter = NULL; texture_t *tx = NULL; if( ptexture->flags & STUDIO_NF_NORMALMAP ) @@ -3797,10 +3796,6 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture COM_FileBase( ptexture->name, name ); COM_StripExtension( mdlname ); - // loading texture filter for studiomodel - if( !FBitSet( ptexture->flags, STUDIO_NF_COLORMAP )) - filter = R_FindTexFilter( va( "%s.mdl/%s", mdlname, name )); // grab texture filter - if( FBitSet( ptexture->flags, STUDIO_NF_NOMIPS )) SetBits( flags, TF_NOMIPMAP ); @@ -3813,7 +3808,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture // build the texname Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name ); - ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags, filter ); + ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags ); if( !ptexture->index ) { diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 42212239..8f84d0fd 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -35,6 +35,7 @@ convar_t *gl_texture_nearest; convar_t *gl_lightmap_nearest; convar_t *gl_wgl_msaa_samples; convar_t *gl_keeptjunctions; +convar_t *gl_emboss_scale; convar_t *gl_showtextures; convar_t *gl_detailscale; convar_t *gl_check_errors; @@ -434,7 +435,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv convar_t *parm = NULL; const char *extensions_string; - MsgDev( D_NOTE, "GL_CheckExtension: %s ", name ); + Con_Reportf( "GL_CheckExtension: %s ", name ); GL_SetExtension( r_ext, true ); if( cvarname ) @@ -445,7 +446,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv if(( parm && !CVAR_TO_BOOL( parm )) || ( !CVAR_TO_BOOL( gl_extensions ) && r_ext != GL_OPENGL_110 )) { - MsgDev( D_NOTE, "- disabled\n" ); + Con_Reportf( "- disabled\n" ); GL_SetExtension( r_ext, false ); return; // nothing to process at } @@ -458,7 +459,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( extensions_string, name )) { GL_SetExtension( r_ext, false ); // update render info - MsgDev( D_NOTE, "- ^1failed\n" ); + Con_Reportf( "- ^1failed\n" ); return; } @@ -474,8 +475,8 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv } if( GL_Support( r_ext )) - MsgDev( D_NOTE, "- ^2enabled\n" ); - else MsgDev( D_NOTE, "- ^1failed\n" ); + Con_Reportf( "- ^2enabled\n" ); + else Con_Reportf( "- ^1failed\n" ); } /* @@ -624,7 +625,7 @@ qboolean GL_CreateContext( void ) return true; } - MsgDev( D_NOTE, "GL_CreateContext: using extended context\n" ); + Con_Reportf( "GL_CreateContext: using extended context\n" ); pwglDeleteContext( hBaseRC ); // release first context glw_state.extended = true; } @@ -769,7 +770,7 @@ VID_StartupGamma void VID_StartupGamma( void ) { BuildGammaTable( vid_gamma->value, vid_brightness->value ); - MsgDev( D_NOTE, "VID_StartupGamma: gamma %g brightness %g\n", vid_gamma->value, vid_brightness->value ); + Con_Reportf( "VID_StartupGamma: gamma %g brightness %g\n", vid_gamma->value, vid_brightness->value ); ClearBits( vid_brightness->flags, FCVAR_CHANGED ); ClearBits( vid_gamma->flags, FCVAR_CHANGED ); } @@ -975,7 +976,7 @@ qboolean GL_SetPixelformat( void ) { if( PFD.dwFlags & PFD_GENERIC_ACCELERATED ) { - MsgDev( D_NOTE, "VID_ChoosePFD: using Generic MCD acceleration\n" ); + Con_Reportf( "VID_ChoosePFD: using Generic MCD acceleration\n" ); } else { @@ -985,7 +986,7 @@ qboolean GL_SetPixelformat( void ) } else { - MsgDev( D_NOTE, "VID_ChoosePFD: using hardware acceleration\n"); + Con_Reportf( "VID_ChoosePFD: using hardware acceleration\n" ); } glConfig.color_bits = PFD.cColorBits; @@ -1019,7 +1020,7 @@ void R_SaveVideoMode( int vid_mode ) Cvar_FullSet( "height", va( "%i", glState.height ), FCVAR_READ_ONLY ); Cvar_SetValue( "vid_mode", mode ); // merge if it out of bounds - MsgDev( D_NOTE, "Set: %s [%dx%d]\n", vidmode[mode].desc, vidmode[mode].width, vidmode[mode].height ); + Con_Reportf( "Set: %s [%dx%d]\n", vidmode[mode].desc, vidmode[mode].width, vidmode[mode].height ); } /* @@ -1366,12 +1367,12 @@ qboolean VID_SetMode( void ) if( R_DescribeVIDMode( iScreenWidth, iScreenHeight )) { - MsgDev( D_NOTE, "found specified vid mode %i [%ix%i]\n", (int)vid_mode->value, iScreenWidth, iScreenHeight ); + Con_Reportf( "found specified vid mode %i [%ix%i]\n", (int)vid_mode->value, iScreenWidth, iScreenHeight ); Cvar_SetValue( "fullscreen", 1 ); } else { - MsgDev( D_NOTE, "failed to set specified vid mode [%ix%i]\n", iScreenWidth, iScreenHeight ); + Con_Reportf( "failed to set specified vid mode [%ix%i]\n", iScreenWidth, iScreenHeight ); Cvar_SetValue( "vid_mode", VID_DEFAULTMODE ); } } @@ -1608,6 +1609,7 @@ void GL_InitCommands( void ) gl_texture_anisotropy = Cvar_Get( "gl_anisotropy", "8", FCVAR_ARCHIVE, "textures anisotropic filter" ); gl_texture_lodbias = Cvar_Get( "gl_texture_lodbias", "0.0", FCVAR_ARCHIVE, "LOD bias for mipmapped textures (perfomance|quality)" ); gl_keeptjunctions = Cvar_Get( "gl_keeptjunctions", "1", FCVAR_ARCHIVE, "removing tjuncs causes blinking pixels" ); + gl_emboss_scale = Cvar_Get( "gl_emboss_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "fake bumpmapping scale" ); gl_showtextures = Cvar_Get( "r_showtextures", "0", FCVAR_CHEAT, "show all uploaded textures" ); gl_finish = Cvar_Get( "gl_finish", "0", FCVAR_ARCHIVE, "use glFinish instead of glFlush" ); gl_nosort = Cvar_Get( "gl_nosort", "0", FCVAR_ARCHIVE, "disable sorting of translucent surfaces" ); diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index bb91e4a5..a6b71a6e 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -441,7 +441,7 @@ void R_SetupSky( const char *skyboxname ) Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] ); else Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] ); - tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY, NULL ); + tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); if( !tr.skyboxTextures[i] ) break; Con_DPrintf( "%s%s%s", skyboxname, r_skyBoxSuffix[i], i != 5 ? ", " : ". " ); } diff --git a/engine/client/s_load.c b/engine/client/s_load.c index 92f52056..0cef0550 100644 --- a/engine/client/s_load.c +++ b/engine/client/s_load.c @@ -135,15 +135,17 @@ wavdata_t *S_LoadSound( sfx_t *sfx ) return sfx->cache; if( !COM_CheckString( sfx->name )) - { - // debug - Con_Printf( "S_LoadSound: sfx %d has NULL name\n", sfx - s_knownSfx ); return NULL; - } // load it from disk if( Q_stricmp( sfx->name, "*default" )) - sc = FS_LoadSound( sfx->name, NULL, 0 ); + { + // load it from disk + if( sfx->name[0] == '*' ) + sc = FS_LoadSound( sfx->name + 1, NULL, 0 ); + else sc = FS_LoadSound( sfx->name, NULL, 0 ); + } + if( !sc ) sc = S_CreateDefaultSound(); if( sc->rate < SOUND_11k ) // some bad sounds @@ -301,7 +303,7 @@ void S_EndRegistration( void ) // free any sounds not from this registration sequence for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ ) { - if( !sfx->name[0] || sfx->name[0] == '*' ) + if( !sfx->name[0] || !Q_stricmp( sfx->name, "*default" )) continue; // don't release default sound if( sfx->servercount != s_registration_sequence ) diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 78fd04a2..270c275b 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1592,19 +1592,79 @@ void S_RawSamples( uint samples, uint rate, word width, word channels, const byt S_PositionedRawSamples =================== */ -static void S_PositionedRawSamples( int entnum, float fvol, float attn, uint samples, uint rate, word width, word channels, const byte *data ) +void S_StreamAviSamples( void *Avi, int entnum, float fvol, float attn, float synctime ) { - rawchan_t *ch; - + int bufferSamples; + int fileSamples; + byte raw[MAX_RAW_SAMPLES]; + float duration = 0.0f; + int r, fileBytes; + rawchan_t *ch = NULL; + + if( !dma.initialized || s_listener.paused || !CL_IsInGame( )) + return; + if( entnum < 0 || entnum >= GI->max_edicts ) return; if( !( ch = S_FindRawChannel( entnum, true ))) return; + if( ch->sound_info.rate == 0 ) + { + if( !AVI_GetAudioInfo( Avi, &ch->sound_info )) + return; // no audiotrack + } + ch->master_vol = bound( 0, fvol * 255, 255 ); ch->dist_mult = (attn / SND_CLIP_DISTANCE); - ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, samples, rate, width, channels, data ); + + // see how many samples should be copied into the raw buffer + if( ch->s_rawend < soundtime ) + ch->s_rawend = soundtime; + + // position is changed, synchronization is lost etc + if( fabs( ch->oldtime - synctime ) > s_mixahead->value ) + ch->sound_info.loopStart = AVI_TimeToSoundPosition( Avi, synctime * 1000 ); + ch->oldtime = synctime; // keep actual time + + while( ch->s_rawend < soundtime + ch->max_samples ) + { + wavdata_t *info = &ch->sound_info; + + bufferSamples = ch->max_samples - (ch->s_rawend - soundtime); + + // decide how much data needs to be read from the file + fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED ); + if( fileSamples <= 1 ) return; // no more samples need + + // our max buffer size + fileBytes = fileSamples * ( info->width * info->channels ); + + if( fileBytes > sizeof( raw )) + { + fileBytes = sizeof( raw ); + fileSamples = fileBytes / ( info->width * info->channels ); + } + + // read audio stream + r = AVI_GetAudioChunk( Avi, raw, info->loopStart, fileBytes ); + info->loopStart += r; // advance play position + + if( r < fileBytes ) + { + fileBytes = r; + fileSamples = r / ( info->width * info->channels ); + } + + if( r > 0 ) + { + // add to raw buffer + ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, + fileSamples, info->rate, info->width, info->channels, raw ); + } + else break; // no more samples for this frame + } } /* @@ -1680,6 +1740,7 @@ static void S_ClearRawChannels( void ) if( !ch ) continue; ch->s_rawend = 0; + ch->oldtime = -1; } } @@ -1883,6 +1944,23 @@ void S_ExtraUpdate( void ) S_UpdateChannels (); } +/* +============ +S_UpdateFrame + +update listener position +============ +*/ +void S_UpdateFrame( ref_viewpass_t *rvp ) +{ + if( !FBitSet( rvp->flags, RF_DRAW_WORLD ) || FBitSet( rvp->flags, RF_ONLY_CLIENTDRAW )) + return; + + VectorCopy( rvp->vieworigin, s_listener.origin ); + AngleVectors( rvp->viewangles, s_listener.forward, s_listener.right, s_listener.up ); + s_listener.entnum = rvp->viewentity; // can be camera entity too +} + /* ============ SND_UpdateSound @@ -1907,17 +1985,13 @@ void SND_UpdateSound( void ) // release raw-channels that no longer used more than 10 secs S_FreeIdleRawChannels(); - s_listener.entnum = cl.viewentity; // can be camera entity too + VectorCopy( cl.simvel, s_listener.velocity ); s_listener.frametime = (cl.time - cl.oldtime); s_listener.waterlevel = cl.local.waterlevel; s_listener.active = CL_IsInGame(); s_listener.inmenu = CL_IsInMenu(); s_listener.paused = cl.paused; - VectorCopy( RI.vieworg, s_listener.origin ); - VectorCopy( cl.simvel, s_listener.velocity ); - AngleVectors( RI.viewangles, s_listener.forward, s_listener.right, s_listener.up ); - if( cl.worldmodel != NULL ) Mod_FatPVS( s_listener.origin, FATPHS_RADIUS, s_listener.pasbytes, world.visbytes, false, !s_phs->value ); @@ -2040,7 +2114,7 @@ void S_Play2_f( void ) if( Cmd_Argc() == 1 ) { - Con_Printf( S_USAGE "play \n" ); + Con_Printf( S_USAGE "play2 \n" ); return; } diff --git a/engine/client/sound.h b/engine/client/sound.h index 6da7ec9d..58d77f10 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -81,6 +81,8 @@ extern byte *sndpool; #define S_RAW_SOUND_SOUNDTRACK -1 #define S_RAW_SAMPLES_PRECISION_BITS 14 +#define CIN_FRAMETIME (1.0f / 30.0f) + typedef struct { int left; @@ -156,6 +158,8 @@ typedef struct rawchan_s vec3_t origin; // only use if fixed_origin is set float radius; // radius of this sound effect volatile uint s_rawend; + wavdata_t sound_info; // advance play position + float oldtime; // catch time jumps size_t max_samples; // buffer length portable_samplepair_t rawsamples[1]; // variable sized } rawchan_t; @@ -316,6 +320,7 @@ sfx_t *S_GetSfxByHandle( sound_t handle ); rawchan_t *S_FindRawChannel( int entnum, qboolean create ); void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum ); void S_StopSound( int entnum, int channel, const char *soundname ); +void S_UpdateFrame( struct ref_viewpass_s *rvp ); uint S_GetRawSamplesLength( int entnum ); void S_ClearRawChannel( int entnum ); void S_StopAllSounds( qboolean ambient ); diff --git a/engine/client/vgui/vgui_int.cpp b/engine/client/vgui/vgui_int.cpp index 7971c441..2c85e0fa 100644 --- a/engine/client/vgui/vgui_int.cpp +++ b/engine/client/vgui/vgui_int.cpp @@ -120,7 +120,7 @@ void VGui_Paint( int paintAll ) void VGui_ViewportPaintBackground( int extents[4] ) { -// Msg( "Vgui_ViewportPaintBackground( %i, %i, %i, %i )\n", extents[0], extents[1], extents[2], extents[3] ); + // not used } void *VGui_GetPanel( void ) diff --git a/engine/common/avikit.c b/engine/common/avikit.c index e2c80705..c2cc4c63 100644 --- a/engine/common/avikit.c +++ b/engine/common/avikit.c @@ -58,6 +58,7 @@ dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, false }; static int (_stdcall *pAVIStreamInfo)( PAVISTREAM pavi, AVISTREAMINFO *psi, LONG lSize ); static int (_stdcall *pAVIStreamRead)( PAVISTREAM pavi, LONG lStart, LONG lSamples, void *lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples ); static PGETFRAME (_stdcall *pAVIStreamGetFrameOpen)( PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted ); +static long (_stdcall *pAVIStreamTimeToSample)( PAVISTREAM pavi, LONG lTime ); static void* (_stdcall *pAVIStreamGetFrame)( PGETFRAME pg, LONG lPos ); static int (_stdcall *pAVIStreamGetFrameClose)( PGETFRAME pg ); static dword (_stdcall *pAVIStreamRelease)( PAVISTREAM pavi ); @@ -84,6 +85,7 @@ static dllfunc_t avifile_funcs[] = { "AVIStreamReadFormat", (void **) &pAVIStreamReadFormat }, { "AVIStreamRelease", (void **) &pAVIStreamRelease }, { "AVIStreamStart", (void **) &pAVIStreamStart }, +{ "AVIStreamTimeToSample", (void **) &pAVIStreamTimeToSample }, { NULL, NULL } }; @@ -275,6 +277,23 @@ long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time ) return (time * Avi->video_fps); } +long AVI_GetVideoFrameCount( movie_state_t *Avi ) +{ + if( !Avi->active ) + return 0; + + return Avi->video_frames; +} + +long AVI_TimeToSoundPosition( movie_state_t *Avi, long time ) +{ + if( !Avi->active || !Avi->audio_stream ) + return 0; + + // UNDONE: what about compressed audio? + return pAVIStreamTimeToSample( Avi->audio_stream, time ) * Avi->audio_bytes_per_sample; +} + // gets the raw frame data byte *AVI_GetVideoFrame( movie_state_t *Avi, long frame ) { @@ -656,11 +675,6 @@ movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio ) return Avi; } -movie_state_t *AVI_LoadVideoNoSound( const char *filename ) -{ - return AVI_LoadVideo( filename, false ); -} - void AVI_FreeVideo( movie_state_t *state ) { if( !state ) return; diff --git a/engine/common/build.c b/engine/common/build.c index dac721ac..04c52685 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 4140; + return 4260; #endif } \ No newline at end of file diff --git a/engine/common/cfgscript.c b/engine/common/cfgscript.c index f61e5041..e469cb09 100644 --- a/engine/common/cfgscript.c +++ b/engine/common/cfgscript.c @@ -63,7 +63,7 @@ qboolean CSCR_ExpectString( parserstate_t *ps, const char *pExpect, qboolean ski } if( skip ) ps->buf = tmp; - if( error ) MsgDev( D_ERROR, "Syntax error in %s: got \"%s\" instead of \"%s\"\n", ps->filename, ps->token, pExpect ); + if( error ) Con_DPrintf( S_ERROR "Syntax error in %s: got \"%s\" instead of \"%s\"\n", ps->filename, ps->token, pExpect ); return false; } @@ -85,7 +85,7 @@ cvartype_t CSCR_ParseType( parserstate_t *ps ) return i; } - MsgDev( D_ERROR, "Cannot parse %s: Bad type %s\n", ps->filename, ps->token ); + Con_DPrintf( S_ERROR "Cannot parse %s: Bad type %s\n", ps->filename, ps->token ); return T_NONE; } @@ -181,7 +181,7 @@ qboolean CSCR_ParseHeader( parserstate_t *ps ) if( Q_atof( ps->token ) != 1 ) { - MsgDev( D_ERROR, "File %s has wrong version %s!\n", ps->filename, ps->token ); + Con_DPrintf( S_ERROR "File %s has wrong version %s!\n", ps->filename, ps->token ); return false; } @@ -192,7 +192,7 @@ qboolean CSCR_ParseHeader( parserstate_t *ps ) if( Q_stricmp( ps->token, "INFO_OPTIONS") && Q_stricmp( ps->token, "SERVER_OPTIONS" )) { - MsgDev( D_ERROR, "DESCRIPTION must be INFO_OPTIONS or SERVER_OPTIONS\n"); + Con_DPrintf( S_ERROR "DESCRIPTION must be INFO_OPTIONS or SERVER_OPTIONS\n"); return false; } @@ -223,13 +223,10 @@ int CSCR_WriteGameCVars( file_t *cfg, const char *scriptfilename ) if( !state.buf || !length ) return 0; - MsgDev( D_INFO, "Reading config script file %s\n", scriptfilename ); + Con_DPrintf( "Reading config script file %s\n", scriptfilename ); if( !CSCR_ParseHeader( &state )) - { - MsgDev( D_ERROR, "Failed to parse header!\n" ); goto finish; - } while( !CSCR_ExpectString( &state, "}", false, false )) { @@ -258,7 +255,7 @@ int CSCR_WriteGameCVars( file_t *cfg, const char *scriptfilename ) } if( COM_ParseFile( state.buf, state.token )) - MsgDev( D_ERROR, "Got extra tokens!\n" ); + Con_DPrintf( S_ERROR "Got extra tokens!\n" ); else success = true; finish: if( !success ) @@ -266,8 +263,8 @@ finish: state.token[sizeof( state.token ) - 1] = 0; if( start && state.buf ) - MsgDev( D_ERROR, "Parse error in %s, byte %d, token %s\n", scriptfilename, (int)( state.buf - start ), state.token ); - else MsgDev( D_ERROR, "Parse error in %s, token %s\n", scriptfilename, state.token ); + Con_DPrintf( S_ERROR "Parse error in %s, byte %d, token %s\n", scriptfilename, (int)( state.buf - start ), state.token ); + else Con_DPrintf( S_ERROR "Parse error in %s, token %s\n", scriptfilename, state.token ); } if( start ) Mem_Free( start ); @@ -296,13 +293,10 @@ int CSCR_LoadDefaultCVars( const char *scriptfilename ) if( !state.buf || !length ) return 0; - MsgDev( D_INFO, "Reading config script file %s\n", scriptfilename ); + Con_DPrintf( "Reading config script file %s\n", scriptfilename ); if( !CSCR_ParseHeader( &state )) - { - MsgDev( D_ERROR, "Failed to parse header!\n" ); goto finish; - } while( !CSCR_ExpectString( &state, "}", false, false )) { @@ -322,15 +316,15 @@ int CSCR_LoadDefaultCVars( const char *scriptfilename ) } if( COM_ParseFile( state.buf, state.token )) - MsgDev( D_ERROR, "Got extra tokens!\n" ); + Con_DPrintf( S_ERROR "Got extra tokens!\n" ); else success = true; finish: if( !success ) { state.token[sizeof( state.token ) - 1] = 0; if( start && state.buf ) - MsgDev( D_ERROR, "Parse error in %s, byte %d, token %s\n", scriptfilename, (int)( state.buf - start ), state.token ); - else MsgDev( D_ERROR, "Parse error in %s, token %s\n", scriptfilename, state.token ); + Con_DPrintf( S_ERROR "Parse error in %s, byte %d, token %s\n", scriptfilename, (int)( state.buf - start ), state.token ); + else Con_DPrintf( S_ERROR "Parse error in %s, token %s\n", scriptfilename, state.token ); } if( start ) Mem_Free( start ); diff --git a/engine/common/cmd.c b/engine/common/cmd.c index e508025b..24ef5622 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -539,11 +539,11 @@ void Cmd_TokenizeString( char *text ) if( !*text ) return; - + if( cmd_argc == 1 ) cmd_args = text; - host.com_ignorebracket = true; + host.com_ignorebracket = true; text = COM_ParseFile( text, cmd_token ); host.com_ignorebracket = false; @@ -973,12 +973,14 @@ void Cmd_ExecuteString( char *text ) if( host.type == HOST_NORMAL ) { if( cls.state >= ca_connected ) + { Cmd_ForwardToServer(); - } - else if( text[0] != '@' && host.type == HOST_NORMAL ) - { - // commands with leading '@' are hidden system commands - Con_Printf( S_WARN "Unknown command \"%s\"\n", text ); + } + else if( text[0] != '@' && Cvar_VariableInteger( "host_gameloaded" )) + { + // commands with leading '@' are hidden system commands + Con_Printf( S_WARN "Unknown command \"%s\"\n", text ); + } } } diff --git a/engine/common/common.h b/engine/common/common.h index 11cc46f4..4dc4f60e 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -57,7 +57,7 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc #define MAX_SERVERINFO_STRING 512 // server handles too many settings. expand to 1024? #define MAX_LOCALINFO_STRING 32768 // localinfo used on server and not sended to the clients #define MAX_SYSPATH 1024 // system filepath -#define MAX_PRINT_MSG 8192 // how many symbols can handle single call of Msg or MsgDev +#define MAX_PRINT_MSG 8192 // how many symbols can handle single call of Con_Printf or Con_DPrintf #define MAX_TOKEN 2048 // parse token length #define MAX_MODS 512 // environment games that engine can keep visible #define MAX_USERMSG_LENGTH 2048 // don't modify it's relies on a client-side definitions @@ -228,9 +228,11 @@ typedef struct gameinfo_s int gamemode; qboolean secure; // prevent to console acess qboolean nomodels; // don't let player to choose model (use player.mdl always) + qboolean noskills; // disable skill menu selection char sp_entity[32]; // e.g. info_player_start char mp_entity[32]; // e.g. info_player_deathmatch + char mp_filter[32]; // filtering multiplayer-maps char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds @@ -576,7 +578,7 @@ typedef enum IMAGE_ROT_90 = BIT(18), // flip from upper left corner to down right corner IMAGE_ROT180 = IMAGE_FLIP_X|IMAGE_FLIP_Y, IMAGE_ROT270 = IMAGE_FLIP_X|IMAGE_FLIP_Y|IMAGE_ROT_90, -// reserved + IMAGE_EMBOSS = BIT(19), // apply emboss mapping IMAGE_RESAMPLE = BIT(20), // resample image to specified dims // reserved // reserved @@ -587,16 +589,6 @@ typedef enum IMAGE_REMAP = BIT(27), // interpret width and height as top and bottom color } imgFlags_t; -// ordering is important! -typedef enum -{ - BLUR_FILTER = 0, - BLUR_FILTER2, - EDGE_FILTER, - EMBOSS_FILTER, - NUM_FILTERS, -} pixfilter_t; - typedef struct rgbdata_s { word width; // image width @@ -612,21 +604,6 @@ typedef struct rgbdata_s size_t size; // for bounds checking } rgbdata_t; -// imgfilter processing flags -typedef enum -{ - FILTER_GRAYSCALE = BIT(0), -} flFlags_t; - -typedef struct imgfilter_s -{ - int filter; // pixfilter_t - float factor; // filter factor value - float bias; // filter bias value - flFlags_t flags; // filter additional flags - uint blendFunc; // blending mode -} imgfilter_t; - // // imagelib // @@ -638,7 +615,7 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ); rgbdata_t *FS_CopyImage( rgbdata_t *in ); void FS_FreeImage( rgbdata_t *pack ); extern const bpc_desc_t PFDesc[]; // image get pixelformat -qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgfilter_t *filter ); +qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, float bumpscale ); void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end, int pal_size ); void Image_PaletteTranslate( byte *palSrc, int top, int bottom, int pal_size ); void Image_SetForceFlags( uint flags ); // set image force flags on loading @@ -882,7 +859,8 @@ qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info ); long AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length ); void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet ); movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio ); -movie_state_t *AVI_LoadVideoNoSound( const char *filename ); +long AVI_TimeToSoundPosition( movie_state_t *Avi, long time ); +long AVI_GetVideoFrameCount( movie_state_t *Avi ); void AVI_CloseVideo( movie_state_t *Avi ); qboolean AVI_IsActive( movie_state_t *Avi ); void AVI_FreeVideo( movie_state_t *Avi ); @@ -929,6 +907,7 @@ void SV_DrawDebugTriangles( void ); void SV_DrawOrthoTriangles( void ); double CL_GetDemoFramerate( void ); qboolean UI_CreditsActive( void ); +void CL_StopPlayback( void ); void CL_ExtraUpdate( void ); int CL_GetMaxClients( void ); int SV_GetMaxClients( void ); diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index be3f4500..04bf2d2b 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -667,7 +667,9 @@ qboolean Cmd_GetCDList( const char *s, char *completedname, int length ) qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { + qboolean use_filter = false; byte buf[MAX_SYSPATH]; + string mpfilter; char *buffer; string result; int i, size; @@ -677,6 +679,8 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) if( FS_FileSize( "maps.lst", onlyingamedir ) > 0 && !fRefresh ) return true; // exist + // setup mpfilter + Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter ); t = FS_Search( "maps/*.bsp", false, onlyingamedir ); if( !t ) @@ -690,6 +694,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) } buffer = Mem_Calloc( host.mempool, t->numfilenames * 2 * sizeof( result )); + use_filter = Q_strlen( GI->mp_filter ) ? true : false; for( i = 0; i < t->numfilenames; i++ ) { @@ -700,6 +705,9 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) if( Q_stricmp( COM_FileExtension( t->filenames[i] ), "bsp" )) continue; + if( use_filter && !Q_strnicmp( t->filenames[i], mpfilter, Q_strlen( mpfilter ))) + continue; + f = FS_Open( t->filenames[i], "rb", onlyingamedir ); COM_FileBase( t->filenames[i], mapname ); @@ -757,7 +765,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) else if( !Q_strcmp( token, "classname" )) { pfile = COM_ParseFile( pfile, token ); - if( !Q_strcmp( token, GI->mp_entity )) + if( !Q_strcmp( token, GI->mp_entity ) || use_filter ) num_spawnpoints++; } if( num_spawnpoints ) break; // valid map @@ -805,6 +813,7 @@ qboolean Cmd_CheckMapsList( qboolean fRefresh ) autocomplete_list_t cmd_list[] = { { "map_background", Cmd_GetMapList }, +{ "changelevel2", Cmd_GetMapList }, { "changelevel", Cmd_GetMapList }, { "playdemo", Cmd_GetDemoList, }, { "timedemo", Cmd_GetDemoList, }, diff --git a/engine/common/console.c b/engine/common/console.c index 17c40965..59e39853 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -559,7 +559,7 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) return false; // keep source to print directly into conback image - font->hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_KEEP_SOURCE, NULL ); + font->hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_KEEP_SOURCE ); R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); if( font->hFontTexture && fontWidth != 0 ) @@ -595,7 +595,7 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font if( !FS_FileExists( fontname, false )) return false; - font->hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_NEAREST, NULL ); + font->hFontTexture = GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_NEAREST ); R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); // setup consolefont @@ -2340,28 +2340,28 @@ void Con_VidInit( void ) { // trying to load truecolor image first if( FS_FileExists( "gfx/shell/conback.bmp", false ) || FS_FileExists( "gfx/shell/conback.tga", false )) - con.background = GL_LoadTexture( "gfx/shell/conback", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "gfx/shell/conback", NULL, 0, TF_IMAGE ); if( !con.background ) { if( FS_FileExists( "cached/conback640", false )) - con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE ); else if( FS_FileExists( "cached/conback", false )) - con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE ); } } else { // trying to load truecolor image first if( FS_FileExists( "gfx/shell/loading.bmp", false ) || FS_FileExists( "gfx/shell/loading.tga", false )) - con.background = GL_LoadTexture( "gfx/shell/loading", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "gfx/shell/loading", NULL, 0, TF_IMAGE ); if( !con.background ) { if( FS_FileExists( "cached/loading640", false )) - con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE ); else if( FS_FileExists( "cached/loading", false )) - con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE ); } } @@ -2396,13 +2396,13 @@ void Con_VidInit( void ) y = Q_strlen( ver ); for( x = 0; x < y; x++ ) Con_DrawCharToConback( ver[x], chars->original->buffer, dest + (x << 3)); - con.background = GL_LoadTexture( "#gfx/conback.lmp", (byte *)cb, length, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "#gfx/conback.lmp", (byte *)cb, length, TF_IMAGE ); } if( cb ) Mem_Free( cb ); } if( !con.background ) // trying the load unmodified conback - con.background = GL_LoadTexture( "gfx/conback.lmp", NULL, 0, TF_IMAGE, NULL ); + con.background = GL_LoadTexture( "gfx/conback.lmp", NULL, 0, TF_IMAGE ); } // missed console image will be replaced as gray background like X-Ray or Crysis diff --git a/engine/common/crtlib.c b/engine/common/crtlib.c index 0f01795d..0fe7678a 100644 --- a/engine/common/crtlib.c +++ b/engine/common/crtlib.c @@ -570,7 +570,6 @@ int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list ar __except( EXCEPTION_EXECUTE_HANDLER ) { Q_strncpy( buffer, "^1sprintf throw exception^7\n", buffersize ); -// memset( buffer, 0, buffersize ); result = buffersize; } diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 42e28605..11feb8cb 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -639,6 +639,18 @@ char *Cvar_VariableString( const char *var_name ) return var->string; } +/* +============ +Cvar_Exists +============ +*/ +qboolean Cvar_Exists( const char *var_name ) +{ + if( Cvar_FindVar( var_name )) + return true; + return false; +} + /* ============ Cvar_SetCheatState diff --git a/engine/common/cvar.h b/engine/common/cvar.h index e190b109..eb51199a 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -62,6 +62,7 @@ float Cvar_VariableValue( const char *var_name ); int Cvar_VariableInteger( const char *var_name ); char *Cvar_VariableString( const char *var_name ); void Cvar_WriteVariables( file_t *f, int group ); +qboolean Cvar_Exists( const char *var_name ); void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); qboolean Cvar_Command( void ); diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 1f0b2e25..fa293745 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -255,7 +255,7 @@ static void listlowercase( stringlist_t *list ) } } -static void listdirectory( stringlist_t *list, const char *path ) +static void listdirectory( stringlist_t *list, const char *path, int lower ) { char pattern[4096]; struct _finddata_t n_file; @@ -277,7 +277,7 @@ static void listdirectory( stringlist_t *list, const char *path ) _findclose( hFile ); // g-cont. disabled for some reasons -// listlowercase( list ); + if( lower ) listlowercase( list ); } /* @@ -310,8 +310,8 @@ static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, long offse middle = (left + right) / 2; diff = Q_stricmp( pack->files[middle].name, name ); - // If we found the file, there's a problem - if( !diff ) MsgDev( D_WARN, "package %s contains the file %s several times\n", pack->filename, name ); + // If we found the file, there's a problem (but don't confuse the users) + if( !diff ) Con_Reportf( S_WARN "package %s contains the file %s several times\n", pack->filename, name ); // If we're too far in the list if( diff > 0 ) right = middle - 1; @@ -413,7 +413,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( packhandle < 0 ) { - MsgDev( D_NOTE, "%s couldn't open\n", packfile ); + Con_Reportf( "%s couldn't open\n", packfile ); if( error ) *error = PAK_LOAD_COULDNT_OPEN; return NULL; } @@ -422,7 +422,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( header.ident != IDPACKV1HEADER ) { - MsgDev( D_NOTE, "%s is not a packfile. Ignored.\n", packfile ); + Con_Reportf( "%s is not a packfile. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_BAD_HEADER; close( packhandle ); return NULL; @@ -430,7 +430,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( header.dirlen % sizeof( dpackfile_t )) { - MsgDev( D_ERROR, "%s has an invalid directory size. Ignored.\n", packfile ); + Con_Reportf( "%s has an invalid directory size. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_BAD_FOLDERS; close( packhandle ); return NULL; @@ -440,7 +440,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( numpackfiles > MAX_FILES_IN_PACK ) { - MsgDev( D_ERROR, "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles ); + Con_DPrintf( S_ERROR "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles ); if( error ) *error = PAK_LOAD_TOO_MANY_FILES; close( packhandle ); return NULL; @@ -448,7 +448,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( numpackfiles <= 0 ) { - MsgDev( D_NOTE, "%s has no files. Ignored.\n", packfile ); + Con_Reportf( "%s has no files. Ignored.\n", packfile ); if( error ) *error = PAK_LOAD_NO_FILES; close( packhandle ); return NULL; @@ -459,7 +459,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) { - MsgDev( D_NOTE, "%s is an incomplete PAK, not loading\n", packfile ); + Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile ); if( error ) *error = PAK_LOAD_CORRUPTED; close( packhandle ); Mem_Free( info ); @@ -504,9 +504,11 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade } } - if( already_loaded ) *already_loaded = false; - if( !Q_stricmp( ext, "wad" )) wad = W_Open( wadfile, &errorcode ); - else MsgDev( D_ERROR, "\"%s\" doesn't have a wad extension\n", wadfile ); + if( already_loaded ) + *already_loaded = false; + + if( !Q_stricmp( ext, "wad" )) + wad = W_Open( wadfile, &errorcode ); if( wad ) { @@ -516,13 +518,13 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade search->flags |= flags; fs_searchpaths = search; - MsgDev( D_REPORT, "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); + Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); return true; } else { if( errorcode != WAD_LOAD_NO_FILES ) - MsgDev( D_ERROR, "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); + Con_DPrintf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); return false; } } @@ -557,10 +559,11 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade } } - if( already_loaded ) *already_loaded = false; + if( already_loaded ) + *already_loaded = false; - if( !Q_stricmp( ext, "pak" )) pak = FS_LoadPackPAK( pakfile, &errorcode ); - else MsgDev( D_ERROR, "\"%s\" does not have a pack extension\n", pakfile ); + if( !Q_stricmp( ext, "pak" )) + pak = FS_LoadPackPAK( pakfile, &errorcode ); if( pak ) { @@ -572,7 +575,7 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade search->flags |= flags; fs_searchpaths = search; - MsgDev( D_REPORT, "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); + Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); // time to add in search list all the wads that contains in current pakfile (if do) for( i = 0; i < pak->numfiles; i++ ) @@ -589,7 +592,7 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade else { if( errorcode != PAK_LOAD_NO_FILES ) - MsgDev( D_ERROR, "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile ); + Con_DPrintf( S_ERROR "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile ); return false; } } @@ -613,7 +616,7 @@ void FS_AddGameDirectory( const char *dir, int flags ) Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); stringlistinit( &list ); - listdirectory( &list, dir ); + listdirectory( &list, dir, true ); stringlistsort( &list ); // add any PAK package in the directory @@ -750,7 +753,7 @@ FS_Rescan */ void FS_Rescan( void ) { - MsgDev( D_NOTE, "FS_Rescan( %s )\n", GI->title ); + Con_Reportf( "FS_Rescan( %s )\n", GI->title ); FS_ClearSearchPath(); @@ -785,6 +788,8 @@ assume GameInfo is valid static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) { file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode + int i, write_ambients = false; + if( !f ) Sys_Error( "FS_WriteGameInfo: can't write %s\n", filepath ); // may be disk-space is out? FS_Print( f, "// generated by Xash3D\n\n\n" ); @@ -843,6 +848,8 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) FS_Printf( f, "sp_entity\t\t\"%s\"\n", GameInfo->sp_entity ); if( Q_strlen( GameInfo->mp_entity )) FS_Printf( f, "mp_entity\t\t\"%s\"\n", GameInfo->mp_entity ); + if( Q_strlen( GameInfo->mp_filter )) + FS_Printf( f, "mp_filter\t\t\"%s\"\n", GameInfo->mp_filter ); if( GameInfo->secure ) FS_Printf( f, "secure\t\t\"%i\"\n", GameInfo->secure ); @@ -859,6 +866,19 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) if( GameInfo->max_particles > 0 ) FS_Printf( f, "max_particles\t%i\n", GameInfo->max_particles ); + for( i = 0; i < NUM_AMBIENTS; i++ ) + { + if( *GameInfo->ambientsound[i] ) + { + if( !write_ambients ) + { + FS_Print( f, "\n" ); + write_ambients = true; + } + FS_Printf( f, "ambient%i\t\t%s\n", i, GameInfo->ambientsound[i] ); + } + } + FS_Print( f, "\n\n\n" ); FS_Close( f ); // all done } @@ -1013,6 +1033,10 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g { pfile = COM_ParseFile( pfile, GameInfo->mp_entity ); } + else if( !Q_stricmp( token, "mpfilter" )) + { + pfile = COM_ParseFile( pfile, GameInfo->mp_filter ); + } else if( !Q_stricmp( token, "secure" )) { pfile = COM_ParseFile( pfile, token ); @@ -1119,6 +1143,10 @@ static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, game { pfile = COM_ParseFile( pfile, GameInfo->mp_entity ); } + else if( !Q_stricmp( token, "mp_filter" )) + { + pfile = COM_ParseFile( pfile, GameInfo->mp_filter ); + } else if( !Q_stricmp( token, "gamedll" )) { pfile = COM_ParseFile( pfile, GameInfo->game_dll ); @@ -1206,18 +1234,18 @@ static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, game pfile = COM_ParseFile( pfile, token ); GameInfo->nomodels = Q_atoi( token ); } + else if( !Q_stricmp( token, "noskills" )) + { + pfile = COM_ParseFile( pfile, token ); + GameInfo->noskills = Q_atoi( token ); + } else if( !Q_strnicmp( token, "ambient", 7 )) { int ambientNum = Q_atoi( token + 7 ); if( ambientNum < 0 || ambientNum > ( NUM_AMBIENTS - 1 )) - { - MsgDev( D_ERROR, "FS_ReadGameInfo: Invalid ambient number %i. Ignored.\n", ambientNum ); - } - else - { - pfile = COM_ParseFile( pfile, GameInfo->ambientsound[ambientNum] ); - } + ambientNum = 0; + pfile = COM_ParseFile( pfile, GameInfo->ambientsound[ambientNum] ); } } @@ -1316,7 +1344,7 @@ void FS_LoadGameInfo( const char *rootfolder ) fs_ext_path = false; if( rootfolder ) Q_strcpy( fs_gamedir, rootfolder ); - MsgDev( D_NOTE, "FS_LoadGameInfo( %s )\n", fs_gamedir ); + Con_Reportf( "FS_LoadGameInfo( %s )\n", fs_gamedir ); // clear any old pathes FS_ClearSearchPath(); @@ -1357,7 +1385,7 @@ void FS_Init( void ) // ignore commandlineoption "-game" for other stuff stringlistinit( &dirs ); - listdirectory( &dirs, "./" ); + listdirectory( &dirs, "./", true ); stringlistsort( &dirs ); SI.numgames = 0; @@ -1487,7 +1515,6 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) opt = O_CREAT; break; default: - MsgDev( D_ERROR, "FS_SysOpen(%s, %s): invalid mode\n", filepath, mode ); return NULL; } @@ -1502,7 +1529,6 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) opt |= O_BINARY; break; default: - MsgDev( D_ERROR, "FS_SysOpen: %s: unknown char (%c) in mode (%s)\n", filepath, mode[ind], mode ); break; } } @@ -2222,7 +2248,7 @@ qboolean FS_WriteFile( const char *filename, const void *data, long len ) if( !file ) { - MsgDev( D_ERROR, "FS_WriteFile: failed on %s\n", filename); + Con_DPrintf( S_ERROR "FS_WriteFile: failed on %s\n", filename ); return false; } @@ -2512,7 +2538,7 @@ qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ) if(( readSize = FS_Read( pInput, buf, size )) < size ) { - MsgDev( D_ERROR, "FS_FileCopy: unexpected end of input file (%d < %d)\n", readSize, size ); + Con_DPrintf( S_ERROR "FS_FileCopy: unexpected end of input file (%d < %d)\n", readSize, size ); fileSize = 0; done = false; break; @@ -2687,7 +2713,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) // get a directory listing and look at each name Q_sprintf( netpath, "%s%s", searchpath->filename, basepath ); stringlistinit( &dirlist ); - listdirectory( &dirlist, netpath ); + listdirectory( &dirlist, netpath, false ); for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) { @@ -2885,7 +2911,7 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t diff = 1; else if( wad->lumps[middle].type > newlump->type ) diff = -1; - else MsgDev( D_WARN, "Wad %s contains the file %s several times\n", wad->filename, name ); + else Con_Reportf( S_WARN "Wad %s contains the file %s several times\n", wad->filename, name ); } // If we're too far in the list @@ -2926,7 +2952,7 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr ) if( FS_Seek( wad->handle, lump->filepos, SEEK_SET ) == -1 ) { - MsgDev( D_ERROR, "W_ReadLump: %s is corrupted\n", lump->name ); + Con_DPrintf( S_ERROR "W_ReadLump: %s is corrupted\n", lump->name ); FS_Seek( wad->handle, oldpos, SEEK_SET ); return NULL; } @@ -2936,7 +2962,7 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr ) if( size < lump->disksize ) { - MsgDev( D_WARN, "W_ReadLump: %s is probably corrupted\n", lump->name ); + Con_DPrintf( S_WARN "W_ReadLump: %s is probably corrupted\n", lump->name ); FS_Seek( wad->handle, oldpos, SEEK_SET ); Mem_Free( buf ); return NULL; @@ -2976,7 +3002,7 @@ wfile_t *W_Open( const char *filename, int *error ) if( wad->handle == NULL ) { - MsgDev( D_ERROR, "W_Open: couldn't open %s\n", filename ); + Con_DPrintf( S_ERROR "W_Open: couldn't open %s\n", filename ); if( error ) *error = WAD_LOAD_COULDNT_OPEN; W_Close( wad ); return NULL; @@ -2989,7 +3015,7 @@ wfile_t *W_Open( const char *filename, int *error ) if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t )) { - MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename ); + Con_DPrintf( S_ERROR "W_Open: %s can't read header\n", filename ); if( error ) *error = WAD_LOAD_BAD_HEADER; W_Close( wad ); return NULL; @@ -2997,7 +3023,7 @@ wfile_t *W_Open( const char *filename, int *error ) if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) { - MsgDev( D_ERROR, "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); + Con_DPrintf( S_ERROR "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); if( error ) *error = WAD_LOAD_BAD_HEADER; W_Close( wad ); return NULL; @@ -3007,12 +3033,12 @@ wfile_t *W_Open( const char *filename, int *error ) if( lumpcount >= MAX_FILES_IN_WAD ) { - MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); + Con_DPrintf( S_WARN "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); if( error ) *error = WAD_LOAD_TOO_MANY_FILES; } else if( lumpcount <= 0 ) { - MsgDev( D_ERROR, "W_Open: %s has no lumps\n", filename ); + Con_DPrintf( S_ERROR "W_Open: %s has no lumps\n", filename ); if( error ) *error = WAD_LOAD_NO_FILES; W_Close( wad ); return NULL; @@ -3023,7 +3049,7 @@ wfile_t *W_Open( const char *filename, int *error ) if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 ) { - MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename ); + Con_DPrintf( S_ERROR "W_Open: %s can't find lump allocation table\n", filename ); if( error ) *error = WAD_LOAD_BAD_FOLDERS; W_Close( wad ); return NULL; @@ -3036,7 +3062,7 @@ wfile_t *W_Open( const char *filename, int *error ) if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size ) { - MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename ); + Con_DPrintf( S_ERROR "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename ); if( error ) *error = WAD_LOAD_CORRUPTED; Mem_Free( srclumps ); W_Close( wad ); diff --git a/engine/common/host.c b/engine/common/host.c index cb25e0d9..6f959d1f 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -418,7 +418,7 @@ double Host_CalcFPS( void ) if( host.type != HOST_DEDICATED && Host_IsLocalGame( ) && !CL_IsTimeDemo( )) { // ajdust fps for vertical synchronization - if( gl_vsync != NULL && gl_vsync->value ) + if( CVAR_TO_BOOL( gl_vsync )) { if( vid_displayfrequency->value != 0.0f ) fps = vid_displayfrequency->value; @@ -647,7 +647,8 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) } else { - if( *in == ' ' ) + // now we found cmdline + if( *in == ' ' && ( in[1] == '+' || in[1] == '-' )) { parse_cmdline = true; *out++ = '\0'; @@ -663,8 +664,12 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) host.mempool = Mem_AllocPool( "Zone Engine" ); + // get name of executable + if( GetModuleFileName( NULL, szTemp, sizeof( szTemp ))) + COM_FileBase( szTemp, SI.exeName ); + // HACKHACK: Quake console is always allowed - if( Sys_CheckParm( "-console" ) || !Q_stricmp( progname, "id1" )) + if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" )) host.allow_console = true; if( Sys_CheckParm( "-dev" )) @@ -682,10 +687,6 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) host.type = HOST_NORMAL; // predict state host.con_showalways = true; - // we can specified custom name, from Sys_NewInstance - if( GetModuleFileName( NULL, szTemp, sizeof( szTemp )) && !host.change_game ) - COM_FileBase( szTemp, SI.exeName ); - COM_ExtractFilePath( szTemp, szRootPath ); if( Q_stricmp( host.rootdir, szRootPath )) { diff --git a/engine/common/hpak.c b/engine/common/hpak.c index e5a77697..bfb131c5 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -95,20 +95,17 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f return; if(( fin != NULL && pData != NULL ) || ( fin == NULL && pData == NULL )) - { - MsgDev( D_ERROR, "HPAK_CreatePak, must specify one of pData or fpSource\n" ); return; - } Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - MsgDev( D_INFO, "creating HPAK %s.\n", pakname ); + Con_Printf( "creating HPAK %s.\n", pakname ); fout = FS_Open( pakname, "wb", false ); if( !fout ) { - MsgDev( D_ERROR, "HPAK_CreatePak: can't write %s.\n", pakname ); + Con_DPrintf( S_ERROR "HPAK_CreatePak: can't write %s.\n", pakname ); return; } @@ -135,7 +132,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f if( memcmp( md5, pResource->rgucMD5_hash, 16 )) { - MsgDev( D_ERROR, "HPAK_CreatePak: bad checksum for %s. Ignored\n", pakname ); + Con_DPrintf( S_ERROR "HPAK_CreatePak: bad checksum for %s. Ignored\n", pakname ); return; } @@ -204,10 +201,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, MD5Context_t ctx; if( pData == NULL && pFile == NULL ) - { - MsgDev( D_ERROR, "HPAK_AddLump: no data\n" ); return; - } if( pResource->nDownloadSize < HPAK_MIN_SIZE || pResource->nDownloadSize > HPAK_MAX_SIZE ) { @@ -238,7 +232,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, if( memcmp( md5, pResource->rgucMD5_hash, 16 )) { - MsgDev( D_ERROR, "HPAK_AddLump: bad checksum for %s. Ignored\n", pResource->szFileName ); + Con_DPrintf( S_ERROR "HPAK_AddLump: bad checksum for %s. Ignored\n", pResource->szFileName ); return; } @@ -267,7 +261,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, if( !file_dst ) { - MsgDev( D_ERROR, "HPAK_AddLump: couldn't open %s.\n", srcname ); + Con_DPrintf( S_ERROR "HPAK_AddLump: couldn't open %s.\n", srcname ); FS_Close( file_src ); return; } @@ -278,7 +272,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, if( hash_pack_header.version != IDHPAK_VERSION ) { // we don't check the HPAK bit for some reason. - MsgDev( D_ERROR, "HPAK_AddLump: %s does not have a valid header.\n", srcname ); + Con_DPrintf( S_ERROR "HPAK_AddLump: %s does not have a valid header.\n", srcname ); FS_Close( file_src ); FS_Close( file_dst ); } @@ -292,7 +286,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, if( srcpak.count < 1 || srcpak.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "HPAK_AddLump: %s contain too many lumps.\n", srcname ); + Con_DPrintf( S_ERROR "HPAK_AddLump: %s contain too many lumps.\n", srcname ); FS_Close( file_src ); FS_Close( file_dst ); return; @@ -386,16 +380,16 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) f = FS_Open( pakname, "rb", false ); if( !f ) { - MsgDev( D_INFO, "Couldn't find %s.\n", pakname ); + Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname ); return true; } - if( !quiet ) MsgDev( D_INFO, "Validating %s\n", pakname ); + if( !quiet ) Con_Printf( "Validating %s\n", pakname ); FS_Read( f, &hdr, sizeof( hdr )); if( hdr.ident != IDHPAKHEADER || hdr.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "HPAK_ValidatePak: %s does not have a valid HPAK header.\n", pakname ); + Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s does not have a valid HPAK header.\n", pakname ); FS_Close( f ); return false; } @@ -405,24 +399,24 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) if( num_lumps < 1 || num_lumps > MAX_FILES_IN_WAD ) { - MsgDev( D_ERROR, "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps ); + Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps ); FS_Close( f ); return false; } - if( !quiet ) MsgDev( D_INFO, "# of Entries: %i\n", num_lumps ); + if( !quiet ) Con_Printf( "# of Entries: %i\n", num_lumps ); dataDir = Z_Malloc( sizeof( hpak_lump_t ) * num_lumps ); FS_Read( f, dataDir, sizeof( hpak_lump_t ) * num_lumps ); - if( !quiet ) MsgDev( D_INFO, "# Type Size FileName : MD5 Hash\n" ); + if( !quiet ) Con_Printf( "# Type Size FileName : MD5 Hash\n" ); for( i = 0; i < num_lumps; i++ ) { if( dataDir[i].disksize < 1 || dataDir[i].disksize > 131071 ) { // odd max size - MsgDev( D_ERROR, "HPAK_ValidatePak: lump %i has invalid size %s\n", i, Q_pretifymem( dataDir[i].disksize, 2 )); + Con_DPrintf( S_ERROR "HPAK_ValidatePak: lump %i has invalid size %s\n", i, Q_pretifymem( dataDir[i].disksize, 2 )); Mem_Free( dataDir ); FS_Close(f); return false; @@ -439,24 +433,24 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) pRes = &dataDir[i].resource; - MsgDev( D_INFO, "%i: %s %s %s: ", i, HPAK_TypeFromIndex( pRes->type ), + Con_Printf( "%i: %s %s %s: ", i, HPAK_TypeFromIndex( pRes->type ), Q_pretifymem( pRes->nDownloadSize, 2 ), pRes->szFileName ); if( memcmp( md5, pRes->rgucMD5_hash, 0x10 )) { if( quiet ) { - MsgDev( D_ERROR, "HPAK_ValidatePak: %s has invalid checksum.\n", pakname ); + Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has invalid checksum.\n", pakname ); Mem_Free( dataPak ); Mem_Free( dataDir ); FS_Close( f ); return false; } - else MsgDev( D_INFO, "failed\n" ); + else Con_DPrintf( S_ERROR "failed\n" ); } else { - if( !quiet ) MsgDev( D_INFO, "OK\n" ); + if( !quiet ) Con_Printf( "OK\n" ); } // at this point, it's passed our checks. @@ -584,21 +578,21 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource f = FS_Open( pakname, "rb", false ); if( !f ) { - MsgDev( D_ERROR, "couldn't open %s.\n", pakname ); + Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); return false; } FS_Read( f, &header, sizeof( header )); if( header.ident != IDHPAKHEADER ) { - MsgDev( D_ERROR, "%s is not an HPAK file\n", pakname ); + Con_DPrintf( S_ERROR "%s is not an HPAK file\n", pakname ); FS_Close( f ); return false; } if( header.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); + Con_DPrintf( S_ERROR "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); FS_Close( f ); return false; } @@ -608,14 +602,14 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource if( directory.count < 1 || directory.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "%s has too many lumps %u.\n", pakname, directory.count ); + Con_DPrintf( S_ERROR "%s has too many lumps %u.\n", pakname, directory.count ); FS_Close( f ); return false; } if( index < 1 || index > directory.count ) { - MsgDev( D_ERROR, "%s, lump with index %i doesn't exist.\n", pakname, index ); + Con_DPrintf( S_ERROR "%s, lump with index %i doesn't exist.\n", pakname, index ); FS_Close( f ); return false; } @@ -674,14 +668,14 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte if( header.ident != IDHPAKHEADER ) { - MsgDev( D_ERROR, "%s it's not a HPK file.\n", pakname ); + Con_DPrintf( S_ERROR "%s it's not a HPK file.\n", pakname ); FS_Close( f ); return false; } if( header.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); + Con_DPrintf( S_ERROR "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); FS_Close( f ); return false; } @@ -691,7 +685,7 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte if( directory.count < 1 || directory.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "HPAK_GetDataPointer: %s has too many lumps %u.\n", filename, directory.count ); + Con_DPrintf( S_ERROR "HPAK_GetDataPointer: %s has too many lumps %u.\n", filename, directory.count ); FS_Close( f ); return false; } @@ -751,7 +745,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) file_src = FS_Open( read_path, "rb", false ); if( !file_src ) { - MsgDev( D_ERROR, "%s couldn't open.\n", read_path ); + Con_DPrintf( S_ERROR "%s couldn't open.\n", read_path ); return; } @@ -761,7 +755,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( !file_dst ) { - MsgDev( D_ERROR, "%s couldn't open.\n", save_path ); + Con_DPrintf( S_ERROR "%s couldn't open.\n", save_path ); FS_Close( file_src ); return; } @@ -775,7 +769,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( hash_pack_header.ident != IDHPAKHEADER || hash_pack_header.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "%s has invalid header.\n", read_path ); + Con_DPrintf( S_ERROR "%s has invalid header.\n", read_path ); FS_Close( file_src ); FS_Close( file_dst ); FS_Delete( save_path ); // delete temp file @@ -787,7 +781,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( hpak_read.count < 1 || hpak_read.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "%s has invalid number of lumps.\n", read_path ); + Con_DPrintf( S_ERROR "%s has invalid number of lumps.\n", read_path ); FS_Close( file_src ); FS_Close( file_dst ); FS_Delete( save_path ); // delete temp file @@ -796,7 +790,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( hpak_read.count == 1 ) { - MsgDev( D_WARN, "%s only has one element, so HPAK will be removed\n", read_path ); + Con_DPrintf( S_WARN "%s only has one element, so HPAK will be removed\n", read_path ); FS_Close( file_src ); FS_Close( file_dst ); FS_Delete( read_path ); @@ -812,7 +806,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( !HPAK_FindResource( &hpak_read, pResource->rgucMD5_hash, NULL )) { - MsgDev( D_ERROR, "HPAK doesn't contain specified lump: %s\n", pResource->szFileName, read_path ); + Con_DPrintf( S_ERROR "HPAK doesn't contain specified lump: %s\n", pResource->szFileName, read_path ); Mem_Free( hpak_read.entries ); Mem_Free( hpak_save.entries ); FS_Close( file_src ); @@ -821,7 +815,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) return; } - MsgDev( D_INFO, "Removing %s from HPAK %s.\n", pResource->szFileName, read_path ); + Con_Printf( "Removing %s from HPAK %s.\n", pResource->szFileName, read_path ); // If there's a collision, we've just corrupted this hpak. for( i = 0, j = 0; i < hpak_read.count; i++ ) @@ -881,7 +875,7 @@ void HPAK_List_f( void ) f = FS_Open( pakname, "rb", false ); if( !f ) { - MsgDev( D_ERROR, "couldn't open %s.\n", pakname ); + Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); return; } @@ -889,14 +883,14 @@ void HPAK_List_f( void ) if( header.ident != IDHPAKHEADER ) { - MsgDev( D_ERROR, "%s is not an HPAK file\n", pakname ); + Con_DPrintf( S_ERROR "%s is not an HPAK file\n", pakname ); FS_Close( f ); return; } if( header.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); + Con_DPrintf( S_ERROR "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); FS_Close( f ); return; } @@ -906,7 +900,7 @@ void HPAK_List_f( void ) if( directory.count < 1 || directory.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "%s has too many lumps %u.\n", pakname, directory.count ); + Con_DPrintf( S_ERROR "%s has too many lumps %u.\n", pakname, directory.count ); FS_Close( f ); return; } @@ -972,7 +966,7 @@ void HPAK_Extract_f( void ) f = FS_Open( pakname, "rb", false ); if( !f ) { - MsgDev( D_ERROR, "couldn't open %s.\n", pakname ); + Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); return; } @@ -980,14 +974,14 @@ void HPAK_Extract_f( void ) if( header.ident != IDHPAKHEADER ) { - MsgDev( D_ERROR, "%s is not an HPAK file\n", pakname ); + Con_DPrintf( S_ERROR "%s is not an HPAK file\n", pakname ); FS_Close( f ); return; } if( header.version != IDHPAK_VERSION ) { - MsgDev( D_ERROR, "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); + Con_DPrintf( S_ERROR "%s has invalid version (%i should be %i).\n", pakname, header.version, IDHPAK_VERSION ); FS_Close( f ); return; } @@ -997,7 +991,7 @@ void HPAK_Extract_f( void ) if( directory.count < 1 || directory.count > HPAK_MAX_ENTRIES ) { - MsgDev( D_ERROR, "%s has too many lumps %u.\n", pakname, directory.count ); + Con_DPrintf( S_ERROR "%s has too many lumps %u.\n", pakname, directory.count ); FS_Close( f ); return; } @@ -1023,7 +1017,7 @@ void HPAK_Extract_f( void ) if( entry->disksize <= 0 || entry->disksize >= HPAK_MAX_SIZE ) { - MsgDev( D_WARN, "Unable to extract data, size invalid: %s\n", Q_memprint( entry->disksize )); + Con_DPrintf( S_WARN "Unable to extract data, size invalid: %s\n", Q_memprint( entry->disksize )); continue; } @@ -1061,7 +1055,7 @@ void HPAK_Remove_f( void ) } else { - MsgDev( D_ERROR, "Could not locate resource %i in %s\n", Q_atoi( Cmd_Argv( 2 )), Cmd_Argv( 1 )); + Con_DPrintf( S_ERROR "Could not locate resource %i in %s\n", Q_atoi( Cmd_Argv( 2 )), Cmd_Argv( 1 )); } } diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index c4640367..8a2479bf 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -57,13 +57,13 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) if( memcmp( bhdr.id, "BM", 2 )) { - MsgDev( D_ERROR, "Image_LoadBMP: only Windows-style BMP files supported (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadBMP: only Windows-style BMP files supported (%s)\n", name ); return false; } if( bhdr.bitmapHeaderSize != 0x28 ) { - MsgDev( D_ERROR, "Image_LoadBMP: invalid header size %i\n", bhdr.bitmapHeaderSize ); + Con_DPrintf( S_ERROR "Image_LoadBMP: invalid header size %i\n", bhdr.bitmapHeaderSize ); return false; } @@ -71,13 +71,13 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) if( bhdr.fileSize != filesize ) { // Sweet Half-Life issues. splash.bmp have bogus filesize - MsgDev( D_REPORT, "Image_LoadBMP: %s have incorrect file size %i should be %i\n", name, filesize, bhdr.fileSize ); + Con_Reportf( S_WARN "Image_LoadBMP: %s have incorrect file size %i should be %i\n", name, filesize, bhdr.fileSize ); } // bogus compression? Only non-compressed supported. if( bhdr.compression != BI_RGB ) { - MsgDev( D_ERROR, "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name ); return false; } @@ -284,7 +284,6 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) if( alpha != 255 ) image.flags |= IMAGE_HAS_ALPHA; break; default: - MsgDev( D_ERROR, "Image_LoadBMP: illegal pixel_size (%s)\n", name ); Mem_Free( image.palette ); Mem_Free( image.rgba ); return false; @@ -343,7 +342,6 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix ) pixel_size = 4; break; default: - MsgDev( D_ERROR, "Image_SaveBMP: unsupported image type %s\n", PFDesc[pix->type].name ); return false; } diff --git a/engine/common/imagelib/img_dds.c b/engine/common/imagelib/img_dds.c index 1e24176d..9db234ec 100644 --- a/engine/common/imagelib/img_dds.c +++ b/engine/common/imagelib/img_dds.c @@ -217,7 +217,7 @@ uint Image_DXTCalcSize( const char *name, dds_t *hdr, size_t filesize ) if( filesize != buffsize ) // main check { - MsgDev( D_WARN, "Image_LoadDDS: (%s) probably corrupted(%i should be %i)\n", name, buffsize, filesize ); + Con_DPrintf( S_WARN "Image_LoadDDS: (%s) probably corrupted (%i should be %i)\n", name, buffsize, filesize ); if( buffsize > filesize ) return false; } @@ -245,10 +245,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) byte *fin; if( filesize < sizeof( dds_t )) - { - MsgDev( D_ERROR, "Image_LoadDDS: file (%s) have invalid size\n", name ); return false; - } memcpy( &header, buffer, sizeof( dds_t )); @@ -257,13 +254,13 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) if( header.dwSize != sizeof( dds_t ) - sizeof( uint )) // size of the structure (minus MagicNum) { - MsgDev( D_ERROR, "Image_LoadDDS: (%s) have corrupted header\n", name ); + Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) have corrupted header\n", name ); return false; } if( header.dsPixelFormat.dwSize != sizeof( dds_pixf_t )) // size of the structure { - MsgDev( D_ERROR, "Image_LoadDDS: (%s) have corrupt pixelformat header\n", name ); + Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) have corrupt pixelformat header\n", name ); return false; } @@ -284,7 +281,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize ) if( image.type == PF_UNKNOWN ) { - MsgDev( D_WARN, "Image_LoadDDS: (%s) has unrecognized type\n", name ); + Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) has unrecognized type\n", name ); return false; } diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index e336ef32..0d883b2c 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -60,17 +60,17 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) // uncompressed colormapped image if( targa_header.pixel_size != 8 ) { - MsgDev( D_WARN, "Image_LoadTGA: (%s) Only 8 bit images supported for type 1 and 9\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit images supported for type 1 and 9\n", name ); return false; } if( targa_header.colormap_length != 256 ) { - MsgDev( D_WARN, "Image_LoadTGA: (%s) Only 8 bit colormaps are supported for type 1 and 9\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit colormaps are supported for type 1 and 9\n", name ); return false; } if( targa_header.colormap_index ) { - MsgDev( D_WARN, "Image_LoadTGA: (%s) colormap_index is not supported for type 1 and 9\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) colormap_index is not supported for type 1 and 9\n", name ); return false; } if( targa_header.colormap_size == 24 ) @@ -95,7 +95,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) } else { - MsgDev( D_WARN, "Image_LoadTGA: (%s) only 24 and 32 bit colormaps are supported for type 1 and 9\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) only 24 and 32 bit colormaps are supported for type 1 and 9\n", name ); return false; } } @@ -104,7 +104,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) // uncompressed or RLE compressed RGB if( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) { - MsgDev( D_WARN, "Image_LoadTGA: (%s) Only 32 or 24 bit images supported for type 2 and 10\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 32 or 24 bit images supported for type 2 and 10\n", name ); return false; } } @@ -113,7 +113,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) // uncompressed greyscale if( targa_header.pixel_size != 8 ) { - MsgDev( D_WARN, "Image_LoadTGA: (%s) Only 8 bit images supported for type 3 and 11\n", name ); + Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit images supported for type 3 and 11\n", name ); return false; } } @@ -257,7 +257,6 @@ qboolean Image_SaveTGA( const char *name, rgbdata_t *pix ) case PF_RGBA_32: case PF_BGRA_32: pixel_size = 4; break; default: - MsgDev( D_ERROR, "Image_SaveTGA: unsupported image type %s\n", PFDesc[pix->type].name ); Mem_Free( buffer ); return false; } diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index e8e48141..775b041f 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -81,36 +81,13 @@ static byte palette_hl[768] = 147,255,247,199,255,255,255,159,91,83 }; -static float FILTER[NUM_FILTERS][FILTER_SIZE][FILTER_SIZE] = -{ -{ // regular blur -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, -{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, -{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -}, -{ // light blur -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, -{ 0.0f, 1.0f, 4.0f, 1.0f, 0.0f }, -{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -}, -{ // find edges -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -{ 0.0f, -1.0f, -1.0f, -1.0f, 0.0f }, -{ 0.0f, -1.0f, 8.0f, -1.0f, 0.0f }, -{ 0.0f, -1.0f, -1.0f, -1.0f, 0.0f }, -{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, -}, -{ // emboss +static float img_emboss[FILTER_SIZE][FILTER_SIZE] = +{ {-0.7f, -0.7f, -0.7f, -0.7f, 0.0f }, {-0.7f, -0.7f, -0.7f, 0.0f, 0.7f }, {-0.7f, -0.7f, 0.0f, 0.7f, 0.7f }, {-0.7f, 0.0f, 0.7f, 0.7f, 0.7f }, { 0.0f, 0.7f, 0.7f, 0.7f, 0.7f }, -} }; /* @@ -1361,7 +1338,7 @@ Filtering algorithm from http://www.student.kuleuven.ac.be/~m0216922/CG/filterin All credit due ================== */ -qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias, flFlags_t flags, GLenum blendFunc ) +static void Image_ApplyFilter( rgbdata_t *pic, float factor ) { int i, x, y; uint *fin, *fout; @@ -1369,7 +1346,7 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias // first expand the image into 32-bit buffer pic = Image_DecompressInternal( pic ); - + factor = bound( 0.0f, factor, 1.0f ); size = image.width * image.height * 4; image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, size ); fout = (uint *)image.tempbuffer; @@ -1381,6 +1358,7 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias { vec3_t vout = { 0.0f, 0.0f, 0.0f }; int pos_x, pos_y; + float avg; for( pos_x = 0; pos_x < FILTER_SIZE; pos_x++ ) { @@ -1391,9 +1369,9 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias // casting's a unary operation anyway, so the othermost set of brackets in the left part // of the rvalue should not be necessary... but i'm paranoid when it comes to C... - vout[0] += ((float)((byte *)&fin[img_y * image.width + img_x])[0]) * FILTER[filter][pos_x][pos_y]; - vout[1] += ((float)((byte *)&fin[img_y * image.width + img_x])[1]) * FILTER[filter][pos_x][pos_y]; - vout[2] += ((float)((byte *)&fin[img_y * image.width + img_x])[2]) * FILTER[filter][pos_x][pos_y]; + vout[0] += ((float)((byte *)&fin[img_y * image.width + img_x])[0]) * img_emboss[pos_x][pos_y]; + vout[1] += ((float)((byte *)&fin[img_y * image.width + img_x])[1]) * img_emboss[pos_x][pos_y]; + vout[2] += ((float)((byte *)&fin[img_y * image.width + img_x])[2]) * img_emboss[pos_x][pos_y]; } } @@ -1401,20 +1379,17 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias for( i = 0; i < 3; i++ ) { vout[i] *= factor; - vout[i] += bias; + vout[i] += 128.0f; // base vout[i] = bound( 0.0f, vout[i], 255.0f ); } - if( flags & FILTER_GRAYSCALE ) - { - // NTSC greyscale conversion standard - float avg = (vout[0] * 30.0f + vout[1] * 59.0f + vout[2] * 11.0f) / 100.0f; + // NTSC greyscale conversion standard + avg = (vout[0] * 30.0f + vout[1] * 59.0f + vout[2] * 11.0f) / 100.0f; - // divide by 255 so GL operations work as expected - vout[0] = avg / 255.0f; - vout[1] = avg / 255.0f; - vout[2] = avg / 255.0f; - } + // divide by 255 so GL operations work as expected + vout[0] = avg / 255.0f; + vout[1] = avg / 255.0f; + vout[2] = avg / 255.0f; // write to temp - first, write data in (to get the alpha channel quickly and // easily, which will be left well alone by this particular operation...!) @@ -1429,29 +1404,9 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias float src = ((float)((byte *)&fin[y * image.width + x])[i]) / 255.0f; float tmp; - switch( blendFunc ) - { - case GL_ADD: - tmp = vout[i] + src; - break; - case GL_BLEND: - // default is FUNC_ADD here - // CsS + CdD works out as Src * Dst * 2 - tmp = vout[i] * src * 2.0f; - break; - case GL_DECAL: - // same as GL_REPLACE unless there's alpha, which we ignore for this - case GL_REPLACE: - tmp = vout[i]; - break; - case GL_ADD_SIGNED: - tmp = (vout[i] + src) - 0.5f; - break; - case GL_MODULATE: - default: // same as default - tmp = vout[i] * src; - break; - } + // default is GL_BLEND here + // CsS + CdD works out as Src * Dst * 2 + tmp = vout[i] * src * 2.0f; // multiply back by 255 to get the proper byte scale tmp *= 255.0f; @@ -1466,11 +1421,9 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias // copy result back memcpy( fin, fout, size ); - - return true; } -qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgfilter_t *filter ) +qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, float bumpscale ) { rgbdata_t *pic = *pix; qboolean result = true; @@ -1483,7 +1436,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgf return false; } - if( !flags && !filter ) + if( !flags ) { // clear any force flags image.force_flags = 0; @@ -1497,7 +1450,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgf ClearBits( pic->flags, IMAGE_HAS_LUMA ); } - if( flags & IMAGE_REMAP ) + if( FBitSet( flags, IMAGE_REMAP )) { // NOTE: user should keep copy of indexed image manually for new changes if( Image_RemapInternal( pic, width, height )) @@ -1505,10 +1458,14 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, imgf } // update format to RGBA if any - if( flags & IMAGE_FORCE_RGBA ) pic = Image_DecompressInternal( pic ); - if( flags & IMAGE_LIGHTGAMMA ) pic = Image_LightGamma( pic ); + if( FBitSet( flags, IMAGE_FORCE_RGBA )) + pic = Image_DecompressInternal( pic ); - if( filter ) Image_ApplyFilter( pic, filter->filter, filter->factor, filter->bias, filter->flags, filter->blendFunc ); + if( FBitSet( flags, IMAGE_LIGHTGAMMA )) + pic = Image_LightGamma( pic ); + + if( FBitSet( flags, IMAGE_EMBOSS )) + Image_ApplyFilter( pic, bumpscale ); out = Image_FlipInternal( pic->buffer, &pic->width, &pic->height, pic->type, flags ); if( pic->buffer != out ) memcpy( pic->buffer, image.tempbuffer, pic->size ); diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 83fae094..954e3c8c 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -31,7 +31,7 @@ qboolean Image_LoadPAL( const char *name, const byte *buffer, size_t filesize ) if( filesize != 768 ) { - MsgDev( D_ERROR, "Image_LoadPAL: (%s) have invalid size (%d should be %d)\n", name, filesize, 768 ); + Con_DPrintf( S_ERROR "Image_LoadPAL: (%s) have invalid size (%d should be %d)\n", name, filesize, 768 ); return false; } @@ -82,7 +82,7 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize ) int numcolors; if( image.hint == IL_HINT_Q1 ) - return false; // Quake1 doesn't have qfonts + return false; // Quake1 doesn't have qfonts if( filesize < sizeof( font )) return false; @@ -120,8 +120,6 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize ) } else { - if( image.hint == IL_HINT_NO ) - MsgDev( D_ERROR, "Image_LoadFNT: (%s) have invalid palette size %d\n", name, numcolors ); return false; } @@ -151,7 +149,8 @@ qboolean Image_LoadMDL( const char *name, const byte *buffer, size_t filesize ) pixels = image.width * image.height; fin = (byte *)pin->index; // setup buffer - if( !Image_ValidSize( name )) return false; + if( !Image_ValidSize( name )) + return false; if( image.hint == IL_HINT_HL ) { @@ -169,8 +168,6 @@ qboolean Image_LoadMDL( const char *name, const byte *buffer, size_t filesize ) } else { - if( image.hint == IL_HINT_NO ) - MsgDev( D_ERROR, "Image_LoadMDL: lump (%s) is corrupted\n", name ); return false; // unknown or unsupported mode rejected } @@ -193,10 +190,7 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, size_t filesize ) if( image.hint == IL_HINT_HL ) { if( !image.d_currentpal ) - { - MsgDev( D_ERROR, "Image_LoadSPR: (%s) palette not installed\n", name ); return false; - } } else if( image.hint == IL_HINT_Q1 ) { @@ -213,10 +207,7 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, size_t filesize ) image.height = pin->height; if( filesize < image.width * image.height ) - { - MsgDev( D_ERROR, "Image_LoadSPR: file (%s) have invalid size\n", name ); return false; - } if( filesize == ( image.width * image.height * 4 )) truecolor = true; @@ -263,10 +254,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize ) int i, pixels; if( filesize < sizeof( lmp )) - { - MsgDev( D_ERROR, "Image_LoadLMP: file (%s) have invalid size\n", name ); return false; - } // valve software trick (particle palette) if( Q_stristr( name, "palette.lmp" )) @@ -296,10 +284,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize ) pixels = image.width * image.height; if( filesize < sizeof( lmp ) + pixels ) - { - MsgDev( D_ERROR, "Image_LoadLMP: file (%s) have invalid size %d\n", name, filesize ); return false; - } if( !Image_ValidSize( name )) return false; @@ -352,10 +337,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) int reflectivity[3] = { 0, 0, 0 }; if( filesize < sizeof( mip )) - { - MsgDev( D_ERROR, "Image_LoadMIP: file (%s) have invalid size\n", name ); return false; - } memcpy( &mip, buffer, sizeof( mip )); image.width = mip.width; @@ -466,8 +448,6 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) } else { - if( image.hint == IL_HINT_NO ) - MsgDev( D_ERROR, "Image_LoadMIP: lump (%s) is corrupted\n", name ); return false; // unknown or unsupported mode rejected } diff --git a/engine/common/input.c b/engine/common/input.c index 7bddbf4c..786e8308 100644 --- a/engine/common/input.c +++ b/engine/common/input.c @@ -43,7 +43,7 @@ static byte scan_to_key[128] = K_SHIFT,'\\','z','x','c','v','b','n','m',',','.','/',K_SHIFT, '*',K_ALT,' ',K_CAPSLOCK, K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10, - K_PAUSE,0,K_HOME,K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5, + K_PAUSE,K_SCROLLOCK,K_HOME,K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END,K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0,0,0,K_F11,K_F12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/engine/common/keys.c b/engine/common/keys.c index 8ddcc66e..b2a6a481 100644 --- a/engine/common/keys.c +++ b/engine/common/keys.c @@ -49,6 +49,7 @@ keyname_t keynames[] = {"CTRL", K_CTRL, "+attack" }, {"SHIFT", K_SHIFT, "+speed" }, {"CAPSLOCK", K_CAPSLOCK, "" }, +{"SCROLLOCK", K_SCROLLOCK, "" }, {"F1", K_F1, "cmd help" }, {"F2", K_F2, "menu_savegame" }, {"F3", K_F3, "menu_loadgame" }, @@ -202,7 +203,7 @@ const char *Key_KeynumToString( int keynum ) if ( keynum < 0 || keynum > 255 ) return ""; // check for printable ascii (don't use quote) - if( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' ) + if( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' && keynum != K_SCROLLOCK ) { tinystr[0] = keynum; tinystr[1] = 0; @@ -419,8 +420,10 @@ void Key_WriteBindings( file_t *f ) for( i = 0; i < 256; i++ ) { - if( keys[i].binding && keys[i].binding[0] ) - FS_Printf( f, "bind %s \"%s\"\n", Key_KeynumToString( i ), keys[i].binding ); + if( !COM_CheckString( keys[i].binding )) + continue; + + FS_Printf( f, "bind %s \"%s\"\n", Key_KeynumToString( i ), keys[i].binding ); } } @@ -436,8 +439,10 @@ void Key_Bindlist_f( void ) for( i = 0; i < 256; i++ ) { - if( keys[i].binding && keys[i].binding[0] ) - Con_Printf( "%s \"%s\"\n", Key_KeynumToString( i ), keys[i].binding ); + if( !COM_CheckString( keys[i].binding )) + continue; + + Con_Printf( "%s \"%s\"\n", Key_KeynumToString( i ), keys[i].binding ); } } diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index c357f0bf..8a1ab247 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -1771,7 +1771,6 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) int num, max, altmax; qboolean custom_palette; char texname[64]; - imgfilter_t *filter; mip_t *mt; int i, j; @@ -1819,7 +1818,6 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) // convert to lowercase Q_strncpy( tx->name, mt->name, sizeof( tx->name )); Q_strnlwr( tx->name, tx->name, sizeof( tx->name )); - filter = R_FindTexFilter( tx->name ); // grab texture filter custom_palette = false; tx->width = mt->width; @@ -1872,7 +1870,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( FS_FileExists( texpath, false )) { - tx->gl_texturenum = GL_LoadTexture( texpath, NULL, 0, 0, filter ); + tx->gl_texturenum = GL_LoadTexture( texpath, NULL, 0, TF_ALLOW_EMBOSS ); bmod->wadlist.wadusage[j]++; // this wad are really used break; } @@ -1888,7 +1886,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) if( custom_palette ) size += sizeof( short ) + 768; Q_snprintf( texname, sizeof( texname ), "#%s:%s.mip", loadstat.name, mt->name ); - tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0, filter ); + tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_ALLOW_EMBOSS ); } // if texture is completely missed @@ -1911,7 +1909,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); if( custom_palette ) size += sizeof( short ) + 768; - tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_MAKELUMA, NULL ); + tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_MAKELUMA ); } else { @@ -1936,7 +1934,7 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) } // okay, loading it from wad or hi-res version - tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_MAKELUMA, NULL ); + tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_MAKELUMA ); if( src ) Mem_Free( src ); } } diff --git a/engine/common/model.c b/engine/common/model.c index 4f20f69f..7f6ccaaa 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -201,6 +201,7 @@ void Mod_Shutdown( void ) ================== Mod_FindName +never return NULL ================== */ model_t *Mod_FindName( const char *filename, qboolean trackCRC ) @@ -208,9 +209,6 @@ model_t *Mod_FindName( const char *filename, qboolean trackCRC ) char modname[MAX_QPATH]; model_t *mod; int i; - - if( !COM_CheckString( filename )) - return NULL; Q_strncpy( modname, filename, sizeof( modname )); @@ -388,7 +386,12 @@ Loads in a model for the given name */ model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC ) { - model_t *mod = Mod_FindName( name, trackCRC ); + model_t *mod; + + if( !COM_CheckString( name )) + return NULL; + + mod = Mod_FindName( name, trackCRC ); return Mod_LoadModel( mod, crash ); } diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 6a05da3f..91d5dcba 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -451,7 +451,7 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) return false; } - MsgDev( D_ERROR, "NET_CompareAdr: bad address type\n" ); + Con_DPrintf( S_ERROR "NET_CompareAdr: bad address type\n" ); return false; } @@ -893,7 +893,7 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len } else { - MsgDev( D_REPORT, "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); + Con_Reportf( "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); } } else @@ -908,7 +908,7 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len case WSAEMSGSIZE: break; default: // let's continue even after errors - MsgDev( D_ERROR, "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); + Con_DPrintf( S_ERROR "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); break; } } @@ -1059,11 +1059,11 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to // let dedicated servers continue after errors if( host.type == HOST_DEDICATED ) { - MsgDev( D_ERROR, "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); + Con_DPrintf( S_ERROR "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); } else if( err == WSAEADDRNOTAVAIL || err == WSAENOBUFS ) { - MsgDev( D_ERROR, "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); + Con_DPrintf( S_ERROR "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to )); } else { @@ -1149,13 +1149,13 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { err = pWSAGetLastError(); if( err != WSAEAFNOSUPPORT ) - MsgDev( D_WARN, "NET_UDPSocket: port: %d socket: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d socket: %s\n", port, NET_ErrorString( )); return INVALID_SOCKET; } if( pIoctlSocket( net_socket, FIONBIO, &optval ) == SOCKET_ERROR ) { - MsgDev( D_WARN, "NET_UDPSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString( )); pCloseSocket( net_socket ); return INVALID_SOCKET; } @@ -1163,7 +1163,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast // make it broadcast capable if( pSetSockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (const char *)&optval, sizeof( optval )) == SOCKET_ERROR ) { - MsgDev( D_WARN, "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( )); pCloseSocket( net_socket ); return INVALID_SOCKET; } @@ -1172,7 +1172,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { if( pSetSockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) == SOCKET_ERROR ) { - MsgDev( D_WARN, "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( )); pCloseSocket( net_socket ); return INVALID_SOCKET; } @@ -1204,7 +1204,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast if( pBind( net_socket, (void *)&addr, sizeof( addr )) == SOCKET_ERROR ) { - MsgDev( D_WARN, "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); pCloseSocket( net_socket ); return INVALID_SOCKET; } @@ -1213,7 +1213,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { optval = 1; if( pSetSockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&optval, sizeof( optval )) == SOCKET_ERROR ) - MsgDev( D_WARN, "NET_UDPSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString( )); } return net_socket; @@ -1294,7 +1294,7 @@ void NET_GetLocalAddress( void ) if( pGetSockName( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) == SOCKET_ERROR ) { // this may happens if multiple clients running on single machine - MsgDev( D_ERROR, "Could not get TCP/IP address. Reason: %s\n", NET_ErrorString( )); + Con_DPrintf( S_ERROR "Could not get TCP/IP address. Reason: %s\n", NET_ErrorString( )); // net.allow_ip = false; } else @@ -1306,7 +1306,7 @@ void NET_GetLocalAddress( void ) } else { - MsgDev( D_ERROR, "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); + Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); } } else @@ -1463,13 +1463,13 @@ void NET_Init( void ) if( !NET_OpenWinSock( )) // loading wsock32.dll { - MsgDev( D_ERROR, "network failed to load wsock32.dll.\n" ); + Con_DPrintf( S_ERROR "network failed to load wsock32.dll.\n" ); return; } if( pWSAStartup( MAKEWORD( 1, 1 ), &net.winsockdata )) { - MsgDev( D_ERROR, "network initialization failed.\n" ); + Con_DPrintf( S_ERROR "network initialization failed.\n" ); NET_FreeWinSock(); return; } @@ -1488,7 +1488,7 @@ void NET_Init( void ) net.sequence_number = 1; net.initialized = true; - MsgDev( D_REPORT, "Base networking initialized.\n" ); + Con_Reportf( "Base networking initialized.\n" ); } diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index 703905b4..ca3bac13 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -118,7 +118,7 @@ load_internal: } if( filename[0] != '#' ) - Con_Reportf( S_WARN "FS_LoadSound: couldn't load \"%s\"\n", loadname ); + Con_DPrintf( S_WARN "FS_LoadSound: couldn't load \"%s\"\n", loadname ); return NULL; } diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index bc2acbdc..b98454df 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -71,16 +71,16 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) return false; #ifdef _DEBUG - if( ret ) MsgDev( D_ERROR, "%s\n", get_error( mpeg )); + if( ret ) Con_DPrintf( S_ERROR "%s\n", get_error( mpeg )); #endif // trying to read header if( !feed_mpeg_header( mpeg, buffer, FRAME_SIZE, filesize, &sc )) { #ifdef _DEBUG - MsgDev( D_ERROR, "Sound_LoadMPG: failed to load (%s): %s\n", name, get_error( mpeg )); + Con_DPrintf( S_ERROR "Sound_LoadMPG: failed to load (%s): %s\n", name, get_error( mpeg )); #else - MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadMPG: (%s) is probably corrupted\n", name ); #endif close_decoder( mpeg ); return false; @@ -97,7 +97,7 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) if( !sound.size ) { // bad mpeg file ? - MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadMPG: (%s) is probably corrupted\n", name ); close_decoder( mpeg ); return false; } @@ -164,22 +164,22 @@ stream_t *Stream_OpenMPG( const char *filename ) // couldn't create decoder if(( mpeg = create_decoder( &ret )) == NULL ) { - MsgDev( D_ERROR, "Stream_OpenMPG: couldn't create decoder\n" ); + Con_DPrintf( S_ERROR "Stream_OpenMPG: couldn't create decoder\n" ); Mem_Free( stream ); FS_Close( file ); return NULL; } #ifdef _DEBUG - if( ret ) MsgDev( D_ERROR, "%s\n", get_error( mpeg )); + if( ret ) Con_DPrintf( S_ERROR "%s\n", get_error( mpeg )); #endif // trying to open stream and read header if( !open_mpeg_stream( mpeg, file, FS_Read, FS_Seek, &sc )) { #ifdef _DEBUG - MsgDev( D_ERROR, "Stream_OpenMPG: failed to load (%s): %s\n", filename, get_error( mpeg )); + Con_DPrintf( S_ERROR "Stream_OpenMPG: failed to load (%s): %s\n", filename, get_error( mpeg )); #else - MsgDev( D_ERROR, "Stream_OpenMPG: (%s) is probably corrupted\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenMPG: (%s) is probably corrupted\n", filename ); #endif close_decoder( mpeg ); Mem_Free( stream ); diff --git a/engine/common/soundlib/snd_wav.c b/engine/common/soundlib/snd_wav.c index 7757813b..80ca54b1 100644 --- a/engine/common/soundlib/snd_wav.c +++ b/engine/common/soundlib/snd_wav.c @@ -155,7 +155,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) if( !( iff_dataPtr && !Q_strncmp( iff_dataPtr + 8, "WAVE", 4 ))) { - MsgDev( D_ERROR, "Sound_LoadWAV: %s missing 'RIFF/WAVE' chunks\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'RIFF/WAVE' chunks\n", name ); return false; } @@ -165,7 +165,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) if( !iff_dataPtr ) { - MsgDev( D_ERROR, "Sound_LoadWAV: %s missing 'fmt ' chunk\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'fmt ' chunk\n", name ); return false; } @@ -176,7 +176,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) { if( fmt != 85 ) { - MsgDev( D_ERROR, "Sound_LoadWAV: %s not a microsoft PCM format\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: %s not a microsoft PCM format\n", name ); return false; } else @@ -189,7 +189,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) sound.channels = GetLittleShort(); if( sound.channels != 1 && sound.channels != 2 ) { - MsgDev( D_ERROR, "Sound_LoadWAV: only mono and stereo WAV files supported (%s)\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: only mono and stereo WAV files supported (%s)\n", name ); return false; } @@ -201,7 +201,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) if( sound.width != 1 && sound.width != 2 ) { - MsgDev( D_WARN, "Sound_LoadWAV: only 8 and 16 bit WAV files supported (%s)\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: only 8 and 16 bit WAV files supported (%s)\n", name ); return false; } @@ -235,7 +235,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) if( !iff_dataPtr ) { - MsgDev( D_WARN, "Sound_LoadWAV: %s missing 'data' chunk\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'data' chunk\n", name ); return false; } @@ -246,7 +246,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) { if( samples < sound.samples ) { - MsgDev( D_ERROR, "Sound_LoadWAV: %s has a bad loop length\n", name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: %s has a bad loop length\n", name ); return false; } } @@ -254,7 +254,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize ) if( sound.samples <= 0 ) { - MsgDev( D_ERROR, "Sound_LoadWAV: file with %i samples (%s)\n", sound.samples, name ); + Con_DPrintf( S_ERROR "Sound_LoadWAV: file with %i samples (%s)\n", sound.samples, name ); return false; } @@ -326,7 +326,7 @@ stream_t *Stream_OpenWAV( const char *filename ) // find "RIFF" chunk if( !StreamFindNextChunk( file, "RIFF", &last_chunk )) { - MsgDev( D_ERROR, "Stream_OpenWAV: %s missing RIFF chunk\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing RIFF chunk\n", filename ); FS_Close( file ); return NULL; } @@ -334,7 +334,7 @@ stream_t *Stream_OpenWAV( const char *filename ) FS_Read( file, chunkName, 4 ); if( !Q_strncmp( chunkName, "WAVE", 4 )) { - MsgDev( D_ERROR, "Stream_OpenWAV: %s missing WAVE chunk\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing WAVE chunk\n", filename ); FS_Close( file ); return NULL; } @@ -344,7 +344,7 @@ stream_t *Stream_OpenWAV( const char *filename ) last_chunk = iff_data; if( !StreamFindNextChunk( file, "fmt ", &last_chunk )) { - MsgDev( D_ERROR, "Stream_OpenWAV: %s missing 'fmt ' chunk\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing 'fmt ' chunk\n", filename ); FS_Close( file ); return NULL; } @@ -354,7 +354,7 @@ stream_t *Stream_OpenWAV( const char *filename ) FS_Read( file, &t, sizeof( t )); if( t != 1 ) { - MsgDev( D_ERROR, "Stream_OpenWAV: %s not a microsoft PCM format\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenWAV: %s not a microsoft PCM format\n", filename ); FS_Close( file ); return NULL; } @@ -375,7 +375,7 @@ stream_t *Stream_OpenWAV( const char *filename ) last_chunk = iff_data; if( !StreamFindNextChunk( file, "data", &last_chunk )) { - MsgDev( D_ERROR, "Stream_OpenWAV: %s missing 'data' chunk\n", filename ); + Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing 'data' chunk\n", filename ); FS_Close( file ); return NULL; } diff --git a/engine/common/sys_win.c b/engine/common/sys_win.c index c476ae6e..c2e76e89 100644 --- a/engine/common/sys_win.c +++ b/engine/common/sys_win.c @@ -623,39 +623,4 @@ void Sys_Print( const char *pMsg ) Sys_PrintLog( logbuf ); Con_WinPrint( buffer ); -} - -/* -================ -MsgDev - -formatted developer message -================ -*/ -void MsgDev( int type, const char *pMsg, ... ) -{ - static char text[MAX_PRINT_MSG]; - va_list argptr; - - if( type >= D_REPORT && host_developer.value < DEV_EXTENDED ) - return; - - va_start( argptr, pMsg ); - Q_vsnprintf( text, sizeof( text ) - 1, pMsg, argptr ); - va_end( argptr ); - - switch( type ) - { - case D_WARN: - Sys_Print( va( "^3Warning:^7 %s", text )); - break; - case D_ERROR: - Sys_Print( va( "^1Error:^7 %s", text )); - break; - case D_INFO: - case D_NOTE: - case D_REPORT: - Sys_Print( text ); - break; - } } \ No newline at end of file diff --git a/engine/common/system.h b/engine/common/system.h index 88b08446..d3384213 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -104,7 +104,6 @@ char *Con_Input( void ); // text messages #define Msg Con_Printf -void MsgDev( int level, const char *pMsg, ... ); #ifdef __cplusplus } diff --git a/engine/keydefs.h b/engine/keydefs.h index ea22139f..c789189c 100644 --- a/engine/keydefs.h +++ b/engine/keydefs.h @@ -23,6 +23,7 @@ #define K_ENTER 13 #define K_ESCAPE 27 #define K_SPACE 32 +#define K_SCROLLOCK 70 // normal keys should be passed as lowercased ascii diff --git a/engine/server/server.h b/engine/server/server.h index ac19976b..d4d7d2c6 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -584,6 +584,7 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, void SV_PlaybackReliableEvent( sizebuf_t *msg, word eventindex, float delay, event_args_t *args ); int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample, int vol, float attn, int flags, int pitch, const vec3_t pos ); qboolean SV_BoxInPVS( const vec3_t org, const vec3_t absmin, const vec3_t absmax ); +void SV_QueueChangeLevel( const char *level, const char *landname ); void SV_WriteEntityPatch( const char *filename ); float SV_AngleMod( float ideal, float current, float speed ); void SV_SpawnEntities( const char *mapname ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 38861d85..6001ad96 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1232,13 +1232,13 @@ void SV_PutClientInServer( sv_client_t *cl ) SetBits( ent->v.flags, FL_GODMODE|FL_NOTARGET ); cl->pViewEntity = NULL; // reset pViewEntity + } - if( svgame.globals->cdAudioTrack ) - { - MSG_BeginServerCmd( &msg, svc_stufftext ); - MSG_WriteString( &msg, va( "cd loop %3d\n", svgame.globals->cdAudioTrack )); - svgame.globals->cdAudioTrack = 0; - } + if( svgame.globals->cdAudioTrack ) + { + MSG_BeginServerCmd( &msg, svc_stufftext ); + MSG_WriteString( &msg, va( "cd loop %3d\n", svgame.globals->cdAudioTrack )); + svgame.globals->cdAudioTrack = 0; } #ifdef HACKS_RELATED_HLMODS @@ -1731,6 +1731,9 @@ static qboolean SV_Godmode_f( sv_client_t *cl ) return true; pEntity->v.flags = pEntity->v.flags ^ FL_GODMODE; + if( pEntity->v.takedamage == DAMAGE_AIM ) + pEntity->v.takedamage = DAMAGE_NO; + else pEntity->v.takedamage = DAMAGE_AIM; if( !FBitSet( pEntity->v.flags, FL_GODMODE )) SV_ClientPrintf( cl, "godmode OFF\n" ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index ea704538..72954f63 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -440,6 +440,42 @@ void SV_Reload_f( void ) COM_LoadLevel( sv_hostmap->string, false ); } +/* +================== +SV_ChangeLevel_f + +classic change level +================== +*/ +void SV_ChangeLevel_f( void ) +{ + if( Cmd_Argc() != 2 ) + { + Con_Printf( S_USAGE "changelevel \n" ); + return; + } + + SV_QueueChangeLevel( Cmd_Argv( 1 ), NULL ); +} + +/* +================== +SV_ChangeLevel2_f + +smooth change level +================== +*/ +void SV_ChangeLevel2_f( void ) +{ + if( Cmd_Argc() != 3 ) + { + Con_Printf( S_USAGE "changelevel2 \n" ); + return; + } + + SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 )); +} + /* ================== SV_Kick_f @@ -802,6 +838,7 @@ void SV_InitHostCommands( void ) Cmd_AddCommand( "load", SV_Load_f, "load a saved game file" ); Cmd_AddCommand( "loadquick", SV_QuickLoad_f, "load a quick-saved game file" ); Cmd_AddCommand( "reload", SV_Reload_f, "continue from latest save or restart level" ); + Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" ); } } @@ -824,13 +861,14 @@ void SV_InitOperatorCommands( void ) Cmd_AddCommand( "edict_usage", SV_EdictUsage_f, "show info about edicts usage" ); Cmd_AddCommand( "entity_info", SV_EntityInfo_f, "show more info about edicts" ); Cmd_AddCommand( "shutdownserver", SV_KillServer_f, "shutdown current server" ); + Cmd_AddCommand( "changelevel", SV_ChangeLevel_f, "change level" ); + Cmd_AddCommand( "changelevel2", SV_ChangeLevel2_f, "smooth change level" ); if( host.type == HOST_NORMAL ) { Cmd_AddCommand( "save", SV_Save_f, "save the game to a file" ); Cmd_AddCommand( "savequick", SV_QuickSave_f, "save the game to the quicksave" ); Cmd_AddCommand( "autosave", SV_AutoSave_f, "save the game to 'autosave' file" ); - Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" ); } else if( host.type == HOST_DEDICATED ) { @@ -857,12 +895,13 @@ void SV_KillOperatorCommands( void ) Cmd_RemoveCommand( "edict_usage" ); Cmd_RemoveCommand( "entity_info" ); Cmd_RemoveCommand( "shutdownserver" ); + Cmd_RemoveCommand( "changelevel" ); + Cmd_RemoveCommand( "changelevel2" ); if( host.type == HOST_NORMAL ) { Cmd_RemoveCommand( "save" ); Cmd_RemoveCommand( "savequick" ); - Cmd_RemoveCommand( "killsave" ); Cmd_RemoveCommand( "autosave" ); } else if( host.type == HOST_DEDICATED ) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index d5fe83e6..34955bd2 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -658,6 +658,91 @@ qboolean SV_BoxInPVS( const vec3_t org, const vec3_t absmin, const vec3_t absmax return true; } +/* +============= +SV_ChangeLevel + +Issue changing level +============= +*/ +void SV_QueueChangeLevel( const char *level, const char *landname ) +{ + int flags, smooth = false; + char mapname[MAX_QPATH]; + char *spawn_entity; + + // hold mapname to other place + Q_strncpy( mapname, level, sizeof( mapname )); + COM_StripExtension( mapname ); + + if( COM_CheckString( landname )) + smooth = true; + + // determine spawn entity classname + if( svs.maxclients == 1 ) + spawn_entity = GI->sp_entity; + else spawn_entity = GI->mp_entity; + + flags = SV_MapIsValid( mapname, spawn_entity, landname ); + + if( FBitSet( flags, MAP_INVALID_VERSION )) + { + Con_Printf( S_ERROR "changelevel: %s is invalid or not supported\n", mapname ); + return; + } + + if( !FBitSet( flags, MAP_IS_EXIST )) + { + Con_Printf( S_ERROR "changelevel: map %s doesn't exist\n", mapname ); + return; + } + + if( smooth && !FBitSet( flags, MAP_HAS_LANDMARK )) + { + if( sv_validate_changelevel->value ) + { + // NOTE: we find valid map but specified landmark it's doesn't exist + // run simple changelevel like in q1, throw warning + Con_Printf( S_WARN "changelevel: %s doesn't contain landmark [%s]. smooth transition was disabled\n", mapname, landname ); + smooth = false; + } + } + + if( svs.maxclients > 1 ) + smooth = false; // multiplayer doesn't support smooth transition + + if( smooth && !Q_stricmp( sv.name, level )) + { + Con_Printf( S_ERROR "can't changelevel with same map. Ignored.\n" ); + return; + } + + if( !smooth && !FBitSet( flags, MAP_HAS_SPAWNPOINT )) + { + if( sv_validate_changelevel->value ) + { + Con_Printf( S_ERROR "changelevel: %s doesn't have a valid spawnpoint. Ignored.\n", mapname ); + return; + } + } + + // bad changelevel position invoke enables in one-way transition + if( sv.framecount < 15 ) + { + if( sv_validate_changelevel->value ) + { + Con_Printf( S_WARN "an infinite changelevel was detected and will be disabled until a next save\\restore\n" ); + return; // lock with svs.spawncount here + } + } + + SV_SkipUpdates (); + + // changelevel will be executed on a next frame + if( smooth ) COM_ChangeLevel( mapname, landname, sv.background ); // Smoothed Half-Life changelevel + else COM_ChangeLevel( mapname, NULL, sv.background ); // Classic Quake changlevel +} + /* ============== SV_WriteEntityPatch @@ -1312,11 +1397,8 @@ pfnChangeLevel */ void pfnChangeLevel( const char *level, const char *landmark ) { - int flags, smooth = false; static uint last_spawncount = 0; - char mapname[MAX_QPATH]; char landname[MAX_QPATH]; - char *spawn_entity; char *text; if( !COM_CheckString( level ) || sv.state != ss_active ) @@ -1326,10 +1408,6 @@ void pfnChangeLevel( const char *level, const char *landmark ) if( svs.spawncount == last_spawncount ) return; last_spawncount = svs.spawncount; - - // hold mapname to other place - Q_strncpy( mapname, level, sizeof( mapname )); - COM_StripExtension( mapname ); landname[0] ='\0'; #ifdef HACKS_RELATED_HLMODS @@ -1346,72 +1424,7 @@ void pfnChangeLevel( const char *level, const char *landmark ) #else Q_strncpy( landname, landmark, sizeof( landname )); #endif - if( COM_CheckString( landname )) - smooth = true; - - // determine spawn entity classname - if( svs.maxclients == 1 ) - spawn_entity = GI->sp_entity; - else spawn_entity = GI->mp_entity; - - flags = SV_MapIsValid( mapname, spawn_entity, landname ); - - if( FBitSet( flags, MAP_INVALID_VERSION )) - { - Con_Printf( S_ERROR "changelevel: %s is invalid or not supported\n", mapname ); - return; - } - - if( !FBitSet( flags, MAP_IS_EXIST )) - { - Con_Printf( S_ERROR "changelevel: map %s doesn't exist\n", mapname ); - return; - } - - if( smooth && !FBitSet( flags, MAP_HAS_LANDMARK )) - { - if( sv_validate_changelevel->value ) - { - // NOTE: we find valid map but specified landmark it's doesn't exist - // run simple changelevel like in q1, throw warning - Con_Printf( S_WARN "changelevel: %s doesn't contain landmark [%s]. smooth transition was disabled\n", mapname, landname ); - smooth = false; - } - } - - if( svs.maxclients > 1 ) - smooth = false; // multiplayer doesn't support smooth transition - - if( smooth && !Q_stricmp( sv.name, level )) - { - Con_Printf( S_ERROR "can't changelevel with same map. Ignored.\n" ); - return; - } - - if( !smooth && !FBitSet( flags, MAP_HAS_SPAWNPOINT )) - { - if( sv_validate_changelevel->value ) - { - Con_Printf( S_ERROR "changelevel: %s doesn't have a valid spawnpoint. Ignored.\n", mapname ); - return; - } - } - - // bad changelevel position invoke enables in one-way transition - if( sv.framecount < 15 ) - { - if( sv_validate_changelevel->value ) - { - Con_Printf( S_WARN "an infinite changelevel was detected and will be disabled until a next save\\restore\n" ); - return; // lock with svs.spawncount here - } - } - - SV_SkipUpdates (); - - // changelevel will be executed on a next frame - if( smooth ) COM_ChangeLevel( mapname, landname, sv.background ); // Smoothed Half-Life changelevel - else COM_ChangeLevel( mapname, NULL, sv.background ); // Classic Quake changlevel + SV_QueueChangeLevel( level, landname ); } /* @@ -2022,6 +2035,9 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample } else { + // TESTTEST + if( *sample == '*' ) chan = CHAN_AUTO; + // precache_sound can be used twice: cache sounds when loading // and return sound index when server is active sound_idx = SV_SoundIndex( sample ); @@ -2090,7 +2106,7 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float msg_dest = MSG_ALL; else if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) msg_dest = MSG_ALL; - else msg_dest = MSG_PAS_R; + else msg_dest = (svs.maxclients <= 1 ) ? MSG_ALL : MSG_PAS_R; // always sending stop sound command if( FBitSet( flags, SND_STOP )) @@ -2111,7 +2127,7 @@ pfnEmitAmbientSound */ void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vol, float attn, int flags, int pitch ) { - int msg_dest = MSG_PAS_R; + int msg_dest; if( sv.state == ss_loading ) SetBits( flags, SND_SPAWNING ); @@ -4598,8 +4614,11 @@ qboolean SV_ParseEdict( char **pfile, edict_t *ent ) } // no reason to keep this data - Mem_Free( pkvd[i].szKeyName ); - Mem_Free( pkvd[i].szValue ); + if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szKeyName )) + Mem_Free( pkvd[i].szKeyName ); + + if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szValue )) + Mem_Free( pkvd[i].szValue ); } if( classname ) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 812960a9..137a4bed 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -671,6 +671,7 @@ void SV_ShutdownGame( void ) SV_FinalMessage( "", true ); S_StopBackgroundTrack(); + CL_StopPlayback(); // stop demo too if( GameState->newGame ) { diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 1b4b2ca6..b2970c4a 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -780,6 +780,7 @@ Does not change the entities velocity at all trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apush, int *blocked, float flDamage ) { trace_t trace; + qboolean monsterBlock; qboolean monsterClip; int type; vec3_t end; @@ -812,10 +813,14 @@ trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apush, int SV_LinkEdict( ent, true ); + if( ent->v.movetype == MOVETYPE_WALK || ent->v.movetype == MOVETYPE_STEP || ent->v.movetype == MOVETYPE_PUSHSTEP ) + monsterBlock = true; + else monsterBlock = false; + if( blocked ) { // more accuracy blocking code - if( flDamage <= 0.0f && FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT )) + if( monsterBlock ) *blocked = !VectorCompareEpsilon( ent->v.origin, end, ON_EPSILON ); // can't move full distance else *blocked = true; } From ef39f9c9cfa2a61cd6570ba68e8def4069089051 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Nov 2018 23:31:16 +0300 Subject: [PATCH 077/205] engine: dedicated: fix build --- engine/common/con_utils.c | 4 ++-- engine/common/dedicated.c | 9 +++++++-- engine/common/host_state.c | 3 +++ engine/common/mod_bmodel.c | 2 ++ engine/common/system.c | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index e001039a..6e8927d6 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1256,7 +1256,6 @@ void Host_WriteConfig( void ) NET_SaveMasters(); } -#endif /* =============== @@ -1325,7 +1324,7 @@ void Host_WriteOpenGLConfig( void ) FS_Printf( f, "\n" ); Cmd_WriteOpenGLVariables( f ); CFG_END( f, "opengl.cfg" ); - } + } else Con_DPrintf( S_ERROR "can't update opengl.cfg.\n" ); } @@ -1356,6 +1355,7 @@ void Host_WriteVideoConfig( void ) } else Con_DPrintf( S_ERROR "can't update video.cfg.\n" ); } +#endif // XASH_DEDICATED void Key_EnumCmds_f( void ) { diff --git a/engine/common/dedicated.c b/engine/common/dedicated.c index 5341a820..6e573ae2 100644 --- a/engine/common/dedicated.c +++ b/engine/common/dedicated.c @@ -304,9 +304,14 @@ void SCR_CheckStartupVids() } -imgfilter_t *R_FindTexFilter( const char *texname ) +void Sys_SetClipboardData( const byte *text, size_t size ) { - return NULL; + +} + +void CL_StopPlayback( void ) +{ + } #include "sprite.h" diff --git a/engine/common/host_state.c b/engine/common/host_state.c index 3d60ea09..d1b12c90 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -135,7 +135,10 @@ void Host_ShutdownGame( void ) void Host_RunFrame( float time ) { + // at this time, we don't need to get events from OS on dedicated +#ifndef XASH_DEDICATED Platform_RunEvents(); +#endif // XASH_DEDICATED // engine main frame Host_Frame( time ); diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index c077f264..203df701 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -1033,8 +1033,10 @@ static void Mod_CalcSurfaceExtents( msurface_t *surf ) info->lightextents[i] = surf->extents[i]; } +#ifndef XASH_DEDICATED if( !FBitSet( tex->flags, TEX_SPECIAL ) && ( surf->extents[i] > 16384 ) && ( tr.block_size == BLOCK_SIZE_DEFAULT )) Con_Reportf( S_ERROR "Bad surface extents %i\n", surf->extents[i] ); +#endif // XASH_DEDICATED } } diff --git a/engine/common/system.c b/engine/common/system.c index e997a446..53812312 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -148,6 +148,7 @@ BOOL WINAPI IsDebuggerPresent(void); #endif #endif +#ifndef XASH_DEDICATED /* ================ Sys_GetClipboardData @@ -177,6 +178,7 @@ void Sys_SetClipboardData( const byte *buffer, size_t size ) { Platform_SetClipboardText( (char *)buffer, size ); } +#endif // XASH_DEDICATED /* ================ From 18353d9ae695806636049b258a1e2e62ee3be59e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Nov 2018 01:06:23 +0300 Subject: [PATCH 078/205] net_ws: allow to set custom IP address, use Q_strncpy instead of Q_strcpy --- engine/common/net_ws.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 3ff7c8f4..32f9f5bd 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1615,15 +1615,15 @@ void NET_GetLocalAddress( void ) // If we have changed the ip var from the command line, use that instead. if( Q_strcmp( net_ipname->string, "localhost" )) { - Q_strcpy( buff, net_ipname->string ); + Q_strncpy( buff, net_ipname->string, sizeof( buff ) ); } else { pGetHostName( buff, 512 ); - } - // ensure that it doesn't overrun the buffer - buff[511] = 0; + // ensure that it doesn't overrun the buffer + buff[511] = 0; + } if( NET_StringToAdr( buff, &net_local )) { @@ -1825,6 +1825,10 @@ void NET_Init( void ) if( Sys_GetParmFromCmdLine( "-port", cmd ) && Q_isdigit( cmd )) Cvar_FullSet( "hostport", cmd, FCVAR_READ_ONLY ); + // specify custom ip + if( Sys_GetParmFromCmdLine( "-ip", cmd )) + Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY ); + // adjust clockwindow if( Sys_GetParmFromCmdLine( "-clockwindow", cmd )) Cvar_SetValue( "clockwindow", Q_atof( cmd )); From d18708acf5eb069187366733c9294822b1fb53a1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Nov 2018 01:09:09 +0300 Subject: [PATCH 079/205] keys: fix indentation --- engine/client/keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/keys.c b/engine/client/keys.c index c7459da0..bc7fccf7 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -281,7 +281,7 @@ int Key_GetKey( const char *pBinding ) continue; if( *keys[i].binding == '+' ) - { + { if( !Q_strnicmp( keys[i].binding + 1, pBinding, Q_strlen( pBinding ))) return i; } From d81288d2819ad0e09b88a9dbe886f2dbf697c569 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Nov 2018 01:35:10 +0300 Subject: [PATCH 080/205] wscript: remove no-download-deps, autodownloading of dependencies not done yet --- wscript | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wscript b/wscript index cbcff9e6..ba81db89 100644 --- a/wscript +++ b/wscript @@ -43,10 +43,6 @@ def options(opt): '--release', action = 'store_true', dest = 'RELEASE', default = False, help = 'strip debug info from binary and enable optimizations') - opt.add_option( - '--no-download-deps', action = 'store_false', dest = 'AUTODL', default = True, - help = 'don\'t try to download dependencies from network') - opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') From 3c5f7de74772fa525e35304edbc0f223abfbe314 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Nov 2018 01:35:42 +0300 Subject: [PATCH 081/205] vgui_support: wscript: forgot to set MSVC_SUBSYSTEM for WinXP compatibility --- vgui_support/wscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index c88f4619..6a6bb879 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -77,4 +77,6 @@ def build(bld): features = 'cxx', includes = includes, use = libs, - install_path = bld.env.LIBDIR) + install_path = bld.env.LIBDIR, + subsystem = bld.env.MSVC_SUBSYSTEM + ) From f350683e836fb7d0f663c6690bd118ffc9ef8882 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 5 Nov 2018 07:57:32 +0500 Subject: [PATCH 082/205] wscript: avoid linuxisms and gccisms. --- wscript | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/wscript b/wscript index ba81db89..242c17a8 100644 --- a/wscript +++ b/wscript @@ -87,14 +87,18 @@ def configure(conf): Logs.warn('WARNING: 64-bit engine may be unstable') if(conf.env.COMPILER_CC != 'msvc'): - if(conf.env.COMPILER_CC == 'gcc'): + if(conf.env.COMPILER_CC == 'gcc') or (conf.env.COMPILER_CC == 'clang'): conf.env.append_unique('LINKFLAGS', ['-Wl,--no-undefined']) if(conf.options.RELEASE): conf.env.append_unique('CFLAGS', ['-O2']) conf.env.append_unique('CXXFLAGS', ['-O2']) - else: + elif(conf.env.COMPILER_CC == 'gcc'): conf.env.append_unique('CFLAGS', ['-Og', '-g']) conf.env.append_unique('CXXFLAGS', ['-Og', '-g']) + else: + conf.env.append_unique('CFLAGS', ['-O0', '-g', '-gdwarf-2']) + conf.env.append_unique('CXXFLAGS', ['-O0', '-g', '-gdwarf-2']) + if conf.options.GCC_COLORS: conf.env.append_unique('CFLAGS', ['-fdiagnostics-color=always']) conf.env.append_unique('CXXFLAGS', ['-fdiagnostics-color=always']) @@ -113,8 +117,10 @@ def configure(conf): # TODO: wrapper around bld.stlib, bld.shlib and so on? conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' - if(conf.env.DEST_OS != 'win32'): + if(conf.env.DEST_OS == 'linux'): conf.check( lib='dl' ) + + if(conf.env.DEST_OS != 'win32'): conf.check( lib='m' ) conf.check( lib='pthread' ) From f36d1f5621323d170657b001e8e85b8cf6279b87 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 5 Nov 2018 08:01:43 +0500 Subject: [PATCH 083/205] Use execv instead of execve, because environ symbol breaks compilation with -Wl,--no-undefined flag under FreeBSD via Waf. --- engine/common/system.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/common/system.c b/engine/common/system.c index 53812312..38196435 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -29,7 +29,6 @@ GNU General Public License for more details. #include #ifndef __ANDROID__ -extern char **environ; #include #endif #endif @@ -299,7 +298,7 @@ void Sys_ShellExecute( const char *path, const char *parms, int shouldExit ) pid_t id = fork( ); if( id == 0 ) { - execve( xdgOpen, (char **)argv, environ ); + execv( xdgOpen, (char **)argv ); fprintf( stderr, "error opening %s %s", xdgOpen, path ); _exit( 1 ); } From d4e5e609afae044d9877072cc53e41d3696dfc78 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Mon, 5 Nov 2018 08:04:54 +0500 Subject: [PATCH 084/205] Do not break video subsystem initialization. --- engine/platform/sdl/vid_sdl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 6718835c..1f6be59d 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -878,6 +878,8 @@ qboolean R_Init_Video( void ) } GL_InitExtensions(); + + return true; } #ifdef XASH_GLES From b60b3f7d183c6734960fa5103ae3823bdb43ebcf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 16 Nov 2018 15:09:40 +0300 Subject: [PATCH 085/205] GameUI: avoid const modifier loss --- engine/client/cl_gameui.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 0415d06b..5887c341 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -715,15 +715,19 @@ for drawing playermodel previews */ static void pfnRenderScene( const ref_viewpass_t *rvp ) { + ref_viewpass_t copy; + // to avoid division by zero if( !rvp || rvp->fov_x <= 0.0f || rvp->fov_y <= 0.0f ) return; + copy = *rvp; + // don't allow special modes from menu - ((ref_viewpass_t *)&rvp)->flags = 0; + copy.flags = 0; R_Set2DMode( false ); - R_RenderFrame( rvp ); + R_RenderFrame( © ); R_Set2DMode( true ); R_PopScene(); } From 25d8a94c1c3ed661d360e2df66c4583309977d8b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 16 Nov 2018 15:25:04 +0300 Subject: [PATCH 086/205] engine: fix implicit declaration, remove dead MsgDev now, fix const modifier loss in host.c --- engine/client/cl_parse.c | 2 +- engine/client/in_joy.c | 4 ++-- engine/client/in_touch.c | 1 + engine/client/input.c | 2 ++ engine/client/vgui/vgui_draw.c | 5 +++-- engine/common/filesystem.c | 4 ++-- engine/common/host.c | 12 ++++++----- engine/common/hpak.c | 2 +- engine/common/masterlist.c | 4 ++-- engine/common/sequence.c | 2 +- engine/common/system.c | 35 ------------------------------- engine/platform/posix/lib_posix.c | 2 +- engine/platform/sdl/events.c | 6 +++--- engine/platform/sdl/in_sdl.c | 32 ++++++++++++++-------------- engine/platform/sdl/vid_sdl.c | 10 ++++----- 15 files changed, 47 insertions(+), 76 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 6a3edd9a..fee43b53 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1940,7 +1940,7 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num ) if( cl_trace_messages->value ) { - MsgDev( D_INFO, "^3USERMSG %s SIZE %i SVC_NUM %i\n", + Con_Reportf( "^3USERMSG %s SIZE %i SVC_NUM %i\n", clgame.msg[i].name, iSize, clgame.msg[i].number ); } diff --git a/engine/client/in_joy.c b/engine/client/in_joy.c index 45c65354..53ec5ba5 100644 --- a/engine/client/in_joy.c +++ b/engine/client/in_joy.c @@ -259,7 +259,7 @@ void Joy_AxisMotionEvent( int id, byte axis, short value ) if( axis >= MAX_AXES ) { - MsgDev( D_INFO, "Only 6 axes is supported\n" ); + Con_Reportf( "Only 6 axes is supported\n" ); return; } @@ -308,7 +308,7 @@ void Joy_ButtonEvent( int id, byte button, byte down ) int origbutton = button; button = ( button & 31 ) + K_AUX1; - MsgDev( D_INFO, "Only 32 joybuttons is supported, converting %i button ID to %s\n", origbutton, Key_KeynumToString( button ) ); + Con_Reportf( "Only 32 joybuttons is supported, converting %i button ID to %s\n", origbutton, Key_KeynumToString( button ) ); } else button += K_AUX1; diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 6207fbb6..4c38088c 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -24,6 +24,7 @@ GNU General Public License for more details. #ifdef XASH_SDL #include #endif +#include "platform/platform.h" typedef enum { diff --git a/engine/client/input.c b/engine/client/input.c index cade8019..d8ced306 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -27,6 +27,8 @@ GNU General Public License for more details. #include "windows.h" #endif +#include "platform/platform.h" + void* in_mousecursor; qboolean in_mouseactive; // false when not focus app qboolean in_mouseinitialized; diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index ed645b30..0a2eae48 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -28,6 +28,7 @@ GNU General Public License for more details. #include static SDL_Cursor* s_pDefaultCursor[20]; #endif +#include "platform/platform.h" int g_textures[VGUI_MAX_TEXTURES]; int g_textureId = 0; @@ -246,7 +247,7 @@ void VGui_Startup( int width, int height ) F( &vgui ); vgui.initialized = true; VGUI_InitCursors(); - MsgDev( D_INFO, "vgui_support: found interal client support\n" ); + Con_Reportf( "vgui_support: found interal client support\n" ); } } #endif // XASH_INTERNAL_GAMELIBS @@ -282,7 +283,7 @@ void VGui_Startup( int width, int height ) if( FS_FileExists( vguiloader, false ) ) Con_Reportf( S_ERROR "Failed to load vgui_support library: %s", COM_GetLibraryError() ); else - MsgDev( D_INFO, "vgui_support: not found\n" ); + Con_Reportf( "vgui_support: not found\n" ); } else { diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index d013e684..8e5935a1 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -626,7 +626,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade search->flags |= flags; fs_searchpaths = search; - MsgDev( D_REPORT, "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); + Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); return true; } else @@ -683,7 +683,7 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade search->flags |= flags; fs_searchpaths = search; - MsgDev( D_REPORT, "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); + Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); // time to add in search list all the wads that contains in current pakfile (if do) for( i = 0; i < pak->numfiles; i++ ) diff --git a/engine/common/host.c b/engine/common/host.c index 5783a81b..fd1e1cc4 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -684,7 +684,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( daemon > 0 ) { // parent - MsgDev( D_INFO, "Child pid: %i\n", daemon ); + Con_Reportf( "Child pid: %i\n", daemon ); exit( 0 ); } else @@ -726,10 +726,12 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha const char *IOS_GetDocsDir(); Q_strncpy( host.rootdir, IOS_GetDocsDir(), sizeof(host.rootdir) ); #elif defined(XASH_SDL) - if( !( baseDir = SDL_GetBasePath() ) ) + char *szBasePath; + + if( !( szBasePath = SDL_GetBasePath() ) ) Sys_Error( "couldn't determine current directory: %s", SDL_GetError() ); - Q_strncpy( host.rootdir, baseDir, sizeof( host.rootdir ) ); - SDL_free( (void*)baseDir ); + Q_strncpy( host.rootdir, szBasePath, sizeof( host.rootdir ) ); + SDL_free( szBasePath ); #else if( !getcwd( host.rootdir, sizeof(host.rootdir) ) ) { @@ -821,7 +823,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #endif if ( !host.rootdir[0] || SetCurrentDirectory( host.rootdir ) != 0) - MsgDev( D_INFO, "%s is working directory now\n", host.rootdir ); + Con_Reportf( "%s is working directory now\n", host.rootdir ); else Sys_Error( "Changing working directory to %s failed.\n", host.rootdir ); diff --git a/engine/common/hpak.c b/engine/common/hpak.c index e8bbee45..2ea1faab 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -806,7 +806,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) if( !HPAK_FindResource( &hpak_read, pResource->rgucMD5_hash, NULL )) { - Con_DPrintf( S_ERROR "HPAK doesn't contain specified lump: %s\n", pResource->szFileName, read_path ); + Con_DPrintf( S_ERROR "HPAK %s doesn't contain specified lump: %s\n", read_path, pResource->szFileName ); Mem_Free( hpak_read.entries ); Mem_Free( hpak_save.entries ); FS_Close( file_src ); diff --git a/engine/common/masterlist.c b/engine/common/masterlist.c index 771c07c1..862d640d 100644 --- a/engine/common/masterlist.c +++ b/engine/common/masterlist.c @@ -54,7 +54,7 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ) if( !res ) { - MsgDev( D_INFO, "Can't resolve adr: %s\n", list->address ); + Con_Reportf( "Can't resolve adr: %s\n", list->address ); list->sent = true; continue; } @@ -181,7 +181,7 @@ static void NET_LoadMasters( void ) if( !afile ) // file doesn't exist yet { - MsgDev( D_INFO, "Cannot load xashcomm.lst\n" ); + Con_Reportf( "Cannot load xashcomm.lst\n" ); return; } diff --git a/engine/common/sequence.c b/engine/common/sequence.c index a3b6840a..91643fbf 100644 --- a/engine/common/sequence.c +++ b/engine/common/sequence.c @@ -1564,7 +1564,7 @@ void Sequence_ParseFile( const char *fileName, qboolean isGlobal ) if( !buffer ) return; - MsgDev( D_INFO, "reading sequence file: %s\n", fileName ); + Con_Reportf( "reading sequence file: %s\n", fileName ); Sequence_ParseBuffer( buffer, bufSize ); diff --git a/engine/common/system.c b/engine/common/system.c index 38196435..a9dddec8 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -731,38 +731,3 @@ void Sys_Print( const char *pMsg ) // Rcon_Print( pMsg ); } - -/* -================ -MsgDev - -formatted developer message -================ -*/ -void MsgDev( int type, const char *pMsg, ... ) -{ - static char text[MAX_PRINT_MSG]; - va_list argptr; - - if( type >= D_REPORT && host_developer.value < DEV_EXTENDED ) - return; - - va_start( argptr, pMsg ); - Q_vsnprintf( text, sizeof( text ) - 1, pMsg, argptr ); - va_end( argptr ); - - switch( type ) - { - case D_WARN: - Sys_Print( va( "^3Warning:^7 %s", text )); - break; - case D_ERROR: - Sys_Print( va( "^1Error:^7 %s", text )); - break; - case D_INFO: - case D_NOTE: - case D_REPORT: - Sys_Print( text ); - break; - } -} diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index a213439c..2b26d7fd 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -175,7 +175,7 @@ void *COM_FunctionFromName( void *hInstance, const char *pName ) void *function; if( !( function = COM_GetProcAddress( hInstance, pName ) ) ) { - MsgDev(D_ERROR, "FunctionFromName: Can't get symbol %s: %s\n", pName, dlerror()); + Con_Reportf( S_ERROR "FunctionFromName: Can't get symbol %s: %s\n", pName, dlerror()); } return function; } diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 491d7d50..14b6e7d9 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -137,11 +137,11 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key ) return; case SDL_SCANCODE_UNKNOWN: { - if( down ) MsgDev( D_INFO, "SDLash_KeyEvent: Unknown scancode\n" ); + if( down ) Con_Reportf( "SDLash_KeyEvent: Unknown scancode\n" ); return; } default: - if( down ) MsgDev( D_INFO, "SDLash_KeyEvent: Unknown key: %s = %i\n", SDL_GetScancodeName( keynum ), keynum ); + if( down ) Con_Reportf( "SDLash_KeyEvent: Unknown key: %s = %i\n", SDL_GetScancodeName( keynum ), keynum ); return; } } @@ -272,7 +272,7 @@ static void SDLash_EventFilter( SDL_Event *event ) if( ( event->tfinger.x > 2 ) && ( event->tfinger.y > 2 ) ) { scale = 2; - MsgDev( D_INFO, "SDL reports screen coordinates, workaround enabled!\n"); + Con_Reportf( "SDL reports screen coordinates, workaround enabled!\n"); } else { diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index b6d7d431..38363a7c 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -111,12 +111,12 @@ static int SDLash_JoyInit_Old( int numjoy ) int num; int i; - MsgDev( D_INFO, "Joystick: SDL\n" ); + Con_Reportf( "Joystick: SDL\n" ); if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK && SDL_InitSubSystem( SDL_INIT_JOYSTICK ) ) { - MsgDev( D_INFO, "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); + Con_Reportf( "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); return 0; } @@ -128,27 +128,27 @@ static int SDLash_JoyInit_Old( int numjoy ) num = SDL_NumJoysticks(); if( num > 0 ) - MsgDev( D_INFO, "%i joysticks found:\n", num ); + Con_Reportf( "%i joysticks found:\n", num ); else { - MsgDev( D_INFO, "No joystick found.\n" ); + Con_Reportf( "No joystick found.\n" ); return 0; } for( i = 0; i < num; i++ ) - MsgDev( D_INFO, "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); + Con_Reportf( "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); - MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + Con_Reportf( "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); joy = SDL_JoystickOpen( numjoy ); if( !joy ) { - MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + Con_Reportf( "Failed to select joystick: %s\n", SDL_GetError( ) ); return 0; } - MsgDev( D_INFO, "Selected joystick: %s\n" + Con_Reportf( "Selected joystick: %s\n" "\tAxes: %i\n" "\tHats: %i\n" "\tButtons: %i\n" @@ -173,12 +173,12 @@ static int SDLash_JoyInit_New( int numjoy ) int temp, num; int i; - MsgDev( D_INFO, "Joystick: SDL GameController API\n" ); + Con_Reportf( "Joystick: SDL GameController API\n" ); if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) ) { - MsgDev( D_INFO, "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); + Con_Reportf( "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); return 0; } @@ -200,28 +200,28 @@ static int SDLash_JoyInit_New( int numjoy ) } if( num > 0 ) - MsgDev( D_INFO, "%i joysticks found:\n", num ); + Con_Reportf( "%i joysticks found:\n", num ); else { - MsgDev( D_INFO, "No joystick found.\n" ); + Con_Reportf( "No joystick found.\n" ); return 0; } for( i = 0; i < num; i++ ) - MsgDev( D_INFO, "%i\t: %s\n", i, SDL_GameControllerNameForIndex( i ) ); + Con_Reportf( "%i\t: %s\n", i, SDL_GameControllerNameForIndex( i ) ); - MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + Con_Reportf( "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); gamecontroller = SDL_GameControllerOpen( numjoy ); if( !gamecontroller ) { - MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + Con_Reportf( "Failed to select joystick: %s\n", SDL_GetError( ) ); return 0; } // was added in SDL2-2.0.6, allow build with earlier versions just in case #if SDL_MAJOR_VERSION > 2 || SDL_MINOR_VERSION > 0 || SDL_PATCHLEVEL >= 6 - MsgDev( D_INFO, "Selected joystick: %s (%i:%i:%i)\n", + Con_Reportf( "Selected joystick: %s (%i:%i:%i)\n", SDL_GameControllerName( gamecontroller ), SDL_GameControllerGetVendor( gamecontroller ), SDL_GameControllerGetProduct( gamecontroller ), diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 1f6be59d..052db635 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -445,7 +445,7 @@ qboolean GL_CreateContext( void ) if( ( glw_state.context = SDL_GL_CreateContext( host.hWnd ) ) == NULL) { - MsgDev(D_ERROR, "GL_CreateContext: %s\n", SDL_GetError()); + Con_Reportf( S_ERROR "GL_CreateContext: %s\n", SDL_GetError()); return GL_DeleteContext(); } @@ -478,7 +478,7 @@ qboolean GL_UpdateContext( void ) { if( SDL_GL_MakeCurrent( host.hWnd, glw_state.context )) { - MsgDev(D_ERROR, "GL_UpdateContext: %s\n", SDL_GetError()); + Con_Reportf( S_ERROR "GL_UpdateContext: %s\n", SDL_GetError()); return GL_DeleteContext(); } @@ -502,7 +502,7 @@ qboolean VID_SetScreenResolution( int width, int height ) if( !SDL_GetClosestDisplayMode(0, &want, &got) ) return false; - MsgDev(D_NOTE, "Got closest display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate); + Con_Reportf( "Got closest display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate); if( SDL_SetWindowDisplayMode( host.hWnd, &got) == -1 ) return false; @@ -1099,7 +1099,7 @@ void GL_InitExtensions( void ) glConfig.renderer_string = pglGetString( GL_RENDERER ); glConfig.version_string = pglGetString( GL_VERSION ); glConfig.extensions_string = pglGetString( GL_EXTENSIONS ); - MsgDev( D_INFO, "^3Video^7: %s\n", glConfig.renderer_string ); + Con_Reportf( "^3Video^7: %s\n", glConfig.renderer_string ); #ifdef XASH_GLES GL_InitExtensionsGLES(); @@ -1146,7 +1146,7 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) SDL_GetCurrentDisplayMode( 0, &displayMode ); - MsgDev( D_INFO, "R_ChangeDisplaySettings: Setting video mode to %dx%d %s\n", width, height, fullscreen ? "fullscreen" : "windowed" ); + Con_Reportf( "R_ChangeDisplaySettings: Setting video mode to %dx%d %s\n", width, height, fullscreen ? "fullscreen" : "windowed" ); // check our desktop attributes glw_state.desktopBitsPixel = SDL_BITSPERPIXEL( displayMode.format ); From 1ef0b44a41b8a17bbf22cc60a346b9c1c737e30d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 16 Nov 2018 15:34:02 +0300 Subject: [PATCH 087/205] filesystem: allow setting extras.pak by env-var for everyone --- engine/common/filesystem.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 8e5935a1..7cb184dc 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -901,27 +901,27 @@ FS_Rescan */ void FS_Rescan( void ) { + const char *str; + const int extrasFlags = FS_NOWRITE_PATH | FS_CUSTOM_PATH; Con_Reportf( "FS_Rescan( %s )\n", GI->title ); FS_ClearSearchPath(); -#ifdef __ANDROID__ - char *str; - if( str = getenv("XASH3D_EXTRAS_PAK1") ) - FS_AddPack_Fullpath( str, NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); - if( str = getenv("XASH3D_EXTRAS_PAK2") ) - FS_AddPack_Fullpath( str, NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); - //FS_AddPack_Fullpath( "/data/data/in.celest.xash3d.hl.test/files/pak.pak", NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); -#elif TARGET_OS_IPHONE +#if TARGET_OS_IPHONE { - FS_AddPack_Fullpath( va( "%sextras.pak", SDL_GetBasePath() ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); - FS_AddPack_Fullpath( va( "%sextras_%s.pak", SDL_GetBasePath(), GI->gamefolder ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddPak_Fullpath( va( "%sextras.pak", SDL_GetBasePath() ), NULL, extrasFlags ); + FS_AddPak_Fullpath( va( "%sextras_%s.pak", SDL_GetBasePath(), GI->gamefolder ), NULL, extrasFlags ); } #elif defined(__SAILFISH__) { - FS_AddPack_Fullpath( va( SHAREPATH"/extras.pak" ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); - FS_AddPack_Fullpath( va( SHAREPATH"/%s/extras.pak", GI->gamefolder ), NULL, false, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddPak_Fullpath( va( SHAREPATH"/extras.pak" ), NULL, extrasFlags ); + FS_AddPak_Fullpath( va( SHAREPATH"/%s/extras.pak", GI->gamefolder ), NULL, extrasFlags ); } +#else + if( ( str = getenv( "XASH3D_EXTRAS_PAK1" ) ) ) + FS_AddPak_Fullpath( str, NULL, extrasFlags ); + if( ( str = getenv( "XASH3D_EXTRAS_PAK2" ) ) ) + FS_AddPak_Fullpath( str, NULL, extrasFlags ); #endif if( Q_stricmp( GI->basedir, GI->gamefolder )) From f0d08611608ce06b999b37dcea8e5d4cee4c241e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 16 Nov 2018 15:34:27 +0300 Subject: [PATCH 088/205] PhysicAPI: fix callback list initialization --- engine/server/sv_phys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 34ec51f7..efd74a2c 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2033,7 +2033,7 @@ static server_physics_api_t gPhysicsAPI = pfnPointContents, SV_MoveNormal, SV_MoveNoEnts, - SV_BoxInPVS, + (void*)SV_BoxInPVS, pfnWriteBytes, Mod_CheckLump, Mod_ReadLump, From 7c772d6bfde8dc2c04d67bafe50bf79f8da7071d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 16 Nov 2018 16:32:16 +0300 Subject: [PATCH 089/205] engine: rework timer stuff, move to platform. Move debugger present checks to platform --- common/defaults.h | 2 +- engine/common/crashhandler.c | 3 - engine/common/system.c | 124 +++++------------------------- engine/platform/linux/sys_linux.c | 73 ++++++++++++++++++ engine/platform/platform.h | 17 ++++ engine/platform/sdl/sys_sdl.c | 39 ++++++++++ engine/platform/win32/sys_win.c | 47 +++++++++++ engine/wscript | 3 + 8 files changed, 198 insertions(+), 110 deletions(-) create mode 100644 engine/platform/linux/sys_linux.c create mode 100644 engine/platform/sdl/sys_sdl.c create mode 100644 engine/platform/win32/sys_win.c diff --git a/common/defaults.h b/common/defaults.h index 8498eca1..7eb68476 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -21,7 +21,7 @@ GNU General Public License for more details. /* =================================================================== -SETUP BACKENDS DEFINATIONS +SETUP BACKENDS DEFINITIONS =================================================================== */ diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 3f2826ba..4afe2317 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -23,9 +23,6 @@ Sys_Crash Crash handler, called from system ================ */ -#define DEBUG_BREAK -/// TODO: implement on windows too - #if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP || XASH_CRASHHANDLER == CRASHHANDLER_WIN32 #if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP #pragma comment( lib, "dbghelp" ) diff --git a/engine/common/system.c b/engine/common/system.c index a9dddec8..40c2d41d 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -16,7 +16,6 @@ GNU General Public License for more details. #include "common.h" #include "mathlib.h" #include "platform/platform.h" -#include #include #ifdef XASH_SDL @@ -37,9 +36,6 @@ GNU General Public License for more details. qboolean error_on_exit = false; // arg for exit(); #define DEBUG_BREAK -#if defined _WIN32 && !defined XASH_SDL -#include -#endif /* ================ @@ -48,103 +44,25 @@ Sys_DoubleTime */ double GAME_EXPORT Sys_DoubleTime( void ) { -#if XASH_TIMER == TIMER_WIN32 - static LARGE_INTEGER g_PerformanceFrequency; - static LARGE_INTEGER g_ClockStart; - LARGE_INTEGER CurrentTime; - - if( !g_PerformanceFrequency.QuadPart ) - { - QueryPerformanceFrequency( &g_PerformanceFrequency ); - QueryPerformanceCounter( &g_ClockStart ); - } - QueryPerformanceCounter( &CurrentTime ); - - return (double)( CurrentTime.QuadPart - g_ClockStart.QuadPart ) / (double)( g_PerformanceFrequency.QuadPart ); -#elif XASH_TIMER == TIMER_SDL - static longtime_t g_PerformanceFrequency; - static longtime_t g_ClockStart; - longtime_t CurrentTime; - - if( !g_PerformanceFrequency ) - { - g_PerformanceFrequency = SDL_GetPerformanceFrequency(); - g_ClockStart = SDL_GetPerformanceCounter(); - } - CurrentTime = SDL_GetPerformanceCounter(); - return (double)( CurrentTime - g_ClockStart ) / (double)( g_PerformanceFrequency ); -#elif XASH_TIMER == TIMER_LINUX - static longtime_t g_PerformanceFrequency; - static longtime_t g_ClockStart; - longtime_t CurrentTime; - struct timespec ts; - - if( !g_PerformanceFrequency ) - { - struct timespec res; - if( !clock_getres(CLOCK_MONOTONIC, &res) ) - g_PerformanceFrequency = 1000000000LL/res.tv_nsec; - } - clock_gettime(CLOCK_MONOTONIC, &ts); - return (double) ts.tv_sec + (double) ts.tv_nsec/1000000000.0; -#endif + return Platform_DoubleTime(); } -#ifdef GDB_BREAK -#include -qboolean Sys_DebuggerPresent( void ) -{ - char buf[1024]; - - int status_fd = open( "/proc/self/status", O_RDONLY ); - if ( status_fd == -1 ) - return 0; - - ssize_t num_read = read( status_fd, buf, sizeof( buf ) ); - - if ( num_read > 0 ) - { - static const char TracerPid[] = "TracerPid:"; - const byte *tracer_pid; - - buf[num_read] = 0; - tracer_pid = (const byte*)Q_strstr( buf, TracerPid ); - if( !tracer_pid ) - return false; - //printf( "%s\n", tracer_pid ); - while( *tracer_pid < '0' || *tracer_pid > '9' ) - if( *tracer_pid++ == '\n' ) - return false; - //printf( "%s\n", tracer_pid ); - return !!Q_atoi( (const char*)tracer_pid ); - } - - return false; -} - -#undef DEBUG_BREAK -#ifdef __i386__ -#define DEBUG_BREAK \ - if( Sys_DebuggerPresent() ) \ - asm volatile("int $3;") -#else -#define DEBUG_BREAK \ - if( Sys_DebuggerPresent() ) \ - raise( SIGINT ) -#endif -#endif - -#if defined _WIN32 && !defined XASH_64BIT -#ifdef _MSC_VER - - -BOOL WINAPI IsDebuggerPresent(void); -#define DEBUG_BREAK if( IsDebuggerPresent() ) \ - _asm{ int 3 } -#else -#define DEBUG_BREAK if( IsDebuggerPresent() ) \ - asm volatile("int $3;") -#endif +#if defined __linux__ || ( defined _WIN32 && !defined XASH_64BIT ) + #undef DEBUG_BREAK + qboolean Sys_DebuggerPresent(); // see sys_linux.c + #ifdef _MSC_VER + #define DEBUG_BREAK \ + if( Sys_IsDebuggerPresent() ) \ + _asm{ int 3 } + #elif __i386__ + #define DEBUG_BREAK \ + if( Sys_DebuggerPresent() ) \ + asm volatile("int $3;") + #else + #define DEBUG_BREAK \ + if( Sys_DebuggerPresent() ) \ + raise( SIGINT ) + #endif #endif #ifndef XASH_DEDICATED @@ -192,13 +110,7 @@ void Sys_Sleep( int msec ) return; msec = min( msec, 1000 ); -#if XASH_TIMER == TIMER_WIN32 - Sleep( msec ); -#elif XASH_TIMER == TIMER_SDL - SDL_Delay( msec ); -#elif XASH_TIMER == TIMER_LINUX - usleep( msec * 1000 ); -#endif + Platform_Sleep( msec ); } /* diff --git a/engine/platform/linux/sys_linux.c b/engine/platform/linux/sys_linux.c new file mode 100644 index 00000000..c6090f5e --- /dev/null +++ b/engine/platform/linux/sys_linux.c @@ -0,0 +1,73 @@ +/* +sys_linux.c - Linux system utils +Copyright (C) 2018 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include +#include +#include "platform/platform.h" + +#if XASH_TIMER == TIMER_LINUX +double Platform_DoubleTime( void ) +{ + static longtime_t g_PerformanceFrequency; + static longtime_t g_ClockStart; + longtime_t CurrentTime; + struct timespec ts; + + if( !g_PerformanceFrequency ) + { + struct timespec res; + if( !clock_getres(CLOCK_MONOTONIC, &res) ) + g_PerformanceFrequency = 1000000000LL/res.tv_nsec; + } + clock_gettime(CLOCK_MONOTONIC, &ts); + return (double) ts.tv_sec + (double) ts.tv_nsec/1000000000.0; +} + +void Platform_Sleep( int msec ) +{ + usleep( msec * 1000 ); +} +#endif // XASH_TIMER == TIMER_LINUX + +qboolean Sys_DebuggerPresent( void ) +{ + char buf[1024]; + + int status_fd = open( "/proc/self/status", O_RDONLY ); + if ( status_fd == -1 ) + return 0; + + ssize_t num_read = read( status_fd, buf, sizeof( buf ) ); + + if ( num_read > 0 ) + { + static const char TracerPid[] = "TracerPid:"; + const byte *tracer_pid; + + buf[num_read] = 0; + tracer_pid = (const byte*)Q_strstr( buf, TracerPid ); + if( !tracer_pid ) + return false; + //printf( "%s\n", tracer_pid ); + while( *tracer_pid < '0' || *tracer_pid > '9' ) + if( *tracer_pid++ == '\n' ) + return false; + //printf( "%s\n", tracer_pid ); + return !!Q_atoi( (const char*)tracer_pid ); + } + + return false; +} diff --git a/engine/platform/platform.h b/engine/platform/platform.h index fc941952..551f05a8 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -17,6 +17,23 @@ GNU General Public License for more details. #ifndef PLATFORM_H #define PLATFORM_H +#include "common.h" +#include "system.h" +#include "defaults.h" + +/* +============================================================================== + + SYSTEM UTILS + +============================================================================== +*/ +double Platform_DoubleTime( void ); +void Platform_Sleep( int msec ); +// commented out, as this is an optional feature or maybe implemented in system API directly +// see system.c +// qboolean Sys_DebuggerPresent( void ); + /* ============================================================================== diff --git a/engine/platform/sdl/sys_sdl.c b/engine/platform/sdl/sys_sdl.c new file mode 100644 index 00000000..143def4f --- /dev/null +++ b/engine/platform/sdl/sys_sdl.c @@ -0,0 +1,39 @@ +/* +sys_sdl.c - SDL2 system utils +Copyright (C) 2018 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include "platform/platform.h" + +#if XASH_TIMER == TIMER_SDL +double Platform_DoubleTime( void ) +{ + static longtime_t g_PerformanceFrequency; + static longtime_t g_ClockStart; + longtime_t CurrentTime; + + if( !g_PerformanceFrequency ) + { + g_PerformanceFrequency = SDL_GetPerformanceFrequency(); + g_ClockStart = SDL_GetPerformanceCounter(); + } + CurrentTime = SDL_GetPerformanceCounter(); + return (double)( CurrentTime - g_ClockStart ) / (double)( g_PerformanceFrequency ); +} + +void Platform_Sleep( int msec ) +{ + SDL_Delay( msec ); +} +#endif // XASH_TIMER == TIMER_SDL diff --git a/engine/platform/win32/sys_win.c b/engine/platform/win32/sys_win.c new file mode 100644 index 00000000..714d7d6a --- /dev/null +++ b/engine/platform/win32/sys_win.c @@ -0,0 +1,47 @@ +/* +sys_win.c - win32 system utils +Copyright (C) 2018 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include +#include "platform/platform.h" + +#if XASH_TIMER == TIMER_WIN32 +double Platform_DoubleTime( void ) +{ + static LARGE_INTEGER g_PerformanceFrequency; + static LARGE_INTEGER g_ClockStart; + LARGE_INTEGER CurrentTime; + + if( !g_PerformanceFrequency.QuadPart ) + { + QueryPerformanceFrequency( &g_PerformanceFrequency ); + QueryPerformanceCounter( &g_ClockStart ); + } + QueryPerformanceCounter( &CurrentTime ); + + return (double)( CurrentTime.QuadPart - g_ClockStart.QuadPart ) / (double)( g_PerformanceFrequency.QuadPart ); +} + +void Platform_Sleep( int msec ) +{ + Sleep( msec ); +} +#endif // XASH_TIMER == TIMER_WIN32 + +qboolean Sys_DebuggerPresent( void ) +{ + return IsDebuggerPresent(); +} + + diff --git a/engine/wscript b/engine/wscript index 83ae7a00..3a4dc9ab 100644 --- a/engine/wscript +++ b/engine/wscript @@ -74,6 +74,9 @@ def build(bld): libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP'] source += bld.path.ant_glob(['platform/win32/*.c']) + if bld.env.DEST_OS == 'linux': + source += bld.path.ant_glob(['platform/linux/*.c']) + # add client files and sdl2 library if not bld.env.DEDICATED: libs.append( 'SDL2' ) From edde871eb5cf1b32d3efe1ff03a39fde8c69c494 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 17 Nov 2018 01:26:57 +0300 Subject: [PATCH 090/205] console: fix console font scale for Quake I fonts, fix drawing arrows in console for Quake I --- engine/client/console.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index d62f475d..4fd5acee 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -537,7 +537,7 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) if( font->hFontTexture && fontWidth != 0 ) { - font->charHeight = fontWidth / 16; + font->charHeight = fontWidth / 16 * con_fontscale->value; font->type = FONT_FIXED; // build fixed rectangles @@ -547,7 +547,7 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) font->fontRc[i].right = font->fontRc[i].left + fontWidth / 16; font->fontRc[i].top = (i / 16) * (fontWidth / 16); font->fontRc[i].bottom = font->fontRc[i].top + fontWidth / 16; - font->charWidths[i] = fontWidth / 16; + font->charWidths[i] = fontWidth / 16 * con_fontscale->value; } font->valid = true; } @@ -871,8 +871,9 @@ static int Con_DrawGenericChar( int x, int y, int number, rgba_t color ) return 0; number = Con_UtfProcessChar(number); - if( number < 32 ) + if( !number ) return 0; + if( y < -con.curFont->charHeight ) return 0; From 9d156b4285447aca6771881b4042fd69c39b3e5d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 17 Nov 2018 01:40:35 +0300 Subject: [PATCH 091/205] crclib: md5: fix memset call --- engine/common/crclib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/crclib.c b/engine/common/crclib.c index 5f4cc802..0d1f9d65 100644 --- a/engine/common/crclib.c +++ b/engine/common/crclib.c @@ -422,7 +422,7 @@ void MD5Final( byte digest[16], MD5Context_t *ctx ) MD5Transform( ctx->buf, (uint *)ctx->in ); memcpy( digest, ctx->buf, 16 ); - memset( ctx, 0, sizeof( ctx )); // in case it's sensitive + memset( ctx, 0, sizeof( *ctx )); // in case it's sensitive } // The four core functions From 847be54457ff08eb49523609b78636f6706eb9b4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 16:04:52 +0300 Subject: [PATCH 092/205] wscript: refactor adding compiler flags, remove --release flag, instead add mandatory --build-type flag --- wscript | 94 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/wscript b/wscript index 242c17a8..4a8a076f 100644 --- a/wscript +++ b/wscript @@ -26,6 +26,22 @@ def get_git_version(): return version +def get_flags_by_compiler(flags, compiler): + out = [] + if compiler in flags: + out += flags[compiler] + elif 'default' in flags: + out += flags['default'] + return out + +def get_flags_by_type(flags, type, compiler): + out = [] + if 'common' in flags: + out += get_flags_by_compiler(flags['common'], compiler) + if type in flags: + out += get_flags_by_compiler(flags[type], compiler) + return out + def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': @@ -38,11 +54,7 @@ def options(opt): opt.add_option( '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, help = 'allow targetting 64-bit engine') - - opt.add_option( - '--release', action = 'store_true', dest = 'RELEASE', default = False, - help = 'strip debug info from binary and enable optimizations') - + opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') @@ -55,9 +67,22 @@ def options(opt): '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, help = 'SDL2 path to build(required for Windows)') + opt.add_option( + '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, + help = 'build type: debug, release or none(custom flags)') + opt.recurse(SUBDIRS) def configure(conf): + conf.start_msg('Build type') + if conf.options.BUILD_TYPE == None: + conf.end_msg('not set', color='RED') + conf.fatal('Please set a build type, for example "--build-type=release"') + elif not conf.options.BUILD_TYPE in ['release', 'debug', 'none']: + conf.end_msg(conf.options.BUILD_TYPE, color='RED') + conf.fatal('Invalid build type. Valid are "debug", "release" or "none"') + conf.end_msg(conf.options.BUILD_TYPE) + conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC conf.load('compiler_cxx compiler_c') if sys.platform == 'win32': @@ -72,45 +97,50 @@ def configure(conf): int check[sizeof(void*) == 4 ? 1: -1]; return 0; }''', - msg = 'Checking if compiler create 32 bit code') + msg = 'Checking if compiler create 32 bit code') except conf.errors.ConfigurationError: # Program not compiled, we have 64 bit conf.env.DEST_64BIT = True + if(conf.env.DEST_64BIT): if(not conf.options.ALLOW64): conf.env.append_value('LINKFLAGS', ['-m32']) - conf.env.append_value('CFLAGS', ['-m32']) - conf.env.append_value('CXXFLAGS', ['-m32']) + conf.env.append_value('CFLAGS', ['-m32']) + conf.env.append_value('CXXFLAGS', ['-m32']) Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') else: Logs.warn('WARNING: 64-bit engine may be unstable') - if(conf.env.COMPILER_CC != 'msvc'): - if(conf.env.COMPILER_CC == 'gcc') or (conf.env.COMPILER_CC == 'clang'): - conf.env.append_unique('LINKFLAGS', ['-Wl,--no-undefined']) - if(conf.options.RELEASE): - conf.env.append_unique('CFLAGS', ['-O2']) - conf.env.append_unique('CXXFLAGS', ['-O2']) - elif(conf.env.COMPILER_CC == 'gcc'): - conf.env.append_unique('CFLAGS', ['-Og', '-g']) - conf.env.append_unique('CXXFLAGS', ['-Og', '-g']) - else: - conf.env.append_unique('CFLAGS', ['-O0', '-g', '-gdwarf-2']) - conf.env.append_unique('CXXFLAGS', ['-O0', '-g', '-gdwarf-2']) + linker_flags = { + 'common': { + 'msvc': ['/DEBUG'], + 'default': ['-Wl,--no-undefined'] + } + } - if conf.options.GCC_COLORS: - conf.env.append_unique('CFLAGS', ['-fdiagnostics-color=always']) - conf.env.append_unique('CXXFLAGS', ['-fdiagnostics-color=always']) - else: - if(conf.options.RELEASE): - conf.env.append_unique('CFLAGS', ['/O2']) - conf.env.append_unique('CXXFLAGS', ['/O2']) - else: - conf.env.append_unique('CFLAGS', ['/Z7']) - conf.env.append_unique('CXXFLAGS', ['/Z7']) - conf.env.append_unique('LINKFLAGS', ['/DEBUG']) - conf.env.append_unique('DEFINES', '_USING_V110_SDK71_') # Force XP compability + compiler_c_cxx_flags = { + 'common': { + 'msvc': ['/D_USING_V110_SDK71_'], + 'default': ['-g'] + }, + 'release': { + 'msvc': ['/Zi', '/O2'], + 'default': ['-O3'] + }, + 'debug': { + 'msvc': ['/Z7'], + 'clang': ['-O0', '-gdwarf-2'], + 'default': ['-O0'] + } + } + + conf.env.append_unique('CFLAGS', get_flags_by_type( + compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + conf.env.append_unique('CXXFLAGS', get_flags_by_type( + compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + conf.env.append_unique('LINKFLAGS', get_flags_by_type( + linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) # Force XP compability, all build targets should add # subsystem=bld.env.MSVC_SUBSYSTEM From 66d1a632a7e74207f169ebef31604f76c3b48d45 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 17:14:38 +0300 Subject: [PATCH 093/205] net_ws: fix possible garabe in return value due to uninitialized value --- engine/common/net_ws.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 32f9f5bd..6c25019e 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -413,7 +413,7 @@ int NET_GetHostByName( const char *hostname ) #ifdef HAVE_GETADDRINFO struct addrinfo *ai = NULL, *cur; struct addrinfo hints; - int ip; + int ip = 0; memset( &hints, 0, sizeof( hints )); hints.ai_family = AF_INET; From 9d93eca5729a0c236fcb1935be744a93652dc330 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 17:50:15 +0300 Subject: [PATCH 094/205] platform: linux: fix descriptor leak --- engine/platform/linux/sys_linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/platform/linux/sys_linux.c b/engine/platform/linux/sys_linux.c index c6090f5e..249e568b 100644 --- a/engine/platform/linux/sys_linux.c +++ b/engine/platform/linux/sys_linux.c @@ -51,6 +51,7 @@ qboolean Sys_DebuggerPresent( void ) return 0; ssize_t num_read = read( status_fd, buf, sizeof( buf ) ); + close( status_fd ); if ( num_read > 0 ) { From deb537c1bb0727a2cd15f3dca41abe2dc5fdd7ab Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 17:52:01 +0300 Subject: [PATCH 095/205] common: fix NULL redefine(do we really need NULL definition?) --- engine/common/common.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/common/common.h b/engine/common/common.h index 5c210fc5..32042092 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -103,6 +103,10 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc #define FBitSet( iBitVector, bit ) ((iBitVector) & (bit)) #ifndef __cplusplus +#ifdef NULL +#undef NULL +#endif + #define NULL ((void *)0) #endif From 28471aee97208d3c11861e23541a2499225ee199 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 18:19:31 +0300 Subject: [PATCH 096/205] cl_game: get rid of int* to float* conversion --- engine/client/cl_game.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index abf3b487..49d6331e 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1583,12 +1583,14 @@ CL_FillRGBA */ void CL_FillRGBA( int x, int y, int w, int h, int r, int g, int b, int a ) { + float _x = x, _y = y, _w = w, _h = h; + r = bound( 0, r, 255 ); g = bound( 0, g, 255 ); b = bound( 0, b, 255 ); a = bound( 0, a, 255 ); - SPR_AdjustSize( (float *)&x, (float *)&y, (float *)&w, (float *)&h ); + SPR_AdjustSize( &_x, &_y, &_w, &_h ); pglDisable( GL_TEXTURE_2D ); pglEnable( GL_BLEND ); @@ -1597,10 +1599,10 @@ void CL_FillRGBA( int x, int y, int w, int h, int r, int g, int b, int a ) pglColor4f( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ); pglBegin( GL_QUADS ); - pglVertex2f( x, y ); - pglVertex2f( x + w, y ); - pglVertex2f( x + w, y + h ); - pglVertex2f( x, y + h ); + pglVertex2f( _x, _y ); + pglVertex2f( _x + _w, _y ); + pglVertex2f( _x + _w, _y + _h ); + pglVertex2f( _x, _y + _h ); pglEnd (); pglColor3f( 1.0f, 1.0f, 1.0f ); @@ -2968,12 +2970,14 @@ pfnFillRGBABlend */ void GAME_EXPORT CL_FillRGBABlend( int x, int y, int w, int h, int r, int g, int b, int a ) { + float _x = x, _y = y, _w = w, _h = h; + r = bound( 0, r, 255 ); g = bound( 0, g, 255 ); b = bound( 0, b, 255 ); a = bound( 0, a, 255 ); - SPR_AdjustSize( (float *)&x, (float *)&y, (float *)&w, (float *)&h ); + SPR_AdjustSize( &_x, &_y, &_w, &_h ); pglDisable( GL_TEXTURE_2D ); pglEnable( GL_BLEND ); @@ -2982,10 +2986,10 @@ void GAME_EXPORT CL_FillRGBABlend( int x, int y, int w, int h, int r, int g, int pglColor4f( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ); pglBegin( GL_QUADS ); - pglVertex2f( x, y ); - pglVertex2f( x + w, y ); - pglVertex2f( x + w, y + h ); - pglVertex2f( x, y + h ); + pglVertex2f( _x, _y ); + pglVertex2f( _x + _w, _y ); + pglVertex2f( _x + _w, _y + _h ); + pglVertex2f( _x, _y + _h ); pglEnd (); pglColor3f( 1.0f, 1.0f, 1.0f ); From d74743ac1d8105655772fa415915a185e78d4c13 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 18:19:45 +0300 Subject: [PATCH 097/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 0b89d609..bc99e055 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 0b89d6090f9c5f655c5fb5ca441b70a6bc25a45e +Subproject commit bc99e055d5648aeb032f571943641edc8ced53dd From f0297fc448dd943f7433177180b7a9bc27dc7b32 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 18:33:32 +0300 Subject: [PATCH 098/205] travis: initial support --- .travis.yml | 42 +++++++++++++++++++++++++++++++++++ scripts/build_linux_engine.sh | 35 +++++++++++++++++++++++++++++ scripts/build_mingw_engine.sh | 15 +++++++++++++ scripts/build_osx_engine.sh | 14 ++++++++++++ scripts/travis_common_deps.sh | 2 ++ scripts/travis_linux_deps.sh | 25 +++++++++++++++++++++ scripts/travis_osx_deps.sh | 5 +++++ 7 files changed, 138 insertions(+) create mode 100644 .travis.yml create mode 100755 scripts/build_linux_engine.sh create mode 100755 scripts/build_mingw_engine.sh create mode 100755 scripts/build_osx_engine.sh create mode 100755 scripts/travis_common_deps.sh create mode 100755 scripts/travis_linux_deps.sh create mode 100755 scripts/travis_osx_deps.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..98df0a39 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +language: c +cache: ccache +compiler: gcc +sudo: false +addons: + apt: + packages: + - mingw-w64-i686-dev + - binutils-mingw-w64-i686 + - gcc-mingw-w64-i686 + - g++-mingw-w64-i686 + - p7zip-full + - gcc-multilib + - g++-multilib + - libx11-dev:i386 + - libxext-dev:i386 + - x11-utils + - libgl1-mesa-dev + - libasound-dev + - zlib1g:i386 + - libstdc++6:i386 +env: + global: + - SDL_VERSION=2.0.8 +git: + depth: 50 + submodules: true +jdk: + - oraclejdk8 +os: + - linux + - osx +# - windows +before_script: + - sh scripts/travis_common_deps.sh + - sh scripts/travis_${TRAVIS_OS_NAME}_deps.sh + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export ANDROID_HOME=$PWD/android-sdk-linux; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:$PWD/android-ndk; fi +script: + - sh scripts/build_${TRAVIS_OS_NAME}_engine.sh +# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/build_android_engine.sh; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/build_mingw_engine.sh; fi diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh new file mode 100755 index 00000000..ee1094f2 --- /dev/null +++ b/scripts/build_linux_engine.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Build custom SDL2 + +cd $TRAVIS_BUILD_DIR/SDL2-2.0.7 +export CC="ccache gcc -msse2 -march=i686 -m32 -ggdb -O2" +./configure \ + --disable-dependency-tracking \ + --disable-render \ + --disable-haptic \ + --disable-power \ + --disable-filesystem \ + --disable-file \ + --enable-alsa-shared \ + --enable-pulseaudio-shared \ + --enable-wayland-shared \ + --enable-x11-shared \ + --disable-libudev \ + --disable-dbus \ + --disable-ibus \ + --disable-ime \ + --disable-fcitx +make -j2 +mkdir -p $TRAVIS_BUILD_DIR/SDL2_linux +make install DESTDIR=$TRAVIS_BUILD_DIR/SDL2_linux + +# Build engine +cd $TRAVIS_BUILD_DIR +export CC="ccache gcc" +export CXX="ccache g++" +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug +./waf build -j2 +# cp engine/xash3d mainui/libxashmenu.so vgui_support/libvgui_support.so vgui_support/vgui.so ../scripts/xash3d.sh . +# cp $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/$(readlink $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/libSDL2-2.0.so.0) libSDL2-2.0.so.0 +# 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-linux.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on xash3d libSDL2-2.0.so.0 libvgui_support.so vgui.so libxashmenu.so xash3d.sh diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh new file mode 100755 index 00000000..496c7f88 --- /dev/null +++ b/scripts/build_mingw_engine.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Build engine + +cd $TRAVIS_BUILD_DIR +mkdir -p mingw-build && cd mingw-build +export CC="ccache i686-w64-mingw32-gcc" +export CXX="ccache i686-w64-mingw32-g++" +export CFLAGS="-static-libgcc -no-pthread" +export CXXFLAGS="-static-libgcc -static-libstdc++" +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw --no-vgui --build-type=debug # can't use VGUI on MinGW +./waf build -j2 +# cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 +# cp /usr/i686-w64-mingw32/lib/libwinpthread-1.dll . # a1ba: remove when travis will be updated to xenial +# 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-mingw.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on xash_sdl.exe menu.dll SDL2.dll vgui_support.dll libwinpthread-1.dll diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh new file mode 100755 index 00000000..43983e7f --- /dev/null +++ b/scripts/build_osx_engine.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Build engine + +cd $TRAVIS_BUILD_DIR +mkdir -p osx-build && cd osx-build +export CFLAGS="-m32" +export CXXFLAGS="-m32" +./waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug +./waf build -j2 +# mkdir -p pkg/ +# cp engine/libxash.dylib game_launch/xash3d mainui/libxashmenu.dylib vgui_support/libvgui_support.dylib VGUI/vgui-dev-master/lib/vgui.dylib ../scripts/xash3d.sh # pkg/ +# cp ~/Library/Frameworks/SDL2.framework/SDL2 pkg/libSDL2.dylib +# tar -cjf $TRAVIS_BUILD_DIR/xash3d-osx.tar.bz2 pkg/* diff --git a/scripts/travis_common_deps.sh b/scripts/travis_common_deps.sh new file mode 100755 index 00000000..9ac7ed3d --- /dev/null +++ b/scripts/travis_common_deps.sh @@ -0,0 +1,2 @@ +git clone --depth 1 https://github.com/FWGS/vgui-dev +git clone --depth 1 https://github.com/FWGS/vgui_support_bin diff --git a/scripts/travis_linux_deps.sh b/scripts/travis_linux_deps.sh new file mode 100755 index 00000000..7b7aed4f --- /dev/null +++ b/scripts/travis_linux_deps.sh @@ -0,0 +1,25 @@ +# SDL2 sources. We will build our own version +curl -s http://libsdl.org/release/SDL2-$SDL_VERSION.tar.gz | tar xzf - +mv SDL2-$SDL_VERSION SDL2_src + +# SDL2 for MinGW prebuilt +curl -s http://libsdl.org/release/SDL2-devel-$SDL_VERSION-mingw.tar.gz | tar xzf - +mv SDL2-$SDL_VERSION SDL2_mingw + +# Android build deps +# curl -s http://dl.google.com/android/android-sdk_r22.0.4-linux.tgz | tar xzf - +# export ANDROID_HOME=$PWD/android-sdk-linux +# export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:$PWD/android-ndk +# sleep 3s; echo y | android update sdk -u --filter platform-tools,build-tools-19.0.0,android-19 --force --all > /dev/null +# wget http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin >/dev/null 2>/dev/null +# 7z x ./android-ndk-r10e-linux-x86_64.bin > /dev/null +# mv android-ndk-r10e android-ndk + +# git clone --depth 1 https://github.com/FWGS/xash3d-android-project +# cd $TRAVIS_BUILD_DIR/xash3d-android-project +# cp debug.keystore ~/.android/debug.keystore +# git submodule update --init jni/src/NanoGL/nanogl xash-extras +# git clone --depth 1 https://github.com/FWGS/hlsdk-xash3d jni/src/hlsdk-xash3d +# rm -r jni/src/Xash3D/xash3d +# ln -s $TRAVIS_BUILD_DIR jni/src/Xash3D/xash3d +cd $TRAVIS_BUILD_DIR diff --git a/scripts/travis_osx_deps.sh b/scripts/travis_osx_deps.sh new file mode 100755 index 00000000..019f4697 --- /dev/null +++ b/scripts/travis_osx_deps.sh @@ -0,0 +1,5 @@ +curl -s https://www.libsdl.org/release/SDL2-$SDL_VERSION.dmg > SDL2.dmg +hdiutil attach SDL2.dmg +cd /Volumes/SDL2 +mkdir -p ~/Library/Frameworks +cp -r SDL2.framework ~/Library/Frameworks/ From 27ee4337fe6918817f48a25cd3b1114d0ec03281 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 18:52:57 +0300 Subject: [PATCH 099/205] travis: fix building sdl2, fix libpath in wscript --- engine/wscript | 5 ++++- scripts/build_linux_engine.sh | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/wscript b/engine/wscript index 3a4dc9ab..12d8be1e 100644 --- a/engine/wscript +++ b/engine/wscript @@ -33,7 +33,10 @@ def configure(conf): conf.start_msg('Configuring SDL2 by provided path') conf.env.HAVE_SDL2 = 1 conf.env.INCLUDES_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include'))] - conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'lib/x86'))] + libpath = 'lib' + if(conf.env.COMPILER_CC == 'msvc'): + libpath = 'lib/x86' + conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, libpath))] conf.env.LIB_SDL2 = ['SDL2'] conf.end_msg('ok') else: diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index ee1094f2..43d75de4 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -2,7 +2,7 @@ # Build custom SDL2 -cd $TRAVIS_BUILD_DIR/SDL2-2.0.7 +cd $TRAVIS_BUILD_DIR/SDL2_src export CC="ccache gcc -msse2 -march=i686 -m32 -ggdb -O2" ./configure \ --disable-dependency-tracking \ From 2b085cb69ed5a294115f394c23b876a0dce712a6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:33:12 +0300 Subject: [PATCH 100/205] game_launch: remove SDL for simplicity --- game_launch/game.cpp | 9 +-------- game_launch/wscript | 16 +--------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 5ea88592..c2cf2de7 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -15,11 +15,6 @@ GNU General Public License for more details. #include "port.h" -#ifdef XASH_SDL -#include -#include -#endif - #include #include #include @@ -69,9 +64,7 @@ static void Xash_Error( const char *szFmt, ... ) vsnprintf( buffer, sizeof(buffer), szFmt, args ); va_end( args ); -#ifdef XASH_SDL - SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Xash Error", buffer, NULL ); -#elif defined( _WIN32 ) +#if defined( _WIN32 ) MessageBoxA( NULL, buffer, "Xash Error", MB_OK ); #else fprintf( stderr, "Xash Error: %s\n", buffer ); diff --git a/game_launch/wscript b/game_launch/wscript index 116a13ed..9c7647a1 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -17,19 +17,7 @@ def configure(conf): # check for dedicated server build if not conf.env.DEDICATED: - if conf.env.DEST_OS != 'win32': # We need SDL2 for showing messagebox in case launcher has failed - # TODO: add way to specify SDL2 path, move to separate function - try: - conf.check_cfg( - path='sdl2-config', - args='--cflags --libs', - package='', - msg='Checking for SDL2', - uselib_store='SDL2') - except conf.errors.ConfigurationError: - conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') - conf.env.append_unique('DEFINES', 'XASH_SDL') - else: + if conf.env.DEST_OS == 'win32': conf.check(lib='USER32') conf.check(lib='SHELL32') @@ -49,8 +37,6 @@ def build(bld): if bld.env.DEST_OS != 'win32': libs += [ 'DL' ] - if not bld.env.DEDICATED: - libs += [ 'SDL2' ] else: # compile resource on Windows bld.load('winres') From 62af116654d01fed3c19a61a99a28813d5cf014d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:37:06 +0300 Subject: [PATCH 101/205] travis: scripts: set SDL2 prefix --- scripts/build_linux_engine.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index 43d75de4..dc8bbf4e 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -19,7 +19,8 @@ export CC="ccache gcc -msse2 -march=i686 -m32 -ggdb -O2" --disable-dbus \ --disable-ibus \ --disable-ime \ - --disable-fcitx + --disable-fcitx \ + --prefix / # get rid of /usr/local stuff make -j2 mkdir -p $TRAVIS_BUILD_DIR/SDL2_linux make install DESTDIR=$TRAVIS_BUILD_DIR/SDL2_linux From d3c310628f36cc36f1dd909255f2b8385da28728 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:41:16 +0300 Subject: [PATCH 102/205] travis: scripts: enable stb font renderer for linux --- scripts/build_linux_engine.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index dc8bbf4e..54a5e8e7 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -29,7 +29,7 @@ make install DESTDIR=$TRAVIS_BUILD_DIR/SDL2_linux cd $TRAVIS_BUILD_DIR export CC="ccache gcc" export CXX="ccache g++" -./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --use-stb ./waf build -j2 # cp engine/xash3d mainui/libxashmenu.so vgui_support/libvgui_support.so vgui_support/vgui.so ../scripts/xash3d.sh . # cp $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/$(readlink $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/libSDL2-2.0.so.0) libSDL2-2.0.so.0 From 7b806add6368ca0c6ebff1e8f69a2f16c9d88b23 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:48:52 +0300 Subject: [PATCH 103/205] wscript: fix SDL2 include path --- engine/wscript | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/wscript b/engine/wscript index 12d8be1e..44b2c1f9 100644 --- a/engine/wscript +++ b/engine/wscript @@ -32,13 +32,16 @@ def configure(conf): if(conf.options.SDL2_PATH): conf.start_msg('Configuring SDL2 by provided path') conf.env.HAVE_SDL2 = 1 - conf.env.INCLUDES_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include'))] + conf.env.INCLUDES_SDL2 = [ + os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include')), + os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL')) + ] libpath = 'lib' if(conf.env.COMPILER_CC == 'msvc'): libpath = 'lib/x86' conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, libpath))] conf.env.LIB_SDL2 = ['SDL2'] - conf.end_msg('ok') + conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_SDL2, conf.env.LIBPATH_SDL2, conf.env.INCLUDES_SDL2)) else: conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') From 4db01487ba4148ea5eb94c767dd0bd922fde766d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:55:47 +0300 Subject: [PATCH 104/205] travis: scripts: install python for osx --- scripts/travis_osx_deps.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/travis_osx_deps.sh b/scripts/travis_osx_deps.sh index 019f4697..8fc18681 100755 --- a/scripts/travis_osx_deps.sh +++ b/scripts/travis_osx_deps.sh @@ -1,3 +1,4 @@ +brew install python curl -s https://www.libsdl.org/release/SDL2-$SDL_VERSION.dmg > SDL2.dmg hdiutil attach SDL2.dmg cd /Volumes/SDL2 From 7852192fc4e3bc36f88104b26d7b137d97191bad Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 22:56:30 +0300 Subject: [PATCH 105/205] wscript: replace include/SDL by include/SDL2 --- engine/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/wscript b/engine/wscript index 44b2c1f9..5dd0c5f8 100644 --- a/engine/wscript +++ b/engine/wscript @@ -34,7 +34,7 @@ def configure(conf): conf.env.HAVE_SDL2 = 1 conf.env.INCLUDES_SDL2 = [ os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include')), - os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL')) + os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL2')) ] libpath = 'lib' if(conf.env.COMPILER_CC == 'msvc'): From e9d988f5d675670807b8d8f44d576a2013ccdc61 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 23:04:30 +0300 Subject: [PATCH 106/205] wscript: spaces to tabs --- wscript | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wscript b/wscript index 4a8a076f..9dff334c 100644 --- a/wscript +++ b/wscript @@ -46,15 +46,15 @@ def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': opt.load('msvc msvs') - + opt.add_option( '--dedicated', action = 'store_true', dest = 'DEDICATED', default = False, help = 'build Xash Dedicated Server(XashDS)') - + opt.add_option( '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, help = 'allow targetting 64-bit engine') - + opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') @@ -68,7 +68,7 @@ def options(opt): help = 'SDL2 path to build(required for Windows)') opt.add_option( - '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, + '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, help = 'build type: debug, release or none(custom flags)') opt.recurse(SUBDIRS) @@ -113,23 +113,23 @@ def configure(conf): Logs.warn('WARNING: 64-bit engine may be unstable') linker_flags = { - 'common': { - 'msvc': ['/DEBUG'], + 'common': { + 'msvc': ['/DEBUG'], 'default': ['-Wl,--no-undefined'] } } compiler_c_cxx_flags = { - 'common': { - 'msvc': ['/D_USING_V110_SDK71_'], + 'common': { + 'msvc': ['/D_USING_V110_SDK71_'], 'default': ['-g'] }, 'release': { - 'msvc': ['/Zi', '/O2'], + 'msvc': ['/Zi', '/O2'], 'default': ['-O3'] }, 'debug': { - 'msvc': ['/Z7'], + 'msvc': ['/Z7'], 'clang': ['-O0', '-gdwarf-2'], 'default': ['-O0'] } From 1b9bfd8906a6e87500abb895e6e288a2d3995004 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 23:04:47 +0300 Subject: [PATCH 107/205] platform: sdl: fix compiling --- engine/platform/sdl/events.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 14b6e7d9..421b8664 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -172,7 +172,8 @@ SDLash_InputEvent */ static void SDLash_InputEvent( SDL_TextInputEvent input ) { - for( char *text = input.text; *text; text++ ) + char *text; + for( text = input.text; *text; text++ ) { int ch; From a6fba0cb96f333069556e35346787e367b20b673 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 23:08:29 +0300 Subject: [PATCH 108/205] travis: scripts: try to fix mingw and osx build --- scripts/build_mingw_engine.sh | 5 ++--- scripts/build_osx_engine.sh | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index 496c7f88..a1548fef 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -3,13 +3,12 @@ # Build engine cd $TRAVIS_BUILD_DIR -mkdir -p mingw-build && cd mingw-build export CC="ccache i686-w64-mingw32-gcc" export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" -./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw --no-vgui --build-type=debug # can't use VGUI on MinGW -./waf build -j2 +./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug # can't compile VGUI support on MinGW, due to differnet C++ ABI +./waf build -o build-mingw -j2 # cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 # cp /usr/i686-w64-mingw32/lib/libwinpthread-1.dll . # a1ba: remove when travis will be updated to xenial # 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-mingw.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on xash_sdl.exe menu.dll SDL2.dll vgui_support.dll libwinpthread-1.dll diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh index 43983e7f..ef614172 100755 --- a/scripts/build_osx_engine.sh +++ b/scripts/build_osx_engine.sh @@ -6,8 +6,8 @@ cd $TRAVIS_BUILD_DIR mkdir -p osx-build && cd osx-build export CFLAGS="-m32" export CXXFLAGS="-m32" -./waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug -./waf build -j2 +python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug +python waf build -j2 # mkdir -p pkg/ # cp engine/libxash.dylib game_launch/xash3d mainui/libxashmenu.dylib vgui_support/libvgui_support.dylib VGUI/vgui-dev-master/lib/vgui.dylib ../scripts/xash3d.sh # pkg/ # cp ~/Library/Frameworks/SDL2.framework/SDL2 pkg/libSDL2.dylib From 81e142936dc958437fbedde66a237e80621ad65c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 18 Nov 2018 23:59:23 +0300 Subject: [PATCH 109/205] wscript: use lower-case win32 libraries names for crosscompiling from Linux --- engine/wscript | 14 +++++++------- game_launch/wscript | 6 +++--- mainui | 2 +- scripts/build_mingw_engine.sh | 1 + scripts/build_osx_engine.sh | 1 - 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/wscript b/engine/wscript index 5dd0c5f8..87c439a7 100644 --- a/engine/wscript +++ b/engine/wscript @@ -50,11 +50,11 @@ def configure(conf): conf.env.append_unique('DEFINES', 'SUPPORT_BSP2_FORMAT') if conf.env.DEST_OS == 'win32': - conf.check( lib='USER32' ) - conf.check( lib='SHELL32' ) - conf.check( lib='GDI32' ) - conf.check( lib='ADVAPI32' ) - conf.check( lib='DBGHELP' ) + conf.check( lib='user32' ) + conf.check( lib='shell32' ) + conf.check( lib='gdi32' ) + conf.check( lib='advapi32' ) + conf.check( lib='dbghelp' ) conf.env.append_unique('DEFINES', 'DBGHELP') def get_subproject_name(ctx): @@ -77,7 +77,7 @@ def build(bld): libs += [ 'DL', 'M', 'PTHREAD' ] source += bld.path.ant_glob(['platform/posix/*.c']) else: - libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP'] + libs += ['user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp'] source += bld.path.ant_glob(['platform/win32/*.c']) if bld.env.DEST_OS == 'linux': @@ -94,7 +94,7 @@ def build(bld): else: if(bld.env.DEST_OS == 'linux'): libs.append('RT') - + includes = ['common', 'server', 'client', 'client/vgui', '.', '../common', '../pm_shared' ] if(bld.env.SINGLE_BINARY): diff --git a/game_launch/wscript b/game_launch/wscript index 9c7647a1..1141b33b 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -18,8 +18,8 @@ def configure(conf): # check for dedicated server build if not conf.env.DEDICATED: if conf.env.DEST_OS == 'win32': - conf.check(lib='USER32') - conf.check(lib='SHELL32') + conf.check(lib='user32') + conf.check(lib='shell32') def get_subproject_name(ctx): return os.path.basename(os.path.realpath(str(ctx.path))) @@ -40,7 +40,7 @@ def build(bld): else: # compile resource on Windows bld.load('winres') - libs += ['USER32', 'SHELL32'] + libs += ['user32', 'shell32'] source += ['game.rc'] bld( diff --git a/mainui b/mainui index bc99e055..177e49ca 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit bc99e055d5648aeb032f571943641edc8ced53dd +Subproject commit 177e49ca640a4a695df92106c211e5a47a838c8b diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index a1548fef..947e7986 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -7,6 +7,7 @@ export CC="ccache i686-w64-mingw32-gcc" export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" +export WINRC="i686-w64-mingw32-windres" ./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug # can't compile VGUI support on MinGW, due to differnet C++ ABI ./waf build -o build-mingw -j2 # cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh index ef614172..48220770 100755 --- a/scripts/build_osx_engine.sh +++ b/scripts/build_osx_engine.sh @@ -3,7 +3,6 @@ # Build engine cd $TRAVIS_BUILD_DIR -mkdir -p osx-build && cd osx-build export CFLAGS="-m32" export CXXFLAGS="-m32" python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug From 776d1cb52cbf38700d711ad4dc5d8cc127473070 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Nov 2018 01:19:24 +0300 Subject: [PATCH 110/205] wscript: try to fix windres searching for game_launch, fix osx 32 bit compiling --- .travis.yml | 9 +++++---- engine/wscript | 5 +++-- game_launch/wscript | 7 +++---- mainui | 2 +- scripts/build_osx_engine.sh | 2 ++ scripts/travis_osx_deps.sh | 2 +- vgui_support/wscript | 2 +- wscript | 14 +++++++++----- 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98df0a39..d22c57e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,10 +27,11 @@ git: submodules: true jdk: - oraclejdk8 -os: - - linux - - osx -# - windows +matrix: + include: + - os: linux + dist: xenial + - os: osx before_script: - sh scripts/travis_common_deps.sh - sh scripts/travis_${TRAVIS_OS_NAME}_deps.sh diff --git a/engine/wscript b/engine/wscript index 87c439a7..b0d53a74 100644 --- a/engine/wscript +++ b/engine/wscript @@ -55,6 +55,7 @@ def configure(conf): conf.check( lib='gdi32' ) conf.check( lib='advapi32' ) conf.check( lib='dbghelp' ) + conf.check( lib='psapi' ) conf.env.append_unique('DEFINES', 'DBGHELP') def get_subproject_name(ctx): @@ -74,10 +75,10 @@ def build(bld): # basic build: dedicated only, no dependencies if bld.env.DEST_OS != 'win32': - libs += [ 'DL', 'M', 'PTHREAD' ] + libs += [ 'DL' , 'M', 'PTHREAD' ] source += bld.path.ant_glob(['platform/posix/*.c']) else: - libs += ['user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp'] + libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI'] source += bld.path.ant_glob(['platform/win32/*.c']) if bld.env.DEST_OS == 'linux': diff --git a/game_launch/wscript b/game_launch/wscript index 1141b33b..9029ea2a 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -18,6 +18,7 @@ def configure(conf): # check for dedicated server build if not conf.env.DEDICATED: if conf.env.DEST_OS == 'win32': + conf.load('winres') conf.check(lib='user32') conf.check(lib='shell32') @@ -34,13 +35,11 @@ def build(bld): source = ['game.cpp'] includes = '. ../common' libs = [] - + if bld.env.DEST_OS != 'win32': libs += [ 'DL' ] else: - # compile resource on Windows - bld.load('winres') - libs += ['user32', 'shell32'] + libs += ['USER32', 'SHELL32'] source += ['game.rc'] bld( diff --git a/mainui b/mainui index 177e49ca..792072b7 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 177e49ca640a4a695df92106c211e5a47a838c8b +Subproject commit 792072b73e3b2d9d6e0da1b9521959663973c98c diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh index 48220770..d0ff01f9 100755 --- a/scripts/build_osx_engine.sh +++ b/scripts/build_osx_engine.sh @@ -3,6 +3,8 @@ # Build engine cd $TRAVIS_BUILD_DIR +export CC="/usr/bin/clang" +export CXX="/usr/bin/clang++" export CFLAGS="-m32" export CXXFLAGS="-m32" python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug diff --git a/scripts/travis_osx_deps.sh b/scripts/travis_osx_deps.sh index 8fc18681..0d3dd7b3 100755 --- a/scripts/travis_osx_deps.sh +++ b/scripts/travis_osx_deps.sh @@ -1,4 +1,4 @@ -brew install python +# brew install python curl -s https://www.libsdl.org/release/SDL2-$SDL_VERSION.dmg > SDL2.dmg hdiutil attach SDL2.dmg cd /Volumes/SDL2 diff --git a/vgui_support/wscript b/vgui_support/wscript index 6a6bb879..ae2a57f1 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -63,7 +63,7 @@ def build(bld): # basic build: dedicated only, no dependencies if bld.env.DEST_OS != 'win32': - libs = [ 'DL', 'M' ] + libs += ['DL','M'] libs.append('VGUI') diff --git a/wscript b/wscript index 9dff334c..0b12a0df 100644 --- a/wscript +++ b/wscript @@ -105,10 +105,14 @@ def configure(conf): if(conf.env.DEST_64BIT): if(not conf.options.ALLOW64): - conf.env.append_value('LINKFLAGS', ['-m32']) - conf.env.append_value('CFLAGS', ['-m32']) - conf.env.append_value('CXXFLAGS', ['-m32']) - Logs.info('NOTE: will build engine with 64-bit toolchain using -m32') + flag = '-m32' + # Think different. + if(conf.env.DEST_OS == 'darwin'): + flag = '-arch i386' + conf.env.append_value('LINKFLAGS', [flag]) + conf.env.append_value('CFLAGS', [flag]) + conf.env.append_value('CXXFLAGS', [flag]) + Logs.info('NOTE: will build engine with 64-bit toolchain using %s' % flag) else: Logs.warn('WARNING: 64-bit engine may be unstable') @@ -169,7 +173,7 @@ def configure(conf): git_version = get_git_version() conf.end_msg(git_version) conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="' + git_version + '"') - + for i in SUBDIRS: conf.setenv(i, conf.env) # derive new env from global one conf.env.ENVNAME = i From 622de7a7f16f53ce2d5719ee864acbba73384dcc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 27 Nov 2018 16:11:26 +0300 Subject: [PATCH 111/205] Apply 4312 update --- engine/client/cl_cmds.c | 26 ++--------- engine/client/cl_demo.c | 95 ++++++++++++++++++-------------------- engine/client/cl_main.c | 5 +- engine/client/cl_scrn.c | 1 - engine/client/cl_view.c | 1 + engine/client/client.h | 3 +- engine/client/gl_backend.c | 94 ++++++++++++++++++++++++++++++++++++- engine/client/gl_local.h | 6 +++ engine/client/gl_rmain.c | 17 +++++-- engine/client/gl_rmisc.c | 1 + engine/client/gl_vidnt.c | 2 + engine/client/gl_warp.c | 7 +++ engine/client/s_main.c | 2 +- engine/common/avikit.c | 3 ++ engine/common/con_utils.c | 3 +- engine/common/crclib.c | 2 +- engine/common/input.c | 4 +- engine/common/mod_bmodel.c | 26 ++++++++++- engine/server/sv_cmds.c | 52 +++++++++++++++++++++ 19 files changed, 259 insertions(+), 91 deletions(-) diff --git a/engine/client/cl_cmds.c b/engine/client/cl_cmds.c index c69a6b5e..a5d711d6 100644 --- a/engine/client/cl_cmds.c +++ b/engine/client/cl_cmds.c @@ -345,7 +345,7 @@ void CL_LevelShot_f( void ) if( cls.demoplayback && ( cls.demonum != -1 )) { Q_sprintf( cls.shotname, "levelshots/%s_%s.bmp", cls.demoname, glState.wideScreen ? "16x9" : "4x3" ); - Q_snprintf( filename, sizeof( filename ), "demos/%s.dem", cls.demoname ); + Q_snprintf( filename, sizeof( filename ), "%s.dem", cls.demoname ); // make sure what levelshot is newer than demo ft1 = FS_FileTime( filename, false ); @@ -385,25 +385,6 @@ void CL_SaveShot_f( void ) cls.scrshot_action = scrshot_savegame; // build new frame for saveshot } -/* -================== -CL_DemoShot_f - -mini-pic in playdemo menu -================== -*/ -void CL_DemoShot_f( void ) -{ - if( Cmd_Argc() < 2 ) - { - Con_Printf( S_USAGE "demoshot \n" ); - return; - } - - Q_sprintf( cls.shotname, "demos/%s.bmp", Cmd_Argv( 1 )); - cls.scrshot_action = scrshot_demoshot; // build new frame for demoshot -} - /* ============== CL_DeleteDemo_f @@ -424,9 +405,8 @@ void CL_DeleteDemo_f( void ) return; } - // delete save and saveshot - FS_Delete( va( "demos/%s.dem", Cmd_Argv( 1 ))); - FS_Delete( va( "demos/%s.bmp", Cmd_Argv( 1 ))); + // delete demo + FS_Delete( va( "%s.dem", Cmd_Argv( 1 ))); } /* diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index f1ffbc27..208d0734 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -669,31 +669,6 @@ void CL_DemoStartPlayback( int mode ) cl.last_command_ack = -1; } -/* -================= -CL_PlayDemoQuake -================= -*/ -void CL_PlayDemoQuake( const char *demoname ) -{ - int c, neg = false; - - cls.demofile = FS_Open( demoname, "rb", true ); - Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); - Q_strncpy( gameui.globals->demoname, demoname, sizeof( gameui.globals->demoname )); - demo.header.host_fps = host_maxfps->value; - cls.forcetrack = 0; - - while(( c = FS_Getc( cls.demofile )) != '\n' ) - { - if( c == '-' ) neg = true; - else cls.forcetrack = cls.forcetrack * 10 + (c - '0'); - } - - if( neg ) cls.forcetrack = -cls.forcetrack; - CL_DemoStartPlayback( DEMO_QUAKE1 ); -} - /* ================= CL_DemoAborted @@ -1178,6 +1153,7 @@ void CL_StopPlayback( void ) // let game known about demo state Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); cls.state = ca_disconnected; + cls.set_lastdemo = false; S_StopBackgroundTrack(); cls.connect_time = 0; cls.demonum = -1; @@ -1356,8 +1332,8 @@ Begins recording a demo from the current position */ void CL_Record_f( void ) { + string demoname, demopath; const char *name; - string demoname, demopath, demoshot; int n; if( Cmd_Argc() == 1 ) @@ -1398,7 +1374,7 @@ void CL_Record_f( void ) for( n = 0; n < 10000; n++ ) { CL_DemoGetName( n, demoname ); - if( !FS_FileExists( va( "demos/%s.dem", demoname ), true )) + if( !FS_FileExists( va( "%s.dem", demoname ), true )) break; } @@ -1411,18 +1387,12 @@ void CL_Record_f( void ) else Q_strncpy( demoname, name, sizeof( demoname )); // open the demo file - Q_sprintf( demopath, "demos/%s.dem", demoname ); - Q_sprintf( demoshot, "demos/%s.bmp", demoname ); - - // unload previous image from memory (it's will be overwritten) - GL_FreeImage( demoshot ); + Q_sprintf( demopath, "%s.dem", demoname ); // make sure what old demo is removed - if( FS_FileExists( demopath, false )) FS_Delete( demopath ); - if( FS_FileExists( demoshot, false )) FS_Delete( demoshot ); + if( FS_FileExists( demopath, false )) + FS_Delete( demopath ); - // write demoshot for preview - Cbuf_AddText( va( "demoshot \"%s\"\n", demoname )); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( gameui.globals->demoname, demoname, sizeof( gameui.globals->demoname )); @@ -1438,12 +1408,11 @@ playdemo */ void CL_PlayDemo_f( void ) { - char filename1[MAX_QPATH]; - char filename2[MAX_QPATH]; + char filename[MAX_QPATH]; char demoname[MAX_QPATH]; - int i; + int i, ident; - if( Cmd_Argc() != 2 ) + if( Cmd_Argc() < 2 ) { Con_Printf( S_USAGE "playdemo \n" ); return; @@ -1462,26 +1431,50 @@ void CL_PlayDemo_f( void ) Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname )); COM_StripExtension( demoname ); - Q_snprintf( filename1, sizeof( filename1 ), "%s.dem", demoname ); - Q_snprintf( filename2, sizeof( filename2 ), "demos/%s.dem", demoname ); + Q_snprintf( filename, sizeof( filename ), "%s.dem", demoname ); - if( FS_FileExists( filename1, true )) + // hidden parameter + if( Cmd_Argc() > 2 ) + cls.set_lastdemo = Q_atoi( Cmd_Argv( 2 )); + + // member last demo + if( cls.set_lastdemo ) + Cvar_Set( "lastdemo", demoname ); + + if( !FS_FileExists( filename, true )) { - CL_PlayDemoQuake( filename1 ); - return; - } - else if( !FS_FileExists( filename2, true )) - { - Con_Printf( S_ERROR "couldn't open %s\n", filename2 ); + Con_Printf( S_ERROR "couldn't open %s\n", filename ); CL_DemoAborted(); return; } - cls.demofile = FS_Open( filename2, "rb", true ); + cls.demofile = FS_Open( filename, "rb", true ); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( gameui.globals->demoname, demoname, sizeof( gameui.globals->demoname )); - // read in the m_DemoHeader + FS_Read( cls.demofile, &ident, sizeof( int )); + FS_Seek( cls.demofile, 0, SEEK_SET ); // rewind back to start + cls.forcetrack = 0; + + // check for quake demos + if( ident != IDEMOHEADER ) + { + int c, neg = false; + + demo.header.host_fps = host_maxfps->value; + + while(( c = FS_Getc( cls.demofile )) != '\n' ) + { + if( c == '-' ) neg = true; + else cls.forcetrack = cls.forcetrack * 10 + (c - '0'); + } + + if( neg ) cls.forcetrack = -cls.forcetrack; + CL_DemoStartPlayback( DEMO_QUAKE1 ); + return; // quake demo is started + } + + // read in the demo header FS_Read( cls.demofile, &demo.header, sizeof( demoheader_t )); if( demo.header.id != IDEMOHEADER ) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 73dd1c41..e6071e0c 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1416,6 +1416,7 @@ void CL_Disconnect( void ) Netchan_Clear( &cls.netchan ); cls.state = ca_disconnected; + cls.set_lastdemo = false; cls.connect_retry = 0; cls.signon = 0; @@ -2627,6 +2628,7 @@ void CL_InitLocal( void ) Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" ); Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" ); cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" ); + Cvar_Get( "lastdemo", "", FCVAR_ARCHIVE, "last played demo" ); // these two added to shut up CS 1.5 about 'unknown' commands Cvar_Get( "lightgamma", "1", FCVAR_ARCHIVE, "ambient lighting level (legacy, unused)" ); @@ -2658,7 +2660,7 @@ void CL_InitLocal( void ) Cmd_AddCommand ("record", CL_Record_f, "record a demo" ); Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "play a demo" ); Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "demo benchmark" ); - Cmd_AddCommand ("killdemo", CL_DeleteDemo_f, "delete a specified demo file and demoshot" ); + Cmd_AddCommand ("killdemo", CL_DeleteDemo_f, "delete a specified demo file" ); Cmd_AddCommand ("startdemos", CL_StartDemos_f, "start playing back the selected demos sequentially" ); Cmd_AddCommand ("demos", CL_Demos_f, "restart looping demos defined by the last startdemos command" ); Cmd_AddCommand ("movie", CL_PlayVideo_f, "play a movie" ); @@ -2680,7 +2682,6 @@ void CL_InitLocal( void ) Cmd_AddCommand ("skyshot", CL_SkyShot_f, "takes a six-sides envmap (skybox) shot with specified name" ); Cmd_AddCommand ("levelshot", CL_LevelShot_f, "same as \"screenshot\", used for create plaque images" ); Cmd_AddCommand ("saveshot", CL_SaveShot_f, "used for create save previews with LoadGame menu" ); - Cmd_AddCommand ("demoshot", CL_DemoShot_f, "used for create demo previews with PlayDemo menu" ); Cmd_AddCommand ("connect", CL_Connect_f, "connect to a server by hostname" ); Cmd_AddCommand ("reconnect", CL_Reconnect_f, "reconnect to current level" ); diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index c5b89bc6..44c493fa 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -255,7 +255,6 @@ void SCR_MakeScreenShot( void ) iRet = VID_ScreenShot( cls.shotname, VID_LEVELSHOT ); break; case scrshot_savegame: - case scrshot_demoshot: iRet = VID_ScreenShot( cls.shotname, VID_MINISHOT ); break; case scrshot_envshot: diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 7db22f67..069f47d4 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -384,6 +384,7 @@ void V_PostRender( void ) CL_DrawDemoRecording(); CL_DrawHUD( CL_CHANGELEVEL ); R_ShowTextures(); + R_ShowTree(); Con_DrawConsole(); UI_UpdateMenu( host.realtime ); Con_DrawVersion(); diff --git a/engine/client/client.h b/engine/client/client.h index 385e18dc..4c31d79d 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -320,7 +320,6 @@ typedef enum scrshot_snapshot, // in-game snapshot scrshot_plaque, // levelshot scrshot_savegame, // saveshot - scrshot_demoshot, // for demos preview scrshot_envshot, // cubemap view scrshot_skyshot, // skybox view scrshot_mapshot // overview layer @@ -654,6 +653,7 @@ typedef struct qboolean timedemo; string demoname; // for demo looping double demotime; // recording time + qboolean set_lastdemo; // store name of last played demo into the cvar file_t *demofile; file_t *demoheader; // contain demo startup info in case we record a demo on this level @@ -734,7 +734,6 @@ void CL_PlayCDTrack_f( void ); void CL_EnvShot_f( void ); void CL_SkyShot_f( void ); void CL_SaveShot_f( void ); -void CL_DemoShot_f( void ); void CL_LevelShot_f( void ); void CL_SetSky_f( void ); void SCR_Viewpos_f( void ); diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index a4f267df..6ecbe85b 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -654,7 +654,7 @@ void R_ShowTextures( void ) static qboolean showHelp = true; string shortname; - if( !gl_showtextures->value ) + if( !CVAR_TO_BOOL( gl_showtextures )) return; if( showHelp ) @@ -746,4 +746,96 @@ rebuild_page: CL_DrawCenterPrint (); pglFinish(); +} + +#define POINT_SIZE 16.0f +#define NODE_INTERVAL_X(x) (x * 16.0f) +#define NODE_INTERVAL_Y(x) (x * 16.0f) + +void R_DrawLeafNode( float x, float y, float scale ) +{ + float downScale = scale * 0.25f;// * POINT_SIZE; + + R_DrawStretchPic( x - downScale * 0.5f, y - downScale * 0.5f, downScale, downScale, 0, 0, 1, 1, tr.particleTexture ); +} + +void R_DrawNodeConnection( float x, float y, float x2, float y2 ) +{ + pglBegin( GL_LINES ); + pglVertex2f( x, y ); + pglVertex2f( x2, y2 ); + pglEnd(); +} + +void R_ShowTree_r( mnode_t *node, float x, float y, float scale, int shownodes ) +{ + float downScale = scale * 0.8f; + + downScale = Q_max( downScale, 1.0f ); + + if( !node ) return; + + tr.recursion_level++; + + if( node->contents < 0 ) + { + mleaf_t *leaf = (mleaf_t *)node; + + if( tr.recursion_level > tr.max_recursion ) + tr.max_recursion = tr.recursion_level; + + if( shownodes == 1 ) + { + if( cl.worldmodel->leafs == leaf ) + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + else if( RI.viewleaf && RI.viewleaf == leaf ) + pglColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); + else pglColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); + R_DrawLeafNode( x, y, scale ); + } + tr.recursion_level--; + return; + } + + if( shownodes == 1 ) + { + pglColor4f( 0.0f, 0.0f, 1.0f, 1.0f ); + R_DrawLeafNode( x, y, scale ); + } + else if( shownodes == 2 ) + { + R_DrawNodeConnection( x, y, x - scale, y + scale ); + R_DrawNodeConnection( x, y, x + scale, y + scale ); + } + + R_ShowTree_r( node->children[1], x - scale, y + scale, downScale, shownodes ); + R_ShowTree_r( node->children[0], x + scale, y + scale, downScale, shownodes ); + + tr.recursion_level--; +} + +void R_ShowTree( void ) +{ + float x = (float)((glState.width - (int)POINT_SIZE) >> 1); + float y = NODE_INTERVAL_Y(1.0); + + if( !cl.worldmodel || !CVAR_TO_BOOL( r_showtree )) + return; + + tr.recursion_level = 0; + + pglEnable( GL_BLEND ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + pglLineWidth( 2.0f ); + pglColor3f( 1, 0.7f, 0 ); + pglDisable( GL_TEXTURE_2D ); + R_ShowTree_r( cl.worldmodel->nodes, x, y, tr.max_recursion * 3.5f, 2 ); + pglEnable( GL_TEXTURE_2D ); + pglLineWidth( 1.0f ); + + R_ShowTree_r( cl.worldmodel->nodes, x, y, tr.max_recursion * 3.5f, 1 ); + + Con_NPrintf( 0, "max recursion %d\n", tr.max_recursion ); } \ No newline at end of file diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index c8edd17c..51bb43b4 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -202,6 +202,10 @@ typedef struct qboolean fResetVis; qboolean fFlipViewModel; + // tree visualization stuff + int recursion_level; + int max_recursion; + byte visbytes[(MAX_MAP_LEAFS+7)/8]; // member custom PVS int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536 int block_size; // lightmap blocksize @@ -270,6 +274,7 @@ void GL_SetRenderMode( int mode ); void GL_TextureTarget( uint target ); void GL_Cull( GLenum cull ); void R_ShowTextures( void ); +void R_ShowTree( void ); // // gl_cull.c @@ -655,6 +660,7 @@ extern convar_t *gl_msaa; extern convar_t *r_speeds; extern convar_t *r_fullbright; extern convar_t *r_norefresh; +extern convar_t *r_showtree; // build graph of visible hull extern convar_t *r_lighting_extended; extern convar_t *r_lighting_modulate; extern convar_t *r_lighting_ambient; diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 1b82f595..947e213a 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -658,13 +658,21 @@ static void R_CheckFog( void ) int i, cnt, count; // quake global fog - if( clgame.movevars.fog_settings != 0 && CL_IsQuakeCompatible( )) + if( CL_IsQuakeCompatible( )) { + if( !clgame.movevars.fog_settings ) + { + if( pglIsEnabled( GL_FOG )) + pglDisable( GL_FOG ); + RI.fogEnabled = false; + return; + } + // quake-style global fog RI.fogColor[0] = ((clgame.movevars.fog_settings & 0xFF000000) >> 24) / 255.0f; RI.fogColor[1] = ((clgame.movevars.fog_settings & 0xFF0000) >> 16) / 255.0f; RI.fogColor[2] = ((clgame.movevars.fog_settings & 0xFF00) >> 8) / 255.0f; - RI.fogDensity = ((clgame.movevars.fog_settings & 0xFF) / 255.0f) * 0.015625f; + RI.fogDensity = ((clgame.movevars.fog_settings & 0xFF) / 255.0f) * 0.01f; RI.fogStart = RI.fogEnd = 0.0f; RI.fogColor[3] = 1.0f; RI.fogCustom = false; @@ -773,7 +781,9 @@ void R_DrawFog( void ) if( !RI.fogEnabled ) return; pglEnable( GL_FOG ); - pglFogi( GL_FOG_MODE, GL_EXP ); + if( CL_IsQuakeCompatible( )) + pglFogi( GL_FOG_MODE, GL_EXP2 ); + else pglFogi( GL_FOG_MODE, GL_EXP ); pglFogf( GL_FOG_DENSITY, RI.fogDensity ); pglFogfv( GL_FOG_COLOR, RI.fogColor ); pglHint( GL_FOG_HINT, GL_NICEST ); @@ -987,7 +997,6 @@ qboolean R_DoResetGamma( void ) return false; case scrshot_plaque: case scrshot_savegame: - case scrshot_demoshot: case scrshot_envshot: case scrshot_skyshot: case scrshot_mapshot: diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index 727f3fa4..8b6e2c52 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -179,6 +179,7 @@ void R_NewMap( void ) cl.worldmodel->leafs[i+1].efrags = NULL; tr.skytexturenum = -1; + tr.max_recursion = 0; pglDisable( GL_FOG ); // clearing texture chains diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 8f84d0fd..5eb86b04 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -60,6 +60,7 @@ convar_t *r_lighting_ambient; convar_t *r_detailtextures; convar_t *r_drawentities; convar_t *r_adjust_fov; +convar_t *r_showtree; convar_t *r_decals; convar_t *r_novis; convar_t *r_nocull; @@ -1596,6 +1597,7 @@ void GL_InitCommands( void ) r_lightmap = Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); r_drawentities = Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT|FCVAR_ARCHIVE, "render entities" ); r_decals = Cvar_Get( "r_decals", "4096", FCVAR_ARCHIVE, "sets the maximum number of decals" ); + r_showtree = Cvar_Get( "r_showtree", "0", FCVAR_ARCHIVE, "build the graph of visible BSP tree" ); window_xpos = Cvar_Get( "_window_xpos", "130", FCVAR_RENDERINFO, "window position by horizontal" ); window_ypos = Cvar_Get( "_window_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" ); diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index a6b71a6e..b49cc2d1 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -372,6 +372,9 @@ void R_DrawSkyBox( void ) // don't fogging skybox (this fix old Half-Life bug) if( !RI.fogSkybox ) R_AllowFog( false ); + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity * 0.5f ); + pglDisable( GL_BLEND ); pglDisable( GL_ALPHA_TEST ); pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); @@ -395,6 +398,10 @@ void R_DrawSkyBox( void ) if( !RI.fogSkybox ) R_AllowFog( true ); + + if( RI.fogEnabled ) + pglFogf( GL_FOG_DENSITY, RI.fogDensity ); + R_LoadIdentity(); } diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 270c275b..5375d6db 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1579,7 +1579,7 @@ S_RawSamples */ void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum ) { - int snd_vol; + int snd_vol = 128; if( entnum < 0 ) snd_vol = 256; // bg track or movie track if( snd_vol < 0 ) snd_vol = 0; // fixup negative values diff --git a/engine/common/avikit.c b/engine/common/avikit.c index c2cc4c63..3ef6c7e6 100644 --- a/engine/common/avikit.c +++ b/engine/common/avikit.c @@ -401,8 +401,11 @@ long AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long l } else { + // we out of soundtrack, just zeroing buffer for( i = 0; i < length; i++ ) audiodata[i] = 0; + + return length; } } diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 04bf2d2b..d1150163 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -191,7 +191,8 @@ qboolean Cmd_GetDemoList( const char *s, char *completedname, int length ) string matchbuf; int i, numdems; - t = FS_Search( va( "demos/%s*.dem", s ), true, true ); // lookup only in gamedir + // lookup only in gamedir + t = FS_Search( va( "%s*.dem", s ), true, true ); if( !t ) return false; COM_FileBase( t->filenames[0], matchbuf ); diff --git a/engine/common/crclib.c b/engine/common/crclib.c index 165708bc..c08a44a1 100644 --- a/engine/common/crclib.c +++ b/engine/common/crclib.c @@ -422,7 +422,7 @@ void MD5Final( byte digest[16], MD5Context_t *ctx ) MD5Transform( ctx->buf, (uint *)ctx->in ); memcpy( digest, ctx->buf, 16 ); - memset( ctx, 0, sizeof( ctx )); // in case it's sensitive + memset( ctx, 0, sizeof( *ctx )); // in case it's sensitive } // The four core functions diff --git a/engine/common/input.c b/engine/common/input.c index 786e8308..67f79067 100644 --- a/engine/common/input.c +++ b/engine/common/input.c @@ -561,8 +561,8 @@ LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ) IN_MouseEvent( temp ); break; case WM_SYSCOMMAND: - // never turn screensaver while Xash is active - if( wParam == SC_SCREENSAVE && host.status != HOST_SLEEP ) + // never turn screensaver or display off while Xash is active + if(( wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER ) && host.status != HOST_SLEEP ) return 0; break; case WM_SYSKEYDOWN: diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 8a1ab247..deec5856 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -1086,7 +1086,7 @@ static void Mod_SetParent( mnode_t *node, mnode_t *parent ) CountClipNodes_r ================== */ -static void CountClipNodes_r( dclipnode32_t *src, hull_t *hull, int nodenum ) +static void CountClipNodes_r( mclipnode_t *src, hull_t *hull, int nodenum ) { // leaf? if( nodenum < 0 ) return; @@ -1099,6 +1099,24 @@ static void CountClipNodes_r( dclipnode32_t *src, hull_t *hull, int nodenum ) CountClipNodes_r( src, hull, src[nodenum].children[1] ); } +/* +================== +CountClipNodes32_r +================== +*/ +static void CountClipNodes32_r( dclipnode32_t *src, hull_t *hull, int nodenum ) +{ + // leaf? + if( nodenum < 0 ) return; + + if( hull->lastclipnode == MAX_MAP_CLIPNODES ) + Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" ); + hull->lastclipnode++; + + CountClipNodes32_r( src, hull, src[nodenum].children[0] ); + CountClipNodes32_r( src, hull, src[nodenum].children[1] ); +} + /* ================== RemapClipNodes_r @@ -1210,7 +1228,7 @@ static void Mod_SetupHull( dbspmodel_t *bmod, model_t *mod, byte *mempool, int h if( VectorIsNull( hull->clip_mins ) && VectorIsNull( hull->clip_maxs )) return; // no hull specified - CountClipNodes_r( bmod->clipnodes_out, hull, headnode ); + CountClipNodes32_r( bmod->clipnodes_out, hull, headnode ); count = hull->lastclipnode; // fit array to real count @@ -1358,6 +1376,10 @@ static void Mod_SetupSubmodels( dbspmodel_t *bmod ) // hull 0 is just shared across all bmodels mod->hulls[0].firstclipnode = bm->headnode[0]; + mod->hulls[0].lastclipnode = bm->headnode[0]; // need to be real count + + // counting a real number of clipnodes per each submodel + CountClipNodes_r( mod->hulls[0].clipnodes, &mod->hulls[0], bm->headnode[0] ); // but hulls1-3 is build individually for a each given submodel for( j = 1; j < MAX_MAP_HULLS; j++ ) diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 72954f63..d894e546 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -268,6 +268,57 @@ void SV_MapBackground_f( void ) COM_LoadLevel( mapname, true ); } +/* +================== +SV_NextMap_f + +Change map for next in alpha-bethical ordering +For development work +================== +*/ +void SV_NextMap_f( void ) +{ + char nextmap[MAX_QPATH]; + int i, next; + search_t *t; + + t = FS_Search( "maps/*.bsp", true, true ); // only in gamedir + if( !t ) + { + Con_Printf( "next map can't be found\n" ); + return; + } + + for( i = 0; i < t->numfilenames; i++ ) + { + const char *ext = COM_FileExtension( t->filenames[i] ); + + if( Q_stricmp( ext, "bsp" )) + continue; + + COM_FileBase( t->filenames[i], nextmap ); + if( Q_stricmp( sv_hostmap->string, nextmap )) + continue; + + next = ( i + 1 ) % t->numfilenames; + COM_FileBase( t->filenames[next], nextmap ); + Cvar_DirectSet( sv_hostmap, nextmap ); + + // found current point, check for valid + if( SV_ValidateMap( nextmap, true )) + { + // found and valid + COM_LoadLevel( nextmap, false ); + Mem_Free( t ); + return; + } + // jump to next map + } + + Con_Printf( "failed to load next map\n" ); + Mem_Free( t ); +} + /* ============== SV_NewGame_f @@ -839,6 +890,7 @@ void SV_InitHostCommands( void ) Cmd_AddCommand( "loadquick", SV_QuickLoad_f, "load a quick-saved game file" ); Cmd_AddCommand( "reload", SV_Reload_f, "continue from latest save or restart level" ); Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" ); + Cmd_AddCommand( "nextmap", SV_NextMap_f, "load next level" ); } } From dcf64c6b33cda6d59a087bfc036c5d0bcb01e7ac Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 27 Nov 2018 16:38:22 +0300 Subject: [PATCH 112/205] Fix build, update menu --- engine/client/gl_rmain.c | 2 +- engine/client/vid_common.c | 1 + mainui | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index b305b99f..46813497 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -782,7 +782,7 @@ void R_DrawFog( void ) if( !RI.fogEnabled ) return; pglEnable( GL_FOG ); - if( CL_IsQuakeCompatible( )) + if( Host_IsQuakeCompatible( )) pglFogi( GL_FOG_MODE, GL_EXP2 ); else pglFogi( GL_FOG_MODE, GL_EXP ); pglFogf( GL_FOG_DENSITY, RI.fogDensity ); diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index ca4e2fe1..e9531d53 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -49,6 +49,7 @@ convar_t *window_ypos; convar_t *r_speeds; convar_t *r_fullbright; convar_t *r_norefresh; +convar_t *r_showtree; convar_t *r_lighting_extended; convar_t *r_lighting_modulate; convar_t *r_lighting_ambient; diff --git a/mainui b/mainui index 792072b7..bee475b9 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 792072b73e3b2d9d6e0da1b9521959663973c98c +Subproject commit bee475b934d48139e18cd4debf0c3f7196305fcb From 9c3e8f7e4890729032b2765104ad62339abe16ce Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 29 Nov 2018 01:54:09 +0300 Subject: [PATCH 113/205] buildinfo: remove XASH_RELEASE --- engine/common/build.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/engine/common/build.c b/engine/common/build.c index b255898d..c1b1ec40 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -133,8 +133,6 @@ const char *Q_buildcommit( void ) { #ifdef XASH_BUILD_COMMIT return XASH_BUILD_COMMIT; -#elif defined(XASH_RELEASE) // don't check it elsewhere to avoid random bugs - return "release"; #else return "notset"; #endif From 3fe737058fb1e8e0f12e279a9e9a4dbeeb56772e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 29 Nov 2018 01:54:25 +0300 Subject: [PATCH 114/205] buildinfo: add host_ver cvar --- engine/common/host.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/host.c b/engine/common/host.c index fd1e1cc4..92e1c539 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -953,6 +953,8 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa ver = Cvar_Get( "ver", va( "%i/%s (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum()), FCVAR_READ_ONLY, "shows an engine version" ); + Cvar_Get( "host_ver", va( "%i %s %s %s %s", Q_buildnum(), XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildcommit() ), FCVAR_READ_ONLY, "detailed info about this build" ); + Mod_Init(); NET_Init(); NET_InitMasters(); From a1edbaced42c4a28be048eeaf159300ccc3dd7c9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 29 Nov 2018 01:54:45 +0300 Subject: [PATCH 115/205] mainui: update submodule --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index bee475b9..4e061f6e 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit bee475b934d48139e18cd4debf0c3f7196305fcb +Subproject commit 4e061f6eb58884ca04b75aac2b0016cbe67132f5 From 05eab0b5b0809a8c9a31bc09cc9bc2abb73676f4 Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 29 Nov 2018 19:48:16 +0300 Subject: [PATCH 116/205] system: fix win32 build --- engine/common/system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/system.c b/engine/common/system.c index 40c2d41d..d215103b 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -52,7 +52,7 @@ double GAME_EXPORT Sys_DoubleTime( void ) qboolean Sys_DebuggerPresent(); // see sys_linux.c #ifdef _MSC_VER #define DEBUG_BREAK \ - if( Sys_IsDebuggerPresent() ) \ + if( Sys_DebuggerPresent() ) \ _asm{ int 3 } #elif __i386__ #define DEBUG_BREAK \ From 137836348b53e77cb9ff6157c2c994fdb1deca1d Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 29 Nov 2018 20:24:19 +0300 Subject: [PATCH 117/205] client: fix GetCenterWindow engine call --- engine/client/cl_game.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 49d6331e..a1041a43 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1949,7 +1949,21 @@ GetWindowCenterX */ static int pfnGetWindowCenterX( void ) { - return host.window_center_x; + int x = 0; +#ifdef _WIN32 + if( m_ignore->value ) + { + POINT pos; + GetCursorPos( &pos ); + return pos.x; + } +#endif + +#ifdef XASH_SDL + SDL_GetWindowPosition( host.hWnd, &x, NULL ); +#endif + + return host.window_center_x + x; } /* @@ -1960,7 +1974,21 @@ GetWindowCenterY */ static int pfnGetWindowCenterY( void ) { - return host.window_center_y; + int y = 0; +#ifdef _WIN32 + if( m_ignore->value ) + { + POINT pos; + GetCursorPos( &pos ); + return pos.y; + } +#endif + +#ifdef XASH_SDL + SDL_GetWindowPosition( host.hWnd, NULL, &y ); +#endif + + return host.window_center_y + y; } /* From 3046c9e68e4b409f64b4b7b5eb8baecc2ee16473 Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 29 Nov 2018 20:24:39 +0300 Subject: [PATCH 118/205] gitignore: update --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e56161d8..82e292aa 100644 --- a/.gitignore +++ b/.gitignore @@ -315,4 +315,6 @@ build-* # Waf build_current .waf-* +waf*/ .lock-waf* +*.lastbuildstate From f173ce11f7eb4192694ae5055cabfd1c09bf7954 Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 29 Nov 2018 20:43:53 +0300 Subject: [PATCH 119/205] console: fix version drawn all the time which engine runs --- engine/client/console.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/client/console.c b/engine/client/console.c index 4fd5acee..7e8c9656 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2191,6 +2191,9 @@ void Con_DrawVersion( void ) return; } + if( host.force_draw_version_time > host.realtime ) + host.force_draw_version = false; + if( host.force_draw_version || draw_version ) Q_snprintf( curbuild, MAX_STRING, "%s v%i/%s (build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); From 0964fb11e45d99f5d840d455933a93f578cb890a Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 29 Nov 2018 21:43:51 +0300 Subject: [PATCH 120/205] sdl: partially fix maximizing window on Windows --- engine/platform/sdl/events.c | 34 ++++++++++++++-------------------- engine/platform/sdl/events.h | 1 - engine/platform/sdl/vid_sdl.c | 26 +++++++------------------- mainui | 2 +- 4 files changed, 22 insertions(+), 41 deletions(-) diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 421b8664..ee8523b0 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -380,9 +380,9 @@ static void SDLash_EventFilter( SDL_Event *event ) if( event->window.windowID != SDL_GetWindowID( host.hWnd ) ) return; - if( ( host.status == HOST_SHUTDOWN ) || - ( Host_IsDedicated() ) ) + if( host.status == HOST_SHUTDOWN || Host_IsDedicated() ) break; // no need to activate + switch( event->window.event ) { case SDL_WINDOWEVENT_MOVED: @@ -392,10 +392,14 @@ static void SDLash_EventFilter( SDL_Event *event ) Cvar_SetValue( "_window_ypos", (float)event->window.data1 ); } break; + case SDL_WINDOWEVENT_MINIMIZED: + host.status = HOST_SLEEP; + VID_RestoreScreenResolution( ); + break; case SDL_WINDOWEVENT_RESTORED: host.status = HOST_FRAME; host.force_draw_version = true; - host.force_draw_version_time = host.realtime + 2; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; if( vid_fullscreen->value ) VID_SetMode(); break; @@ -407,16 +411,11 @@ static void SDLash_EventFilter( SDL_Event *event ) S_Activate( true ); } host.force_draw_version = true; - host.force_draw_version_time = host.realtime + 2; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; if( vid_fullscreen->value ) VID_SetMode(); break; - case SDL_WINDOWEVENT_MINIMIZED: - host.status = HOST_SLEEP; - VID_RestoreScreenResolution(); - break; case SDL_WINDOWEVENT_FOCUS_LOST: - #if TARGET_OS_IPHONE { // Keep running if ftp server enabled @@ -431,24 +430,19 @@ static void SDLash_EventFilter( SDL_Event *event ) S_Activate( false ); } host.force_draw_version = true; - host.force_draw_version_time = host.realtime + 1; + host.force_draw_version_time = host.realtime + 2; VID_RestoreScreenResolution(); break; - case SDL_WINDOWEVENT_CLOSE: - Sys_Quit(); - break; case SDL_WINDOWEVENT_RESIZED: - if( vid_fullscreen->value ) break; - R_ChangeDisplaySettingsFast( event->window.data1, - event->window.data2 ); - break; case SDL_WINDOWEVENT_MAXIMIZED: { - int w, h; - if( vid_fullscreen->value ) break; + int w = VID_MIN_WIDTH, h = VID_MIN_HEIGHT; + if( vid_fullscreen->value ) + break; SDL_GL_GetDrawableSize( host.hWnd, &w, &h ); - R_ChangeDisplaySettingsFast( w, h ); + R_SaveVideoMode( w, h ); + SCR_VidInit(); // tell the client.dll what vid_mode has changed break; } default: diff --git a/engine/platform/sdl/events.h b/engine/platform/sdl/events.h index a1ab29b2..d5102331 100644 --- a/engine/platform/sdl/events.h +++ b/engine/platform/sdl/events.h @@ -22,7 +22,6 @@ GNU General Public License for more details. // window management void VID_RestoreScreenResolution( void ); -void R_ChangeDisplaySettingsFast( int width, int height ); // for fast resizing qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ); void VID_DestroyWindow( void ); void GL_InitExtensions( void ); diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 052db635..370da1c7 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -517,7 +517,7 @@ qboolean VID_SetScreenResolution( int width, int height ) SDL_GL_GetDrawableSize( host.hWnd, &got.w, &got.h ); - R_ChangeDisplaySettingsFast( got.w, got.h ); + R_SaveVideoMode( got.w, got.h ); return true; } @@ -571,8 +571,10 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !fullscreen ) { wndFlags |= SDL_WINDOW_RESIZABLE; - xpos = max( 0, Cvar_VariableInteger( "_window_xpos" ) ); - ypos = max( 0, Cvar_VariableInteger( "_window_ypos" ) ); + xpos = Cvar_VariableInteger( "_window_xpos" ); + ypos = Cvar_VariableInteger( "_window_ypos" ); + if( xpos < 0 ) xpos = SDL_WINDOWPOS_CENTERED; + if( ypos < 0 ) ypos = SDL_WINDOWPOS_CENTERED; } else { @@ -675,7 +677,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) return false; SDL_GL_GetDrawableSize( host.hWnd, &width, &height ); - R_ChangeDisplaySettingsFast( width, height ); + R_SaveVideoMode( width, height ); return true; } @@ -1126,20 +1128,6 @@ void GL_InitExtensions( void ) glw_state.initialized = true; } -/* -================== -R_ChangeDisplaySettingsFast - -Change window size fastly to custom values, without setting vid mode -================== -*/ -void R_ChangeDisplaySettingsFast( int width, int height ) -{ - R_SaveVideoMode( width, height ); - - SCR_VidInit(); -} - rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) { SDL_DisplayMode displayMode; @@ -1177,7 +1165,7 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) SDL_SetWindowBordered( host.hWnd, true ); SDL_SetWindowSize( host.hWnd, width, height ); SDL_GL_GetDrawableSize( host.hWnd, &width, &height ); - R_ChangeDisplaySettingsFast( width, height ); + R_SaveVideoMode( width, height ); } return rserr_ok; diff --git a/mainui b/mainui index 4e061f6e..7af08ccb 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 4e061f6eb58884ca04b75aac2b0016cbe67132f5 +Subproject commit 7af08ccb66b5a74ec6cab298b63f4b829585fd12 From 7ad7af76dd8f254b2698936e4ad89143c93e0cff Mon Sep 17 00:00:00 2001 From: a1batross Date: Fri, 30 Nov 2018 22:56:20 +0300 Subject: [PATCH 121/205] render: register r_showtree cvar --- engine/client/vid_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index e9531d53..357f04a2 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -439,6 +439,7 @@ void GL_InitCommands( void ) r_speeds = Cvar_Get( "r_speeds", "0", FCVAR_ARCHIVE, "shows renderer speeds" ); r_fullbright = Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); r_norefresh = Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); + r_showtree = Cvar_Get( "r_showtree", "0", FCVAR_ARCHIVE, "build the graph of visible BSP tree" ); r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", FCVAR_ARCHIVE, "allow to get lighting from world and bmodels" ); r_lighting_modulate = Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "lightstyles modulate scale" ); r_lighting_ambient = Cvar_Get( "r_lighting_ambient", "0.3", FCVAR_ARCHIVE, "map ambient lighting scale" ); From aed53c4fda785ed82b97d5fa61fbc955acc3b2c4 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 5 Dec 2018 23:56:41 +0700 Subject: [PATCH 122/205] Do not spam in console if vsync not availiable --- engine/platform/sdl/vid_sdl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 370da1c7..50081756 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -400,8 +400,7 @@ void GL_UpdateSwapInterval( void ) // disable VSync while level is loading if( cls.state < ca_active ) { - if( SDL_GL_SetSwapInterval( gl_vsync->value ) ) - Con_Reportf( S_ERROR "SDL_GL_SetSwapInterval: %s\n", SDL_GetError( ) ); + SDL_GL_SetSwapInterval( gl_vsync->value ); SetBits( gl_vsync->flags, FCVAR_CHANGED ); } else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED )) From 298393b653d8a60e20291cbd4c9ae1a699644c24 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 5 Dec 2018 23:57:05 +0700 Subject: [PATCH 123/205] Initial amd64 port --- engine/client/cl_demo.c | 8 +- engine/client/cl_gameui.c | 2 +- engine/client/cl_remap.c | 12 +- engine/client/cl_video.c | 4 +- engine/client/client.h | 6 +- engine/client/gl_studio.c | 3 +- engine/client/mod_dbghulls.c | 6 +- engine/client/s_stream.c | 4 +- engine/client/s_vox.c | 4 +- engine/client/titles.c | 8 +- engine/common/common.c | 22 +-- engine/common/common.h | 37 ++-- engine/common/custom.c | 2 +- engine/common/filesystem.c | 68 ++++---- engine/common/host.c | 2 +- engine/common/imagelib/img_bmp.c | 24 +-- engine/common/imagelib/img_main.c | 5 +- engine/common/imagelib/img_wad.c | 19 ++- engine/common/mathlib.h | 6 +- engine/common/mod_bmodel.c | 2 +- engine/common/mod_studio.c | 4 +- engine/common/net_buffer.c | 22 +-- engine/common/net_buffer.h | 6 +- engine/common/net_ws.c | 2 +- engine/common/protocol.h | 10 +- engine/common/soundlib/snd_main.c | 10 +- engine/common/soundlib/snd_mp3.c | 18 +- engine/common/soundlib/snd_wav.c | 8 +- engine/common/soundlib/soundlib.h | 28 ++-- engine/eiface.h | 2 +- engine/server/server.h | 6 +- engine/server/sv_client.c | 2 +- engine/server/sv_game.c | 270 +++++++++++++++++++++++++++--- engine/server/sv_init.c | 5 +- engine/server/sv_save.c | 2 +- 35 files changed, 443 insertions(+), 196 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 962e99c2..5c069fef 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -349,9 +349,9 @@ Write demo header */ void CL_WriteDemoHeader( const char *name ) { - long copysize; - long savepos; - long curpos; + int copysize; + int savepos; + int curpos; Con_Printf( "recording to %s.\n", name ); cls.demofile = FS_Open( name, "wb", false ); @@ -489,7 +489,7 @@ void CL_DrawDemoRecording( void ) { char string[64]; rgba_t color = { 255, 255, 255, 255 }; - long pos; + int pos; int len; if(!( host_developer.value && cls.demorecording )) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 5887c341..eaf9a890 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -376,7 +376,7 @@ pfnPIC_Load ========= */ -static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long image_size, long flags ) +static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, int image_size, int flags ) { HIMAGE tx; diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c index dfa7af55..637dc4ed 100644 --- a/engine/client/cl_remap.c +++ b/engine/client/cl_remap.c @@ -78,7 +78,9 @@ byte *CL_CreateRawTextureFromPixels( texture_t *tx, size_t *size, int topcolor, // fill header if( !pin.name[0] ) Q_strncpy( pin.name, "#raw_remap_image.mdl", sizeof( pin.name )); pin.flags = STUDIO_NF_COLORMAP; // just in case :-) - pin.index = (int)(tx + 1); // pointer to pixels + //pin.index = (int)(tx + 1); // pointer to pixels + // no more pointer-to-int-to-pointer casts + Image_SetMDLPointer( (byte*)((texture_t *)tx + 1) ); pin.width = tx->width; pin.height = tx->height; @@ -102,7 +104,8 @@ void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomco gl_texture_t *glt; texture_t *tx = NULL; char texname[128]; - int i, size, index; + int i, index; + size_t size; byte paletteBackup[768]; byte *raw, *pal; @@ -145,7 +148,8 @@ void CL_UpdateStudioTexture( mstudiotexture_t *ptexture, int topcolor, int botto rgbdata_t *pic; texture_t *tx = NULL; char texname[128], name[128], mdlname[128]; - int i, size, index; + int i, index; + size_t size; byte paletteBackup[768]; byte *raw, *pal; @@ -430,4 +434,4 @@ void CL_ClearAllRemaps( void ) Mem_Free( clgame.remap_info ); } clgame.remap_info = NULL; -} \ No newline at end of file +} diff --git a/engine/client/cl_video.c b/engine/client/cl_video.c index 58de74b2..d386ac20 100644 --- a/engine/client/cl_video.c +++ b/engine/client/cl_video.c @@ -242,7 +242,7 @@ qboolean SCR_PlayCinematic( const char *arg ) return true; } -long SCR_GetAudioChunk( char *rawdata, long length ) +int SCR_GetAudioChunk( char *rawdata, int length ) { int r; @@ -307,4 +307,4 @@ void SCR_FreeCinematic( void ) AVI_CloseVideo( cin_state ); AVI_Shutdown(); -} \ No newline at end of file +} diff --git a/engine/client/client.h b/engine/client/client.h index 91ad215c..18fcd300 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -548,8 +548,8 @@ typedef struct ui_globalvars_t *globals; qboolean drawLogo; // set to TRUE if logo.avi missed or corrupted - long logo_xres; - long logo_yres; + int logo_xres; + int logo_yres; float logo_length; qboolean use_text_api; @@ -1058,7 +1058,7 @@ void Con_LoadHistory( void ); // void S_StreamRawSamples( int samples, int rate, int width, int channels, const byte *data ); void S_StreamAviSamples( void *Avi, int entnum, float fvol, float attn, float synctime ); -void S_StartBackgroundTrack( const char *intro, const char *loop, long position, qboolean fullpath ); +void S_StartBackgroundTrack( const char *intro, const char *loop, int position, qboolean fullpath ); void S_StopBackgroundTrack( void ); void S_StreamSetPause( int pause ); void S_StartStreaming( void ); diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 40ec0926..ed0ceff4 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -3560,7 +3560,8 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture SetBits( flags, TF_NOMIPMAP ); // NOTE: replace index with pointer to start of imagebuffer, ImageLib expected it - ptexture->index = (int)((byte *)phdr) + ptexture->index; + //ptexture->index = (int)((byte *)phdr) + ptexture->index; + Image_SetMDLPointer((byte *)phdr + ptexture->index); size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768; if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ) && FBitSet( ptexture->flags, STUDIO_NF_MASKED )) diff --git a/engine/client/mod_dbghulls.c b/engine/client/mod_dbghulls.c index d2226d0f..edf27a31 100644 --- a/engine/client/mod_dbghulls.c +++ b/engine/client/mod_dbghulls.c @@ -733,7 +733,7 @@ void R_DrawWorldHull( void ) list_for_each_entry( poly, &hull->polys, chain ) { - srand((unsigned long)poly); + srand((unsigned int)poly); pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); pglBegin( GL_POLYGON ); for( i = 0; i < poly->numpoints; i++ ) @@ -766,7 +766,7 @@ void R_DrawModelHull( void ) pglDisable( GL_TEXTURE_2D ); list_for_each_entry( poly, &hull->polys, chain ) { - srand((unsigned long)poly); + srand((unsigned int)poly); pglColor3f( rand() % 256 / 255.0, rand() % 256 / 255.0, rand() % 256 / 255.0 ); pglBegin( GL_POLYGON ); for( i = 0; i < poly->numpoints; i++ ) @@ -775,4 +775,4 @@ void R_DrawModelHull( void ) } pglEnable( GL_TEXTURE_2D ); pglDisable( GL_POLYGON_OFFSET_FILL ); -} \ No newline at end of file +} diff --git a/engine/client/s_stream.c b/engine/client/s_stream.c index bc7a8acc..c912548a 100644 --- a/engine/client/s_stream.c +++ b/engine/client/s_stream.c @@ -71,7 +71,7 @@ float S_GetMusicVolume( void ) S_StartBackgroundTrack ================= */ -void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, long position, qboolean fullpath ) +void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, int position, qboolean fullpath ) { S_StopBackgroundTrack(); @@ -341,4 +341,4 @@ void S_StreamSoundTrack( void ) } else break; // no more samples for this frame } -} \ No newline at end of file +} diff --git a/engine/client/s_vox.c b/engine/client/s_vox.c index bee8ae90..39b0e1d7 100644 --- a/engine/client/s_vox.c +++ b/engine/client/s_vox.c @@ -606,7 +606,7 @@ void VOX_ReadSentenceFile( const char *psentenceFileName ) { char c, *pch, *pFileData; char *pchlast, *pSentenceData; - int fileSize; + size_t fileSize; // load file pFileData = (char *)FS_LoadFile( psentenceFileName, &fileSize, false ); @@ -684,4 +684,4 @@ void VOX_Init( void ) void VOX_Shutdown( void ) { g_numSentences = 0; -} \ No newline at end of file +} diff --git a/engine/client/titles.c b/engine/client/titles.c index 23b55fcb..a2b4ccbc 100644 --- a/engine/client/titles.c +++ b/engine/client/titles.c @@ -325,16 +325,18 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) // copy Name heap pNameHeap = ((char *)clgame.titles) + messageSize; memcpy( pNameHeap, nameHeap, nameHeapSize ); - nameOffset = pNameHeap - clgame.titles[0].pName; + //nameOffset = pNameHeap - clgame.titles[0].pName; //undefined on amd64 + // copy text & fixup pointers pCurrentText = pNameHeap + nameHeapSize; for( i = 0; i < messageCount; i++ ) { - clgame.titles[i].pName += nameOffset; // adjust name pointer (parallel buffer) + clgame.titles[i].pName = pNameHeap; // adjust name pointer (parallel buffer) Q_strcpy( pCurrentText, clgame.titles[i].pMessage ); // copy text over clgame.titles[i].pMessage = pCurrentText; + pNameHeap += Q_strlen( pNameHeap ) + 1; pCurrentText += Q_strlen( pCurrentText ) + 1; } @@ -342,4 +344,4 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) Con_DPrintf( S_ERROR "TextMessage: overflow text message buffer!\n" ); clgame.numTitles = messageCount; -} \ No newline at end of file +} diff --git a/engine/common/common.c b/engine/common/common.c index b5f06d40..286bccb0 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -45,7 +45,7 @@ void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, } #endif // DEBUG -static long idum = 0; +static int idum = 0; #define MAX_RANDOM_RANGE 0x7FFFFFFFUL #define IA 16807 @@ -58,12 +58,12 @@ static long idum = 0; #define AM (1.0 / IM) #define RNMX (1.0 - EPS) -static long lran1( void ) +static int lran1( void ) { - static long iy = 0; - static long iv[NTAB]; + static int iy = 0; + static int iv[NTAB]; int j; - long k; + int k; if( idum <= 0 || !iy ) { @@ -100,7 +100,7 @@ static float fran1( void ) return temp; } -void COM_SetRandomSeed( long lSeed ) +void COM_SetRandomSeed( int lSeed ) { if( lSeed ) idum = lSeed; else idum = -time( NULL ); @@ -1009,7 +1009,7 @@ byte* COM_LoadFileForMe( const char *filename, int *pLength ) { string name; byte *file, *pfile; - long iLength; + size_t iLength; if( !COM_CheckString( filename )) { @@ -1052,11 +1052,11 @@ byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ) /* ============= -COM_LoadFile +COM_SaveFile ============= */ -int COM_SaveFile( const char *filename, const void *data, long len ) +int COM_SaveFile( const char *filename, const void *data, int len ) { // check for empty filename if( !COM_CheckString( filename )) @@ -1222,8 +1222,8 @@ int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCom if( filename1 && filename2 ) { - long ft1 = FS_FileTime( filename1, false ); - long ft2 = FS_FileTime( filename2, false ); + int ft1 = FS_FileTime( filename1, false ); + int ft2 = FS_FileTime( filename2, false ); // one of files is missing if( ft1 == -1 || ft2 == -1 ) diff --git a/engine/common/common.h b/engine/common/common.h index 32042092..1fcf8737 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -545,9 +545,9 @@ const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); const char *COM_FileWithoutPath( const char *in ); byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type ); void W_Close( wfile_t *wad ); -byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); -byte *FS_LoadDirectFile( const char *path, long *filesizeptr ); -qboolean FS_WriteFile( const char *filename, const void *data, long len ); +byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); +byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ); +qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ); qboolean COM_ParseVector( char **pfile, float *v, size_t size ); void COM_NormalizeAngles( vec3_t angles ); int COM_FileSize( const char *filename ); @@ -557,14 +557,14 @@ int COM_CheckString( const char *string ); int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare ); search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ); file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ); -long FS_Write( file_t *file, const void *data, size_t datasize ); -long FS_Read( file_t *file, void *buffer, size_t buffersize ); +fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize ); +fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ); int FS_VPrintf( file_t *file, const char *format, va_list ap ); -int FS_Seek( file_t *file, long offset, int whence ); +int FS_Seek( file_t *file, fs_offset_t offset, int whence ); int FS_Gets( file_t *file, byte *string, size_t bufsize ); int FS_Printf( file_t *file, const char *format, ... ) _format( 2 ); -long FS_FileSize( const char *filename, qboolean gamedironly ); -long FS_FileTime( const char *filename, qboolean gamedironly ); +fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly ); +int FS_FileTime( const char *filename, qboolean gamedironly ); int FS_Print( file_t *file, const char *msg ); qboolean FS_Rename( const char *oldname, const char *newname ); int FS_FileExists( const char *filename, int gamedironly ); @@ -573,11 +573,11 @@ qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ); qboolean FS_Delete( const char *path ); int FS_UnGetc( file_t *file, byte c ); void COM_StripExtension( char *path ); -long FS_Tell( file_t *file ); +fs_offset_t FS_Tell( file_t *file ); qboolean FS_Eof( file_t *file ); int FS_Close( file_t *file ); int FS_Getc( file_t *file ); -long FS_FileLength( file_t *f ); +fs_offset_t FS_FileLength( file_t *f ); /* ======================================================================== @@ -704,6 +704,7 @@ void Image_PaletteTranslate( byte *palSrc, int top, int bottom, int pal_size ); void Image_SetForceFlags( uint flags ); // set image force flags on loading size_t Image_DXTGetLinearSize( int type, int width, int height, int depth ); void Image_ClearForceFlags( void ); +void Image_SetMDLPointer( byte *p ); /* ======================================================================== @@ -763,9 +764,9 @@ wavdata_t *FS_LoadSound( const char *filename, const byte *buffer, size_t size ) void FS_FreeSound( wavdata_t *pack ); stream_t *FS_OpenStream( const char *filename ); wavdata_t *FS_StreamInfo( stream_t *stream ); -long FS_ReadStream( stream_t *stream, int bytes, void *buffer ); -long FS_SetStreamPos( stream_t *stream, long newpos ); -long FS_GetStreamPos( stream_t *stream ); +int FS_ReadStream( stream_t *stream, int bytes, void *buffer ); +int FS_SetStreamPos( stream_t *stream, int newpos ); +int FS_GetStreamPos( stream_t *stream ); void FS_FreeStream( stream_t *stream ); qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ); uint Sound_GetApproxWavePlayLen( const char *filepath ); @@ -784,7 +785,7 @@ const char *Q_buildcommit( void ); // qboolean Host_IsQuakeCompatible( void ); void EXPORT Host_Shutdown( void ); -int Host_CompareFileTime( long ft1, long ft2 ); +int Host_CompareFileTime( int ft1, int ft2 ); void Host_NewInstance( const char *name, const char *finalmsg ); void Host_EndGame( qboolean abort, const char *message, ... ) _format( 2 ); void Host_AbortCurrentFrame( void ); @@ -842,7 +843,7 @@ cvar_t *pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, cvar_t *pfnCvar_RegisterGameUIVariable( const char *szName, const char *szValue, int flags ); char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize ); void COM_HexConvert( const char *pszInput, int nInputLength, byte *pOutput ); -int COM_SaveFile( const char *filename, const void *data, long len ); +int COM_SaveFile( const char *filename, const void *data, int len ); byte* COM_LoadFileForMe( const char *filename, int *pLength ); qboolean COM_IsSafeFileToDownload( const char *filename ); cvar_t *pfnCVarGetPointer( const char *szVarName ); @@ -986,7 +987,7 @@ struct cmdalias_s *Cmd_AliasGetList( void ); char *Cmd_GetName( struct cmd_s *cmd ); struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); -void SV_StartMusic( const char *curtrack, const char *looptrack, long position ); +void SV_StartMusic( const char *curtrack, const char *looptrack, int position ); void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale ); void Log_Printf( const char *fmt, ... ) _format( 1 ); struct sizebuf_s *SV_GetReliableDatagram( void ); @@ -1043,7 +1044,7 @@ void SCR_Init( void ); void SCR_UpdateScreen( void ); void SCR_BeginLoadingPlaque( qboolean is_background ); void SCR_CheckStartupVids( void ); -long SCR_GetAudioChunk( char *rawdata, long length ); +int SCR_GetAudioChunk( char *rawdata, int length ); wavdata_t *SCR_GetMovieInfo( void ); void SCR_Shutdown( void ); void Con_Print( const char *txt ); @@ -1061,7 +1062,7 @@ void Info_WriteVars( file_t *f ); void Info_Print( const char *s ); void Cmd_WriteVariables( file_t *f ); int Cmd_CheckMapsList( int fRefresh ); -void COM_SetRandomSeed( long lSeed ); +void COM_SetRandomSeed( int lSeed ); int COM_RandomLong( int lMin, int lMax ); float COM_RandomFloat( float fMin, float fMax ); qboolean LZSS_IsCompressed( const byte *source ); diff --git a/engine/common/custom.c b/engine/common/custom.c index f624b0c6..a379b38b 100644 --- a/engine/common/custom.c +++ b/engine/common/custom.c @@ -63,7 +63,7 @@ void COM_ClearCustomizationList( customization_t *pHead, qboolean bCleanDecals ) qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResource, int playernumber, int flags, customization_t **pOut, int *nLumps ) { qboolean bError = false; - long checksize = 0; + size_t checksize = 0; customization_t *pCust; if( pOut ) *pOut = NULL; diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 7cb184dc..a3b7e772 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -68,13 +68,13 @@ typedef struct wadtype_s typedef struct file_s { int handle; // file descriptor - long real_length; // uncompressed file size (for files opened in "read" mode) - long position; // current position in the file - long offset; // offset into the package (0 if external file) + fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode) + fs_offset_t position; // current position in the file + fs_offset_t offset; // offset into the package (0 if external file) int ungetc; // single stored character from ungetc, cleared to EOF when read time_t filetime; // pak, wad or real filetime // contents buffer - long buff_ind, buff_len; // buffer current index and length + fs_offset_t buff_ind, buff_len; // buffer current index and length byte buff[FILE_BUFF_SIZE]; // intermediate buffer } file_t; @@ -123,11 +123,11 @@ qboolean fs_caseinsensitive = true; // try to search missing files static void FS_InitMemory( void ); static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype ); -static dpackfile_t *FS_AddFileToPack( const char* name, pack_t *pack, long offset, long size ); -static byte *W_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); +static dpackfile_t *FS_AddFileToPack( const char* name, pack_t *pack, fs_offset_t offset, fs_offset_t size ); +static byte *W_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); static wfile_t *W_Open( const char *filename, int *errorcode ); static qboolean FS_SysFolderExists( const char *path ); -static long FS_SysFileTime( const char *filename ); +static int FS_SysFileTime( const char *filename ); static char W_TypeFromExt( const char *lumpname ); static const char *W_ExtFromType( char lumptype ); static void FS_Purge( file_t* file ); @@ -389,7 +389,7 @@ FS_AddFileToPack Add a file to the list of files contained into a package ==================== */ -static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, long offset, long size ) +static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, fs_offset_t offset, fs_offset_t size ) { int left, right, middle; dpackfile_t *pfile; @@ -1705,7 +1705,7 @@ FS_SysFileTime Internal function used to determine filetime ==================== */ -static long FS_SysFileTime( const char *filename ) +static int FS_SysFileTime( const char *filename ) { struct stat buf; @@ -2154,9 +2154,9 @@ FS_Write Write "datasize" bytes into a file ==================== */ -long FS_Write( file_t *file, const void *data, size_t datasize ) +fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize ) { - long result; + fs_offset_t result; if( !file ) return 0; @@ -2168,7 +2168,7 @@ long FS_Write( file_t *file, const void *data, size_t datasize ) FS_Purge( file ); // write the buffer and update the position - result = write( file->handle, data, (long)datasize ); + result = write( file->handle, data, (fs_offset_t)datasize ); file->position = lseek( file->handle, 0, SEEK_CUR ); if( file->real_length < file->position ) @@ -2186,10 +2186,10 @@ FS_Read Read up to "buffersize" bytes from a file ==================== */ -long FS_Read( file_t *file, void *buffer, size_t buffersize ) +fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ) { - long count, done; - long nb; + fs_offset_t count, done; + fs_offset_t nb; // nothing to copy if( buffersize == 0 ) return 1; @@ -2209,7 +2209,7 @@ long FS_Read( file_t *file, void *buffer, size_t buffersize ) { count = file->buff_len - file->buff_ind; - done += ((long)buffersize > count ) ? count : (long)buffersize; + done += ((fs_offset_t)buffersize > count ) ? count : (fs_offset_t)buffersize; memcpy( buffer, &file->buff[file->buff_ind], done ); file->buff_ind += done; @@ -2226,8 +2226,8 @@ long FS_Read( file_t *file, void *buffer, size_t buffersize ) // if we have a lot of data to get, put them directly into "buffer" if( buffersize > sizeof( file->buff ) / 2 ) { - if( count > (long)buffersize ) - count = (long)buffersize; + if( count > (fs_offset_t)buffersize ) + count = (fs_offset_t)buffersize; lseek( file->handle, file->offset + file->position, SEEK_SET ); nb = read (file->handle, &((byte *)buffer)[done], count ); @@ -2241,8 +2241,8 @@ long FS_Read( file_t *file, void *buffer, size_t buffersize ) } else { - if( count > (long)sizeof( file->buff )) - count = (long)sizeof( file->buff ); + if( count > (fs_offset_t)sizeof( file->buff )) + count = (fs_offset_t)sizeof( file->buff ); lseek( file->handle, file->offset + file->position, SEEK_SET ); nb = read( file->handle, file->buff, count ); @@ -2252,7 +2252,7 @@ long FS_Read( file_t *file, void *buffer, size_t buffersize ) file->position += nb; // copy the requested data in "buffer" (as much as we can) - count = (long)buffersize > file->buff_len ? file->buff_len : (long)buffersize; + count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize; memcpy( &((byte *)buffer)[done], file->buff, count ); file->buff_ind = count; done += count; @@ -2303,7 +2303,7 @@ Print a string into a file int FS_VPrintf( file_t *file, const char *format, va_list ap ) { int len; - long buff_size = MAX_SYSPATH; + fs_offset_t buff_size = MAX_SYSPATH; char *tempbuff; if( !file ) return 0; @@ -2402,7 +2402,7 @@ FS_Seek Move the position index in a file ==================== */ -int FS_Seek( file_t *file, long offset, int whence ) +int FS_Seek( file_t *file, fs_offset_t offset, int whence ) { // compute the file offset switch( whence ) @@ -2446,7 +2446,7 @@ FS_Tell Give the current position in a file ==================== */ -long FS_Tell( file_t *file ) +fs_offset_t FS_Tell( file_t *file ) { if( !file ) return 0; return file->position - file->buff_len + file->buff_ind; @@ -2487,11 +2487,11 @@ Filename are relative to the xash directory. Always appends a 0 byte. ============ */ -byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ) +byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ) { file_t *file; byte *buf = NULL; - long filesize = 0; + fs_offset_t filesize = 0; file = FS_Open( path, "rb", gamedironly ); @@ -2522,11 +2522,11 @@ Filename are relative to the xash directory. Always appends a 0 byte. ============ */ -byte *FS_LoadDirectFile( const char *path, long *filesizeptr ) +byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ) { file_t *file; byte *buf = NULL; - long filesize = 0; + fs_offset_t filesize = 0; file = FS_SysOpen( path, "rb" ); @@ -2556,7 +2556,7 @@ FS_WriteFile The filename will be prefixed by the current game directory ============ */ -qboolean FS_WriteFile( const char *filename, const void *data, long len ) +qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ) { file_t *file; @@ -2724,7 +2724,7 @@ FS_FileSize return size of file in bytes ================== */ -long FS_FileSize( const char *filename, qboolean gamedironly ) +fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly ) { int length = -1; // in case file was missed file_t *fp; @@ -2749,7 +2749,7 @@ FS_FileLength return size of file in bytes ================== */ -long FS_FileLength( file_t *f ) +fs_offset_t FS_FileLength( file_t *f ) { if( !f ) return 0; return f->real_length; @@ -2762,7 +2762,7 @@ FS_FileTime return time of creation file in seconds ================== */ -long FS_FileTime( const char *filename, qboolean gamedironly ) +int FS_FileTime( const char *filename, qboolean gamedironly ) { searchpath_t *search; int pack_ind; @@ -3253,7 +3253,7 @@ W_ReadLump reading lump into temp buffer =========== */ -byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr ) +byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, fs_offset_t *lumpsizeptr ) { size_t oldpos, size = 0; byte *buf; @@ -3454,7 +3454,7 @@ W_LoadFile loading lump into the tmp buffer =========== */ -static byte *W_LoadFile( const char *path, long *lumpsizeptr, qboolean gamedironly ) +static byte *W_LoadFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly ) { searchpath_t *search; int index; diff --git a/engine/common/host.c b/engine/common/host.c index 92e1c539..0fd6dec7 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -57,7 +57,7 @@ convar_t *host_framerate; convar_t *con_gamemaps; convar_t *build, *ver; -int Host_CompareFileTime( long ft1, long ft2 ) +int Host_CompareFileTime( int ft1, int ft2 ) { if( ft1 < ft2 ) { diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index 498e215d..12415cf5 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -48,20 +48,20 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) buf_p = (byte *)buffer; bhdr.id[0] = *buf_p++; bhdr.id[1] = *buf_p++; // move pointer - bhdr.fileSize = *(long *)buf_p; buf_p += 4; - bhdr.reserved0 = *(long *)buf_p; buf_p += 4; - bhdr.bitmapDataOffset = *(long *)buf_p; buf_p += 4; - bhdr.bitmapHeaderSize = *(long *)buf_p; buf_p += 4; - bhdr.width = *(long *)buf_p; buf_p += 4; - bhdr.height = *(long *)buf_p; buf_p += 4; + bhdr.fileSize = *(int *)buf_p; buf_p += 4; + bhdr.reserved0 = *(int *)buf_p; buf_p += 4; + bhdr.bitmapDataOffset = *(int *)buf_p; buf_p += 4; + bhdr.bitmapHeaderSize = *(int *)buf_p; buf_p += 4; + bhdr.width = *(int *)buf_p; buf_p += 4; + bhdr.height = *(int *)buf_p; buf_p += 4; bhdr.planes = *(short *)buf_p; buf_p += 2; bhdr.bitsPerPixel = *(short *)buf_p; buf_p += 2; - bhdr.compression = *(long *)buf_p; buf_p += 4; - bhdr.bitmapDataSize = *(long *)buf_p; buf_p += 4; - bhdr.hRes = *(long *)buf_p; buf_p += 4; - bhdr.vRes = *(long *)buf_p; buf_p += 4; - bhdr.colors = *(long *)buf_p; buf_p += 4; - bhdr.importantColors = *(long *)buf_p; buf_p += 4; + bhdr.compression = *(int *)buf_p; buf_p += 4; + bhdr.bitmapDataSize = *(int *)buf_p; buf_p += 4; + bhdr.hRes = *(int *)buf_p; buf_p += 4; + bhdr.vRes = *(int *)buf_p; buf_p += 4; + bhdr.colors = *(int *)buf_p; buf_p += 4; + bhdr.importantColors = *(int *)buf_p; buf_p += 4; // bogus file header check if( bhdr.reserved0 != 0 ) return false; diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 57cf79d4..e9f3f6e9 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -213,7 +213,8 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) const char *ext = COM_FileExtension( filename ); string path, loadname, sidename; qboolean anyformat = true; - int i, filesize = 0; + int i; + size_t filesize = 0; const loadpixformat_t *format; const cubepack_t *cmap; byte *f; @@ -488,4 +489,4 @@ rgbdata_t *FS_CopyImage( rgbdata_t *in ) } return out; -} \ No newline at end of file +} diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 954e3c8c..f5e4eecb 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -129,6 +129,19 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize ) return Image_AddIndexedImageToPack( fin, image.width, image.height ); } +/* +====================== +Image_SetMDLPointer + +Transfer buffer pointer before Image_LoadMDL +====================== +*/ +static void *g_mdltexdata; +void Image_SetMDLPointer(byte *p) +{ + g_mdltexdata = p; +} + /* ============ Image_LoadMDL @@ -147,7 +160,9 @@ qboolean Image_LoadMDL( const char *name, const byte *buffer, size_t filesize ) image.width = pin->width; image.height = pin->height; pixels = image.width * image.height; - fin = (byte *)pin->index; // setup buffer + fin = (byte *)g_mdltexdata; + ASSERT(fin); + g_mdltexdata = NULL; if( !Image_ValidSize( name )) return false; @@ -496,4 +511,4 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) image.depth = 1; return Image_AddIndexedImageToPack( fin, image.width, image.height ); -} \ No newline at end of file +} diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 2b898780..f939dc10 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -67,8 +67,8 @@ GNU General Public License for more details. #define Q_min( a, b ) (((a) < (b)) ? (a) : (b)) #define Q_max( a, b ) (((a) > (b)) ? (a) : (b)) #define Q_recip( a ) ((float)(1.0f / (float)(a))) -#define Q_floor( a ) ((float)(long)(a)) -#define Q_ceil( a ) ((float)(long)((a) + 1)) +#define Q_floor( a ) ((float)(int)(a)) +#define Q_ceil( a ) ((float)(int)((a) + 1)) #define Q_round( x, y ) (floor( x / y + 0.5 ) * y ) #define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f))) #define IS_NAN(x) (((*(int *)&x) & (255<<23)) == (255<<23)) @@ -199,4 +199,4 @@ extern const matrix3x4 matrix3x4_identity; extern const matrix4x4 matrix4x4_identity; extern const float m_bytenormals[NUMVERTEXNORMALS][3]; -#endif//MATHLIB_H \ No newline at end of file +#endif//MATHLIB_H diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 6548ab5f..0a4a219c 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -1528,7 +1528,7 @@ static void Mod_LoadEntities( dbspmodel_t *bmod ) if( bmod->isworld ) { char entfilename[MAX_QPATH]; - long entpatchsize; + int entpatchsize; size_t ft1, ft2; // world is check for entfile too diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index d209a443..fc6d1da9 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -602,7 +602,7 @@ void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstud pseqgroup = (mstudioseqgroup_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqgroupindex) + pseqdesc->seqgroup; if( pseqdesc->seqgroup == 0 ) - return ((byte *)m_pStudioHeader + pseqgroup->data + pseqdesc->animindex); + return ((byte *)m_pStudioHeader + pseqdesc->animindex); paSequences = (cache_user_t *)m_pSubModel->submodels; @@ -914,7 +914,7 @@ void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean i pseqgroup = (mstudioseqgroup_t *)((byte *)pstudiohdr + pstudiohdr->seqgroupindex) + pseqdesc->seqgroup; if( pseqdesc->seqgroup == 0 ) - panim = (mstudioanim_t *)((byte *)pstudiohdr + pseqgroup->data + pseqdesc->animindex); + panim = (mstudioanim_t *)((byte *)pstudiohdr + pseqdesc->animindex); else continue; for( j = 0; j < pstudiohdr->numbones; j++ ) diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index 9488a2c3..b6355403 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -165,7 +165,7 @@ void MSG_WriteUBitLong( sizebuf_t *sb, uint curData, int numbits ) dword iCurBitMasked; int nBitsWritten; - Assert(( iDWord * 4 + sizeof( long )) <= (uint)MSG_GetMaxBytes( sb )); + Assert(( iDWord * 4 + sizeof( int )) <= (uint)MSG_GetMaxBytes( sb )); iCurBitMasked = iCurBit & 31; ((dword *)sb->pData)[iDWord] &= BitWriteMasks[iCurBitMasked][nBitsLeft]; @@ -304,12 +304,12 @@ void MSG_WriteVec3Angles( sizebuf_t *sb, const float *fa ) void MSG_WriteBitFloat( sizebuf_t *sb, float val ) { - long intVal; + int intVal; - Assert( sizeof( long ) == sizeof( float )); + Assert( sizeof( int ) == sizeof( float )); Assert( sizeof( float ) == 4 ); - intVal = *((long *)&val ); + intVal = *((int *)&val ); MSG_WriteUBitLong( sb, intVal, 32 ); } @@ -360,9 +360,9 @@ void MSG_WriteWord( sizebuf_t *sb, int val ) MSG_WriteUBitLong( sb, val, sizeof( word ) << 3 ); } -void MSG_WriteLong( sizebuf_t *sb, long val ) +void MSG_WriteLong( sizebuf_t *sb, int val ) { - MSG_WriteSBitLong( sb, val, sizeof( long ) << 3 ); + MSG_WriteSBitLong( sb, val, sizeof( int ) << 3 ); } void MSG_WriteDword( sizebuf_t *sb, dword val ) @@ -456,10 +456,10 @@ uint MSG_ReadUBitLong( sizebuf_t *sb, int numbits ) float MSG_ReadBitFloat( sizebuf_t *sb ) { - long val; + int val; int bit, byte; - Assert( sizeof( float ) == sizeof( long )); + Assert( sizeof( float ) == sizeof( int )); Assert( sizeof( float ) == 4 ); if( MSG_Overflow( sb, 32 )) @@ -617,9 +617,9 @@ void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa ) } -long MSG_ReadLong( sizebuf_t *sb ) +int MSG_ReadLong( sizebuf_t *sb ) { - return MSG_ReadSBitLong( sb, sizeof( long ) << 3 ); + return MSG_ReadSBitLong( sb, sizeof( int ) << 3 ); } dword MSG_ReadDword( sizebuf_t *sb ) @@ -685,4 +685,4 @@ void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove ) MSG_SeekToBit( sb, startbit, SEEK_SET ); sb->nDataBits -= bitstoremove; -} \ No newline at end of file +} diff --git a/engine/common/net_buffer.h b/engine/common/net_buffer.h index 62a5f284..4a631898 100644 --- a/engine/common/net_buffer.h +++ b/engine/common/net_buffer.h @@ -86,7 +86,7 @@ void MSG_WriteChar( sizebuf_t *sb, int val ); void MSG_WriteByte( sizebuf_t *sb, int val ); void MSG_WriteShort( sizebuf_t *sb, int val ); void MSG_WriteWord( sizebuf_t *sb, int val ); -void MSG_WriteLong( sizebuf_t *sb, long val ); +void MSG_WriteLong( sizebuf_t *sb, int val ); void MSG_WriteDword( sizebuf_t *sb, dword val ); void MSG_WriteCoord( sizebuf_t *sb, float val ); void MSG_WriteFloat( sizebuf_t *sb, float val ); @@ -123,7 +123,7 @@ int MSG_ReadChar( sizebuf_t *sb ); int MSG_ReadByte( sizebuf_t *sb ); int MSG_ReadShort( sizebuf_t *sb ); int MSG_ReadWord( sizebuf_t *sb ); -long MSG_ReadLong( sizebuf_t *sb ); +int MSG_ReadLong( sizebuf_t *sb ); dword MSG_ReadDword( sizebuf_t *sb ); float MSG_ReadCoord( sizebuf_t *sb ); float MSG_ReadFloat( sizebuf_t *sb ); @@ -132,4 +132,4 @@ void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa ); qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes ); char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine ); -#endif//NET_BUFFER_H \ No newline at end of file +#endif//NET_BUFFER_H diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 6c25019e..beeaa59e 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -258,7 +258,7 @@ typedef struct float fakelag; // cached fakelag value LONGPACKET split; int split_flags[NET_MAX_FRAGMENTS]; - long sequence_number; + int sequence_number; int ip_sockets[NS_COUNT]; qboolean initialized; qboolean threads_initialized; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 742d065e..1bff7a55 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -30,7 +30,7 @@ GNU General Public License for more details. #define svc_print 8 // [byte] id [string] null terminated string #define svc_stufftext 9 // [string] stuffed into client's console buffer #define svc_setangle 10 // [angle angle angle] set the view angle to this absolute value -#define svc_serverdata 11 // [long] protocol ... +#define svc_serverdata 11 // [int] protocol ... #define svc_lightstyle 12 // [index][pattern][float] #define svc_updateuserinfo 13 // [byte] playernum, [string] userinfo #define svc_deltatable 14 // [table header][...] @@ -77,7 +77,7 @@ GNU General Public License for more details. // reserved #define svc_resourcelocation 56 // [string] #define svc_querycvarvalue 57 // [string] -#define svc_querycvarvalue2 58 // [string][long] (context) +#define svc_querycvarvalue2 58 // [string][int] (context) #define svc_lastmsg 58 // start user messages at this point // client to server @@ -183,8 +183,8 @@ GNU General Public License for more details. #define PROTOCOL_VERSION_QUAKE 15 // listed only unmatched ops -#define svc_updatestat 3 // [byte] [long] (svc_event) -#define svc_version 4 // [long] server version (svc_changing) +#define svc_updatestat 3 // [byte] [int] (svc_event) +#define svc_version 4 // [int] server version (svc_changing) #define svc_updatename 13 // [byte] [string] (svc_updateuserinfo) #define svc_updatefrags 14 // [byte] [short] (svc_deltatable) #define svc_stopsound 16 // (svc_resource) @@ -243,4 +243,4 @@ GNU General Public License for more details. extern const char *svc_strings[svc_lastmsg+1]; extern const char *clc_strings[clc_lastmsg+1]; -#endif//NET_PROTOCOL_H \ No newline at end of file +#endif//NET_PROTOCOL_H diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index ca3bac13..66ba9568 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -59,7 +59,7 @@ wavdata_t *FS_LoadSound( const char *filename, const byte *buffer, size_t size ) const char *ext = COM_FileExtension( filename ); string path, loadname; qboolean anyformat = true; - int filesize = 0; + size_t filesize = 0; const loadwavfmt_t *format; byte *f; @@ -223,7 +223,7 @@ FS_ReadStream extract stream as wav-data and put into buffer, move file pointer ================ */ -long FS_ReadStream( stream_t *stream, int bytes, void *buffer ) +int FS_ReadStream( stream_t *stream, int bytes, void *buffer ) { if( !stream || !stream->format || !stream->format->readfunc ) return 0; @@ -241,7 +241,7 @@ FS_GetStreamPos get stream position (in bytes) ================ */ -long FS_GetStreamPos( stream_t *stream ) +int FS_GetStreamPos( stream_t *stream ) { if( !stream || !stream->format || !stream->format->getposfunc ) return -1; @@ -256,7 +256,7 @@ FS_SetStreamPos set stream position (in bytes) ================ */ -long FS_SetStreamPos( stream_t *stream, long newpos ) +int FS_SetStreamPos( stream_t *stream, int newpos ) { if( !stream || !stream->format || !stream->format->setposfunc ) return -1; @@ -277,4 +277,4 @@ void FS_FreeStream( stream_t *stream ) return; stream->format->freefunc( stream ); -} \ No newline at end of file +} diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index b98454df..d04819db 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -32,12 +32,12 @@ typedef struct } wavinfo_t; // custom stdio -typedef long (*pfread)( void *handle, void *buf, size_t count ); -typedef long (*pfseek)( void *handle, long offset, int whence ); +typedef int (*pfread)( void *handle, void *buf, size_t count ); +typedef int (*pfseek)( void *handle, int offset, int whence ); extern void *create_decoder( int *error ); -extern int feed_mpeg_header( void *mpg, const char *data, long bufsize, long streamsize, wavinfo_t *sc ); -extern int feed_mpeg_stream( void *mpg, const char *data, long bufsize, char *outbuf, size_t *outsize ); +extern int feed_mpeg_header( void *mpg, const char *data, int bufsize, int streamsize, wavinfo_t *sc ); +extern int feed_mpeg_stream( void *mpg, const char *data, int bufsize, char *outbuf, size_t *outsize ); extern int open_mpeg_stream( void *mpg, void *file, pfread f_read, pfseek f_seek, wavinfo_t *sc ); extern int read_mpeg_stream( void *mpg, char *outbuf, size_t *outsize ); extern int get_stream_pos( void *mpg ); @@ -205,7 +205,7 @@ Stream_ReadMPG assume stream is valid ================= */ -long Stream_ReadMPG( stream_t *stream, long needBytes, void *buffer ) +int Stream_ReadMPG( stream_t *stream, int needBytes, void *buffer ) { // buffer handling int bytesWritten = 0; @@ -216,7 +216,7 @@ long Stream_ReadMPG( stream_t *stream, long needBytes, void *buffer ) while( 1 ) { byte *data; - long outsize; + int outsize; if( !stream->buffsize ) { @@ -253,7 +253,7 @@ Stream_SetPosMPG assume stream is valid ================= */ -long Stream_SetPosMPG( stream_t *stream, long newpos ) +int Stream_SetPosMPG( stream_t *stream, int newpos ) { if( set_stream_pos( stream->ptr, newpos ) != -1 ) { @@ -273,7 +273,7 @@ Stream_GetPosMPG assume stream is valid ================= */ -long Stream_GetPosMPG( stream_t *stream ) +int Stream_GetPosMPG( stream_t *stream ) { return get_stream_pos( stream->ptr ); } @@ -300,4 +300,4 @@ void Stream_FreeMPG( stream_t *stream ) } Mem_Free( stream ); -} \ No newline at end of file +} diff --git a/engine/common/soundlib/snd_wav.c b/engine/common/soundlib/snd_wav.c index 80ca54b1..68484393 100644 --- a/engine/common/soundlib/snd_wav.c +++ b/engine/common/soundlib/snd_wav.c @@ -403,7 +403,7 @@ Stream_ReadWAV assume stream is valid ================= */ -long Stream_ReadWAV( stream_t *stream, long bytes, void *buffer ) +int Stream_ReadWAV( stream_t *stream, int bytes, void *buffer ) { int remaining; @@ -426,7 +426,7 @@ Stream_SetPosWAV assume stream is valid ================= */ -long Stream_SetPosWAV( stream_t *stream, long newpos ) +int Stream_SetPosWAV( stream_t *stream, int newpos ) { // NOTE: stream->pos it's real file position without header size if( FS_Seek( stream->file, stream->buffsize + newpos, SEEK_SET ) != -1 ) @@ -445,7 +445,7 @@ Stream_GetPosWAV assume stream is valid ================= */ -long Stream_GetPosWAV( stream_t *stream ) +int Stream_GetPosWAV( stream_t *stream ) { return stream->pos; } @@ -462,4 +462,4 @@ void Stream_FreeWAV( stream_t *stream ) if( stream->file ) FS_Close( stream->file ); Mem_Free( stream ); -} \ No newline at end of file +} diff --git a/engine/common/soundlib/soundlib.h b/engine/common/soundlib/soundlib.h index df384d22..f9577b66 100644 --- a/engine/common/soundlib/soundlib.h +++ b/engine/common/soundlib/soundlib.h @@ -34,9 +34,9 @@ typedef struct streamfmt_s const char *ext; stream_t *(*openfunc)( const char *filename ); - long (*readfunc)( stream_t *stream, long bytes, void *buffer ); - long (*setposfunc)( stream_t *stream, long newpos ); - long (*getposfunc)( stream_t *stream ); + int (*readfunc)( stream_t *stream, int bytes, void *buffer ); + int (*setposfunc)( stream_t *stream, int newpos ); + int (*getposfunc)( stream_t *stream ); void (*freefunc)( stream_t *stream ); } streamfmt_t; @@ -95,14 +95,14 @@ typedef struct stream_s typedef struct { int riff_id; // 'RIFF' - long rLen; + int rLen; int wave_id; // 'WAVE' int fmt_id; // 'fmt ' - long pcm_header_len; // varies... + int pcm_header_len; // varies... short wFormatTag; short nChannels; // 1,2 for stereo data is (l,r) pairs - long nSamplesPerSec; - long nAvgBytesPerSec; + int nSamplesPerSec; + int nAvgBytesPerSec; short nBlockAlign; short nBitsPerSample; } wavehdr_t; @@ -110,7 +110,7 @@ typedef struct typedef struct { int data_id; // 'data' or 'fact' - long dLen; + int dLen; } chunkhdr_t; extern sndlib_t sound; @@ -124,14 +124,14 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ); // stream operate // stream_t *Stream_OpenWAV( const char *filename ); -long Stream_ReadWAV( stream_t *stream, long bytes, void *buffer ); -long Stream_SetPosWAV( stream_t *stream, long newpos ); -long Stream_GetPosWAV( stream_t *stream ); +int Stream_ReadWAV( stream_t *stream, int bytes, void *buffer ); +int Stream_SetPosWAV( stream_t *stream, int newpos ); +int Stream_GetPosWAV( stream_t *stream ); void Stream_FreeWAV( stream_t *stream ); stream_t *Stream_OpenMPG( const char *filename ); -long Stream_ReadMPG( stream_t *stream, long bytes, void *buffer ); -long Stream_SetPosMPG( stream_t *stream, long newpos ); -long Stream_GetPosMPG( stream_t *stream ); +int Stream_ReadMPG( stream_t *stream, int bytes, void *buffer ); +int Stream_SetPosMPG( stream_t *stream, int newpos ); +int Stream_GetPosMPG( stream_t *stream ); void Stream_FreeMPG( stream_t *stream ); #endif//SOUNDLIB_H diff --git a/engine/eiface.h b/engine/eiface.h index c25093b6..39d9e946 100644 --- a/engine/eiface.h +++ b/engine/eiface.h @@ -287,7 +287,7 @@ typedef struct KeyValueData_s char *szClassName; // in: entity classname char *szKeyName; // in: name of key char *szValue; // in: value of key - long fHandled; // out: DLL sets to true if key-value pair was understood + int fHandled; // out: DLL sets to true if key-value pair was understood } KeyValueData; diff --git a/engine/server/server.h b/engine/server/server.h index b0553602..96d09cf1 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -592,6 +592,11 @@ edict_t* SV_CreateNamedEntity( edict_t *ent, string_t className ); string_t SV_AllocString( const char *szValue ); string_t SV_MakeString( const char *szValue ); const char *SV_GetString( string_t iString ); +void SV_SetStringArrayMode( qboolean dynamic ); +void SV_EmptyStringPool( void ); +#ifdef XASH_64BIT +void SV_PrintStr64Stats_f( void ); +#endif sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only ); int SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); @@ -613,7 +618,6 @@ void SV_RestartStaticEnts( void ); int pfnGetCurrentPlayer( void ); edict_t *SV_EdictNum( int n ); char *SV_Localinfo( void ); - // // sv_log.c // diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 83cf1fd9..5b4f4cd8 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2049,7 +2049,7 @@ void SV_TSourceEngineQuery( netadr_t from ) MSG_WriteString( &buf, GI->game_url ); MSG_WriteString( &buf, GI->update_url ); MSG_WriteByte( &buf, 0 ); - MSG_WriteLong( &buf, (long)GI->version ); + MSG_WriteLong( &buf, (int)GI->version ); MSG_WriteLong( &buf, GI->size ); if( GI->gamemode == 2 ) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 6d71bc7b..179cb121 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -571,7 +571,7 @@ void SV_RestartAmbientSounds( void ) soundlist_t soundInfo[256]; string curtrack, looptrack; int i, nSounds; - long position; + int position; if( !SV_Active( )) return; @@ -2152,7 +2152,7 @@ SV_StartMusic ================= */ -void SV_StartMusic( const char *curtrack, const char *looptrack, long position ) +void SV_StartMusic( const char *curtrack, const char *looptrack, int position ) { MSG_BeginServerCmd( &sv.multicast, svc_stufftext ); MSG_WriteString( &sv.multicast, va( "music \"%s\" \"%s\" %li\n", curtrack, looptrack, position )); @@ -2964,41 +2964,248 @@ void *pfnPvEntPrivateData( edict_t *pEdict ) return NULL; } + +#ifdef XASH_64BIT +static struct str64_s +{ + size_t maxstringarray; + qboolean allowdup; + char *staticstringarray; + char *pstringarray; + char *pstringarraystatic; + char *pstringbase; + char *poldstringbase; + char *plast; + qboolean dynamic; + size_t maxalloc; + size_t numdups; + size_t numoverflows; + size_t totalalloc; +} str64; +#endif + +/* +================== +SV_EmptyStringPool + +Free strings on server stop. Reset string pointer on 64 bits +================== +*/ +void SV_EmptyStringPool( void ) +{ +#ifdef XASH_64BIT + if( str64.dynamic ) // switch only after array fill (more space for multiplayer games) + str64.pstringbase = str64.pstringarray; + else + { + str64.pstringbase = str64.poldstringbase = str64.pstringarraystatic; + str64.plast = str64.pstringbase + 1; + } +#else + Mem_EmptyPool( svgame.stringspool ); +#endif +} + +/* +=============== +SV_SetStringArrayMode + +use different arrays on 64 bit platforms +set dynamic after complete server spawn +this helps not to lose strings that belongs to static game part +=============== +*/ +void SV_SetStringArrayMode( qboolean dynamic ) +{ +#ifdef XASH_64BIT + Con_Reportf( "SV_SetStringArrayMode(%d) %d\n", dynamic, str64.dynamic ); + + if( dynamic == str64.dynamic ) + return; + + str64.dynamic = dynamic; + + SV_EmptyStringPool(); +#endif +} + +#ifdef XASH_64BIT +#ifndef _WIN32 +#define USE_MMAP +#include +#endif +#endif + +/* +================== +SV_AllocStringPool + +alloc string pool on 32bit platforms +alloc string array near the server library on 64bit platforms if possible +alloc string array somewhere if not (MAKE_STRING will not work. Always call ALLOC_STRING instead, or crash) +this case need patched game dll with MAKE_STRING checking ptrdiff size +================== +*/ +void SV_AllocStringPool( void ) +{ +#ifdef XASH_64BIT + void *ptr = NULL; + string lenstr; + + Con_Reportf( "SV_AllocStringPool()\n" ); + if( Sys_GetParmFromCmdLine( "-str64alloc", lenstr ) ) + { + str64.maxstringarray = Q_atoi( lenstr ); + if( str64.maxstringarray < 1024 || str64.maxstringarray >= INT_MAX ) + str64.maxstringarray = 65536; + } + else str64.maxstringarray = 65536; + if( Sys_CheckParm( "-str64dup" ) ) + str64.allowdup = true; + +#ifdef USE_MMAP + { + size_t pagesize = sysconf( _SC_PAGESIZE ); + int arrlen = (str64.maxstringarray * 2) & ~(pagesize - 1); + void *base = svgame.dllFuncs.pfnGameInit; + void *start = svgame.hInstance - arrlen; + + while( start - base > INT_MIN ) + { + void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0 ); + if( mapptr && mapptr != (void*)-1 && mapptr - base > INT_MIN && mapptr - base < INT_MAX ) + { + ptr = mapptr; + break; + } + if( mapptr ) munmap( mapptr, arrlen ); + start -= arrlen; + } + + if( !ptr ) + { + start = base; + while( start - base < INT_MAX ) + { + void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0 ); + if( mapptr && mapptr != (void*)-1 && mapptr - base > INT_MIN && mapptr - base < INT_MAX ) + { + ptr = mapptr; + break; + } + if( mapptr ) munmap( mapptr, arrlen ); + start += arrlen; + } + } + + + if( ptr ) + { + Con_Reportf( "SV_AllocStringPool: Allocated string array near the server library: %p %p\n", base, ptr ); + + } + else + { + Con_Reportf( "SV_AllocStringPool: Failed to allocate string array near the server library!\n" ); + ptr = str64.staticstringarray = Mem_Calloc(host.mempool, str64.maxstringarray * 2); + } + } +#else + ptr = str64.staticstringarray = Mem_Calloc(host.mempool, str64.maxstringarray * 2); +#endif + + str64.pstringarray = ptr; + str64.pstringarraystatic = ptr + str64.maxstringarray; + str64.pstringbase = str64.poldstringbase = ptr; + str64.plast = ptr + 1; + svgame.globals->pStringBase = ptr; +#else + svgame.stringspool = Mem_AllocPool( "Server Strings" ); + svgame.globals->pStringBase = ""; +#endif +} + +void SV_FreeStringPool( void ) +{ +#ifdef XASH_64BIT + Con_Reportf( "SV_FreeStringPool()\n" ); + + if( str64.pstringarray != str64.staticstringarray ) + munmap( str64.pstringarray, (str64.maxstringarray * 2) & ~(sysconf( _SC_PAGESIZE ) - 1) ); + else + Mem_Free( str64.staticstringarray ); +#else + Mem_FreePool( &svgame.stringspool ); +#endif +} + /* ============= SV_AllocString allocate new engine string +on 64bit platforms find in array string if deduplication enabled (default) +if not found, add to array +use -str64dup to disable deduplication, -str64alloc to set array size ============= */ -string_t SV_AllocString( const char *szString ) +string_t GAME_EXPORT SV_AllocString( const char *szValue ) { - char *out, *out_p; - int i, l; + const char *newString = NULL; if( svgame.physFuncs.pfnAllocString != NULL ) - return svgame.physFuncs.pfnAllocString( szString ); + return svgame.physFuncs.pfnAllocString( szValue ); +#ifdef XASH_64BIT + int cmp = 1; - if( !COM_CheckString( szString )) - return 0; + if( !str64.allowdup ) + for( newString = str64.poldstringbase + 1; newString < str64.plast && ( cmp = Q_strcmp( newString, szValue ) ); newString += Q_strlen( newString ) + 1 ); - l = Q_strlen( szString ) + 1; - - out = out_p = Mem_Calloc( svgame.stringspool, l ); - for( i = 0; i < l; i++ ) + if( cmp ) { - if( szString[i] == '\\' && i < l - 1 ) - { - i++; - if( szString[i] == 'n') - *out_p++ = '\n'; - else *out_p++ = '\\'; - } - else *out_p++ = szString[i]; - } + uint len = Q_strlen( szValue ); - return out - svgame.globals->pStringBase; -} + if( str64.plast - str64.poldstringbase + len + 2 > str64.maxstringarray ) + { + str64.plast = str64.pstringbase + 1; + str64.poldstringbase = str64.pstringbase; + str64.numoverflows++; + } + + //MsgDev( D_NOTE, "SV_AllocString: %ld %s\n", str64.plast - svgame.globals->pStringBase, szValue ); + memcpy( str64.plast, szValue, len + 1 ); + str64.totalalloc += len + 1; + + newString = str64.plast; + str64.plast += len + 1; + } + else + str64.numdups++; + //MsgDev( D_NOTE, "SV_AllocString: dup %ld %s\n", newString - svgame.globals->pStringBase, szValue ); + + if( newString - str64.pstringarray > str64.maxalloc ) + str64.maxalloc = newString - str64.pstringarray; + + return newString - svgame.globals->pStringBase; +#else + newString = _copystring( svgame.stringspool, szValue, __FILE__, __LINE__ ); + return newString - svgame.globals->pStringBase; +#endif +} + +#ifdef XASH_64BIT +void SV_PrintStr64Stats_f( void ) +{ + Msg( "====================\n" ); + Msg( "64 bit string pool statistics\n" ); + Msg( "====================\n" ); + Msg( "string array size: %lu\n", str64.maxstringarray ); + Msg( "total alloc %lu\n", str64.totalalloc ); + Msg( "maximum array usage: %lu\n", str64.maxalloc ); + Msg( "overflow counter: %lu\n", str64.numoverflows ); + Msg( "dup string counter: %lu\n", str64.numdups ); +} +#endif /* ============= @@ -3011,9 +3218,18 @@ string_t SV_MakeString( const char *szValue ) { if( svgame.physFuncs.pfnMakeString != NULL ) return svgame.physFuncs.pfnMakeString( szValue ); +#ifdef XASH_64BIT + { + long long ptrdiff = szValue - svgame.globals->pStringBase; + if( ptrdiff > INT_MAX || ptrdiff < INT_MIN ) + return SV_AllocString(szValue); + else + return (int)ptrdiff; + } +#else return szValue - svgame.globals->pStringBase; -} - +#endif +} /* ============= @@ -4745,7 +4961,7 @@ void SV_UnloadProgs( void ) Delta_Shutdown (); Mod_ClearUserData (); - Mem_FreePool( &svgame.stringspool ); + SV_FreeStringPool(); if( svgame.dllFuncs2.pfnGameShutdown != NULL ) svgame.dllFuncs2.pfnGameShutdown (); @@ -4892,7 +5108,7 @@ qboolean SV_LoadProgs( const char *name ) e->free = true; // mark all edicts as freed Cvar_FullSet( "host_gameloaded", "1", FCVAR_READ_ONLY ); - svgame.stringspool = Mem_AllocPool( "Server Strings" ); + SV_AllocStringPool(); // fire once Con_Printf( "Dll loaded for game ^2\"%s\"\n", svgame.dllFuncs.pfnGetGameDescription( )); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index a294e8ce..affe4bff 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -514,6 +514,8 @@ void SV_ActivateServer( int runPhysics ) svgame.globals->time = sv.time; svgame.dllFuncs.pfnServerActivate( svgame.edicts, svgame.numEntities, svs.maxclients ); + SV_SetStringArrayMode( true ); + // parse user-specified resources SV_CreateGenericResources(); @@ -616,7 +618,7 @@ void SV_DeactivateServer( void ) SV_ClearPhysEnts (); - Mem_EmptyPool( svgame.stringspool ); + SV_EmptyStringPool(); for( i = 0; i < svs.maxclients; i++ ) { @@ -914,6 +916,7 @@ State machine exec new map */ void SV_ExecLoadLevel( void ) { + SV_SetStringArrayMode( false ); if( SV_SpawnServer( GameState->levelName, NULL, GameState->backgroundMap )) { SV_SpawnEntities( GameState->levelName ); diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 86a7528d..5fb9e478 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -2133,7 +2133,7 @@ used for reload game after player death const char *SV_GetLatestSave( void ) { static char savename[MAX_QPATH]; - long newest = 0, ft; + int newest = 0, ft; int i, found = 0; search_t *t; From 4b58b0b168ee4c73ee89cc11080b3363894df7a4 Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 6 Dec 2018 03:00:39 +0700 Subject: [PATCH 124/205] Fix console background aspect ratio --- engine/client/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/console.c b/engine/client/console.c index 7e8c9656..d0200e69 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2026,7 +2026,7 @@ void Con_DrawSolidConsole( int lines ) // draw the background GL_SetRenderMode( kRenderNormal ); pglColor4ub( 255, 255, 255, 255 ); // to prevent grab color from screenfade - R_DrawStretchPic( 0, lines - glState.height, glState.width, glState.height, 0, 0, 1, 1, con.background ); + R_DrawStretchPic( 0, lines - glState.width * 3 / 4, glState.width, glState.width * 3 / 4, 0, 0, 1, 1, con.background ); if( !con.curFont || !host.allow_console ) return; // nothing to draw From 17b0c23f2e82131aeafd7d01425af509c3c469fe Mon Sep 17 00:00:00 2001 From: mittorn Date: Thu, 6 Dec 2018 04:28:19 +0700 Subject: [PATCH 125/205] Show engine arch in version --- engine/client/console.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index d0200e69..29275318 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2041,7 +2041,7 @@ void Con_DrawSolidConsole( int lines ) memcpy( color, g_color_table[7], sizeof( color )); - Q_snprintf( curbuild, MAX_STRING, "%s %i/%s (hw build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, "%s %i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = glState.width - stringLen; @@ -2195,8 +2195,8 @@ void Con_DrawVersion( void ) host.force_draw_version = false; if( host.force_draw_version || draw_version ) - Q_snprintf( curbuild, MAX_STRING, "%s v%i/%s (build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); - else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, "%s v%i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); + else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (%s-%s build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = glState.width - stringLen * 1.05f; From 130a7b3c42360227080a45154185bfc39bd63908 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:16:23 +0300 Subject: [PATCH 126/205] scripts: move makepak.py from android project repo to engine main repo, port to python3, replace spaces by tabs --- scripts/makepak.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 scripts/makepak.py diff --git a/scripts/makepak.py b/scripts/makepak.py new file mode 100644 index 00000000..72e61710 --- /dev/null +++ b/scripts/makepak.py @@ -0,0 +1,46 @@ +from __future__ import print_function +import sys +import struct +import os + +#dummy class for stuffing the file headers into +class FileEntry: + pass + +#arguments are source directory, then target filename e.g. "pak1.pak" +rootdir = sys.argv[1] +pakfilename = sys.argv[2] + +pakfile = open(pakfilename,"wb") + +#write a dummy header to start with +pakfile.write(struct.Struct("<4s2l").pack(b"PACK",0,0)) + +#walk the directory recursively, add the files and record the file entries +offset = 12 +fileentries = [] +for root, subFolders, files in os.walk(rootdir): + for file in files: + entry = FileEntry() + impfilename = os.path.join(root,file) + entry.filename = os.path.relpath(impfilename,rootdir).replace("\\","/") + if(entry.filename.startswith(".git")):continue + print("pak: "+entry.filename) + with open(impfilename, "rb") as importfile: + pakfile.write(importfile.read()) + entry.offset = offset + entry.length = importfile.tell() + offset = offset + entry.length + fileentries.append(entry) +tablesize = 0 + +#after all the file data, write the list of entries +for entry in fileentries: + pakfile.write(struct.Struct("<56s").pack(entry.filename.encode("ascii"))) + pakfile.write(struct.Struct(" Date: Wed, 12 Dec 2018 04:18:09 +0300 Subject: [PATCH 127/205] scripts: add script building AppImage based on portable build for Linux --- scripts/build_appimage.sh | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 scripts/build_appimage.sh diff --git a/scripts/build_appimage.sh b/scripts/build_appimage.sh new file mode 100644 index 00000000..2cfa3879 --- /dev/null +++ b/scripts/build_appimage.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +APP=Xash3DFWGS +ARCH=i686 +APPDIR=$APP.AppDir +mkdir -p $APPDIR + +# Generate extras.pak +python3 scripts/makepak.py extras.pak + +# Copy all needed files +cp SDL2_linux/lib/libSDL2-2.0.so.0 $APPDIR/ +cp vgui-dev/lib/vgui.so $APPDIR/ +cp extras.pak $APPDIR/extras.pak +cp build/engine/libxash.so \ + build/mainui/libmenu.so \ + build/vgui_support/libvgui_support.so \ + build/game_launch/xash3d $APPDIR + +cat > $APPDIR/AppRun << 'EOF' +#!/bin/sh + +echo "Xash3D FWGS installed as AppImage." + +ENGINEROOT=$(dirname -- "$(readlink -f -- "$0")") +if [ -z "$XASH3D_BASEDIR" ]; then + export XASH3D_BASEDIR=$PWD +fi +export XASH3D_EXTRAS_PAK1="${ENGINEROOT}"/extras.pak +export LD_LIBRARY_PATH="${ENGINEROOT}":$LD_LIBRARY_PATH +${DEBUGGER} "${ENGINEROOT}"/xash3d "$@" +exit $? +EOF + +chmod +x $APPDIR/xash3d # Engine launcher +chmod +x $APPDIR/AppRun # Engine launcher script + +wget "https://raw.githubusercontent.com/FWGS/fwgs-artwork/master/xash3d/icon_512.png" -O $APPDIR/$APP.png + +cat > $APPDIR/$APP.desktop < Date: Wed, 12 Dec 2018 04:18:58 +0300 Subject: [PATCH 128/205] scripts: add xash-extras to travis common deps --- scripts/travis_common_deps.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/travis_common_deps.sh b/scripts/travis_common_deps.sh index 9ac7ed3d..89099ecc 100755 --- a/scripts/travis_common_deps.sh +++ b/scripts/travis_common_deps.sh @@ -1,2 +1,3 @@ git clone --depth 1 https://github.com/FWGS/vgui-dev git clone --depth 1 https://github.com/FWGS/vgui_support_bin +git clone --depth 1 https://github.com/FWGS/xash-extras From f9b13472b589b3ca52053ab395a13eeb57e21f7f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:19:17 +0300 Subject: [PATCH 129/205] scripts: autobuild appimage on travis --- scripts/build_linux_engine.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index 54a5e8e7..2144e153 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -31,6 +31,6 @@ export CC="ccache gcc" export CXX="ccache g++" ./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --use-stb ./waf build -j2 -# cp engine/xash3d mainui/libxashmenu.so vgui_support/libvgui_support.so vgui_support/vgui.so ../scripts/xash3d.sh . -# cp $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/$(readlink $TRAVIS_BUILD_DIR/sdl2-linux/usr/local/lib/libSDL2-2.0.so.0) libSDL2-2.0.so.0 -# 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-linux.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on xash3d libSDL2-2.0.so.0 libvgui_support.so vgui.so libxashmenu.so xash3d.sh + +# Build AppImage +scripts/build_appimage.sh From baa94e206480f30a6818fbbf709265a465601c43 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:19:47 +0300 Subject: [PATCH 130/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 7af08ccb..b476b796 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 7af08ccb66b5a74ec6cab298b63f4b829585fd12 +Subproject commit b476b79624d1d4af5b462ad42733ba675e439ef0 From 16aa5a78fe69b3e39357b00640eba59f5d812565 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:21:10 +0300 Subject: [PATCH 131/205] travis: building appimage requires libfuse2 library --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d22c57e0..5394f9d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ addons: - libasound-dev - zlib1g:i386 - libstdc++6:i386 + - libfuse2:i386 env: global: - SDL_VERSION=2.0.8 From 017714f3a05587d963cf3c85f8f83b4ba26407c3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:24:47 +0300 Subject: [PATCH 132/205] scripts: give build_appimage.sh +x --- scripts/build_appimage.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/build_appimage.sh diff --git a/scripts/build_appimage.sh b/scripts/build_appimage.sh old mode 100644 new mode 100755 From 239c6759cb13d8d395a97615b7b894471d9c82ee Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:32:58 +0300 Subject: [PATCH 133/205] scripts: add travis deploy script --- scripts/travis-deploy.sh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 scripts/travis-deploy.sh diff --git a/scripts/travis-deploy.sh b/scripts/travis-deploy.sh new file mode 100755 index 00000000..3c8fc992 --- /dev/null +++ b/scripts/travis-deploy.sh @@ -0,0 +1,39 @@ +#!/bin/bash +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo "Travis should not deploy from pull requests" + exit 0 +else + SOURCE_NAME=$1 + shift + mkdir xash3d-travis + cp -a $* xash3d-travis/ + cd xash3d-travis + git init + git config user.name FWGS-deployer + git config user.email FWGS-deployer@users.noreply.github.com + git remote add travis-deploy-public https://FWGS-deployer:${GH_TOKEN}@github.com/FWGS/xash3d-deploy.git + echo \# $TRAVIS_BRANCH branch autobuilds from $SOURCE_NAME >> README.md + echo >> README.md + echo Short changelog: >> README.md + echo \`\`\` >> README.md + (cd $TRAVIS_BUILD_DIR;git log --pretty=format:'%h %ad %s' --date iso -n 10 HEAD `curl https://raw.githubusercontent.com/FWGS/xash3d-deploy/$SOURCE_NAME-$TRAVIS_BRANCH/commit.txt` +.. )| cut -d ' ' -f 1-3,5-100 >> README.md + echo \`\`\` >> README.md + echo >> README.md + echo [Code on GitHub]\(https://github.com/FWGS/xash3d-fwgs/tree/$TRAVIS_COMMIT\) >> README.md + echo >> README.md + echo [Full changelog for this build]\(https://github.com/FWGS/xash3d-fwgs/commits/$TRAVIS_COMMIT\) >> README.md + echo >> README.md + for arg in $*; do + echo \* [$arg]\(https://github.com/FWGS/xash3d-deploy/blob/$SOURCE_NAME-$TRAVIS_BRANCH/$arg\?raw\=true\) >> README.md + echo >> README.md + done + echo $TRAVIS_COMMIT > commit.txt + git add . + git commit -m "Latest travis deploy $TRAVIS_COMMIT" + git checkout -b $SOURCE_NAME-$TRAVIS_BRANCH + git push -q --force travis-deploy-public $SOURCE_NAME-$TRAVIS_BRANCH >/dev/null 2>/dev/null + git checkout -b $SOURCE_NAME-latest + git push -q --force travis-deploy-public $SOURCE_NAME-latest >/dev/null 2>/dev/null +fi +exit 0 From 6a78b4ed1f036732e7419bcd5845700f9a987554 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:33:18 +0300 Subject: [PATCH 134/205] scripts: fix appimagetool warning --- scripts/build_appimage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_appimage.sh b/scripts/build_appimage.sh index 2cfa3879..874eb4c6 100755 --- a/scripts/build_appimage.sh +++ b/scripts/build_appimage.sh @@ -2,7 +2,7 @@ APP=Xash3DFWGS ARCH=i686 -APPDIR=$APP.AppDir +APPDIR=$APP-i386.AppDir mkdir -p $APPDIR # Generate extras.pak From ec80b0884f4c611175443d10d7a5cfecc1dadc73 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:33:36 +0300 Subject: [PATCH 135/205] travis: deploy at least appimage now --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5394f9d3..7ded5d1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,3 +42,7 @@ script: - sh scripts/build_${TRAVIS_OS_NAME}_engine.sh # - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/build_android_engine.sh; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/build_mingw_engine.sh; fi +after_script: + - cd ${TRAVIS_BUILD_DIR} + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ccache --show-stats > ccache_stats.log; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/travis-deploy.sh newengine Xash3DFWGS-i386.AppImage ccache_stats.log; fi From 154ef82719da9934fbaa1bb7c58c0f2f7d686d8c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:49:11 +0300 Subject: [PATCH 136/205] scripts: add mingw build archive creation --- scripts/build_mingw_engine.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index 947e7986..44390976 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -1,7 +1,6 @@ #!/bin/bash # Build engine - cd $TRAVIS_BUILD_DIR export CC="ccache i686-w64-mingw32-gcc" export CXX="ccache i686-w64-mingw32-g++" @@ -10,6 +9,9 @@ export CXXFLAGS="-static-libgcc -static-libstdc++" export WINRC="i686-w64-mingw32-windres" ./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug # can't compile VGUI support on MinGW, due to differnet C++ ABI ./waf build -o build-mingw -j2 -# cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 -# cp /usr/i686-w64-mingw32/lib/libwinpthread-1.dll . # a1ba: remove when travis will be updated to xenial -# 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-mingw.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on xash_sdl.exe menu.dll SDL2.dll vgui_support.dll libwinpthread-1.dll +cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 +cp vgui_support_bin/vgui_support.dll . +cp build-mingw/engine/xash.dll . +cp build-mingw/mainui/menu.dll . +cp build-mingw/game_launch/xash3d.exe . +7z a -t7z $TRAVIS_BUILD_DIR/xash3d-mingw.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on *.dll *.exe From cbb8db0089515cbf1517597ca3e7386e31538229 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Dec 2018 04:50:24 +0300 Subject: [PATCH 137/205] travis: add mingw build deployment --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7ded5d1f..126873ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,4 +45,4 @@ script: after_script: - cd ${TRAVIS_BUILD_DIR} - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ccache --show-stats > ccache_stats.log; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/travis-deploy.sh newengine Xash3DFWGS-i386.AppImage ccache_stats.log; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh scripts/travis-deploy.sh newengine Xash3DFWGS-i386.AppImage xash3d-mingw.7z ccache_stats.log; fi From 6eb327eac271ec3f3d1313189b5cf327dc2b46ba Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 03:25:46 +0300 Subject: [PATCH 138/205] scripts: enable verbose build, fix including SDL2 --- scripts/build_mingw_engine.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index 44390976..437f8890 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -7,9 +7,9 @@ export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" export WINRC="i686-w64-mingw32-windres" -./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug # can't compile VGUI support on MinGW, due to differnet C++ ABI -./waf build -o build-mingw -j2 -cp SDL2/SDL2-2.0.7/i686-w64-mingw32/bin/SDL2.dll . # Install SDL2 +./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug --verbose # can't compile VGUI support on MinGW, due to differnet C++ ABI +./waf build -o build-mingw -j2 --verbose +cp $TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32//bin/SDL2.dll . # Install SDL2 cp vgui_support_bin/vgui_support.dll . cp build-mingw/engine/xash.dll . cp build-mingw/mainui/menu.dll . From 09938b978b3b583a946d35ea2b883e460a31671e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 07:48:51 +0300 Subject: [PATCH 139/205] gitignore: allow waflib directory, hide pyc and pycache --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 82e292aa..72a641d9 100644 --- a/.gitignore +++ b/.gitignore @@ -315,6 +315,10 @@ build-* # Waf build_current .waf-* -waf*/ +waf-*/ +waf3-*/ .lock-waf* *.lastbuildstate +*.unsuccessfulbuild +__pycache__ +*.pyc From ec50c4571ccf129b7b1844adf5ffab19c6ecb338 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:00:22 +0300 Subject: [PATCH 140/205] waflib: add some waf plugins by FWGS --- scripts/waflib/cxx11.py | 51 ++++++++++++++++++++++++++ scripts/waflib/force_32bit.py | 58 ++++++++++++++++++++++++++++++ scripts/waflib/fwgslib.py | 33 +++++++++++++++++ scripts/waflib/gitversion.py | 33 +++++++++++++++++ scripts/waflib/sdl2.py | 68 +++++++++++++++++++++++++++++++++++ 5 files changed, 243 insertions(+) create mode 100644 scripts/waflib/cxx11.py create mode 100644 scripts/waflib/force_32bit.py create mode 100644 scripts/waflib/fwgslib.py create mode 100644 scripts/waflib/gitversion.py create mode 100644 scripts/waflib/sdl2.py diff --git a/scripts/waflib/cxx11.py b/scripts/waflib/cxx11.py new file mode 100644 index 00000000..5b9e0619 --- /dev/null +++ b/scripts/waflib/cxx11.py @@ -0,0 +1,51 @@ +# encoding: utf-8 +# cxx11.py -- check if compiler can compile C++11 code with lambdas +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from fwgslib import get_flags_by_compiler + +# Input: +# CXX11_MANDATORY(optional) -- fail if C++11 not available +# Output: +# HAVE_CXX11 -- true if C++11 available, otherwise else + +def check_cxx11(ctx, msg): + try: + # not best way, but this check + # was written for exactly mainui_cpp, + # where lambdas are mandatory + ctx.check_cxx( + fragment='int main( void ){ auto pfnLambda = [](){}; return 0;}', + msg = msg) + except ctx.errors.ConfigurationError: + return False + return True + +def configure(conf): + conf.env.HAVE_CXX11 = True # predict state + if not check_cxx11(conf, 'Checking if \'{0}\' supports C++11'.format(conf.env.COMPILER_CC)): + modern_cpp_flags = { + 'msvc': [], + 'default': ['-std=c++11'] + } + flags = get_flags_by_compiler(modern_cpp_flags, conf.env.COMPILER_CC) + if(len(flags) == 0): + conf.env.HAVE_CXX11 = False + else: + env_stash = conf.env + conf.env.append_unique('CXXFLAGS', flags) + if not check_cxx11(conf, '...trying with additional flags'): + conf.env.HAVE_CXX11 = False + conf.env = env_stash + if getattr(conf.env, 'CXX11_MANDATORY'): + conf.fatal('C++11 support not available!') + diff --git a/scripts/waflib/force_32bit.py b/scripts/waflib/force_32bit.py new file mode 100644 index 00000000..65335691 --- /dev/null +++ b/scripts/waflib/force_32bit.py @@ -0,0 +1,58 @@ +# encoding: utf-8 +# force_32bit.py -- force compiler to create 32-bit code +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from fwgslib import get_flags_by_compiler + +# Input: +# BIT32_MANDATORY(optional) -- fail if 32bit mode not available +# BIT32_ALLOW64(optional) -- ignore all checks, just set DEST_SIZEOF_VOID_P to 8 +# Output: +# DEST_SIZEOF_VOID_P -- an integer, equals sizeof(void*) on target + +def check_32bit(ctx, msg): + try: + ctx.check_cc( + fragment='''int main( void ) + { + int check[sizeof(void*) == 4 ? 1: -1]; + return 0; + }''', + msg = msg) + except ctx.errors.ConfigurationError: + return False + return True + +def configure(conf): + if getattr(conf.env, 'BIT32_ALLOW64'): + conf.env.DEST_SIZEOF_VOID_P = 8 + else: + if check_32bit(conf, 'Checking if \'{0}\' can target 32-bit'.format(conf.env.COMPILER_CC)): + conf.env.DEST_SIZEOF_VOID_P = 4 # predict state + else: + flag = '-m32' + # Think different. + if(conf.env.DEST_OS == 'darwin'): + flag = '-arch i386' + env_stash = conf.env + conf.env.append_value('LINKFLAGS', [flag]) + conf.env.append_value('CFLAGS', [flag]) + conf.env.append_value('CXXFLAGS', [flag]) + if check_32bit(conf, '...trying with additional flags'.format(conf.env.COMPILER_CC)): + conf.env.DEST_SIZEOF_VOID_P = 4 + else: + conf.env.DEST_SIZEOF_VOID_P = 8 + conf.env = env_stash + if getattr(conf.env, 'BIT32_MANDATORY') and conf.env.DEST_SIZEOF_VOID_P == 8: + conf.fatal('Compiler can\'t create 32-bit code!') + +4 diff --git a/scripts/waflib/fwgslib.py b/scripts/waflib/fwgslib.py new file mode 100644 index 00000000..e3b560ea --- /dev/null +++ b/scripts/waflib/fwgslib.py @@ -0,0 +1,33 @@ +# encoding: utf-8 +# fwgslib.py -- utils for Waf build system by FWGS +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import os + +def get_subproject_name(ctx): + return os.path.basename(os.path.realpath(str(ctx.path))) + +def get_flags_by_compiler(flags, compiler): + out = [] + if compiler in flags: + out += flags[compiler] + elif 'default' in flags: + out += flags['default'] + return out + +def get_flags_by_type(flags, type, compiler): + out = [] + if 'common' in flags: + out += get_flags_by_compiler(flags['common'], compiler) + if type in flags: + out += get_flags_by_compiler(flags[type], compiler) + return out \ No newline at end of file diff --git a/scripts/waflib/gitversion.py b/scripts/waflib/gitversion.py new file mode 100644 index 00000000..7b4025a1 --- /dev/null +++ b/scripts/waflib/gitversion.py @@ -0,0 +1,33 @@ +# encoding: utf-8 +# gitversion.py -- waf plugin to get git version +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import os + +def get_git_version(): + # try grab the current version number from git + version = None + if os.path.exists('.git'): + try: + version = os.popen('git describe --dirty --always').read().strip() + except Exception as e: + pass + + if(len(version) == 0): + version = None + + return version + +def configure(conf): + conf.start_msg('Checking git hash') + conf.env.GIT_VERSION = get_git_version() + conf.end_msg(conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'no') diff --git a/scripts/waflib/sdl2.py b/scripts/waflib/sdl2.py new file mode 100644 index 00000000..080f8fc3 --- /dev/null +++ b/scripts/waflib/sdl2.py @@ -0,0 +1,68 @@ +# encoding: utf-8 +# sdl2.py -- sdl2 waf plugin +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import os + +def options(opt): + opt.add_option( + '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, + help = 'SDL2 path to build(required for Windows)') + + opt.add_option( + '--skip-sdl2-sanity-check', action='store_false', default = True, dest='SDL2_SANITY_CHECK', + help = 'Skip checking SDL2 sanity') + +def configure(conf): + if conf.options.SDL2_PATH: + conf.start_msg('Configuring SDL2 by provided path') + conf.env.HAVE_SDL2 = 1 + conf.env.INCLUDES_SDL2 = [ + os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include')), + os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL2')) + ] + libpath = 'lib' + if conf.env.COMPILER_CC == 'msvc': + if conf.env.DEST_CPU == 'x86_64': + libpath = 'lib/x64' + else: + libpath = 'lib/' + conf.env.DEST_CPU + conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, libpath))] + conf.env.LIB_SDL2 = ['SDL2'] + conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_SDL2, conf.env.LIBPATH_SDL2, conf.env.INCLUDES_SDL2)) + else: + try: + conf.check_cfg( + path='sdl2-config', + args='--cflags --libs', + package='', + msg='Checking for library SDL2', + uselib_store='SDL2') + except conf.errors.ConfigurationError: + conf.env.HAVE_SDL2 = 0 + + if conf.env.HAVE_SDL2 and conf.options.SDL2_SANITY_CHECK: + try: + conf.check_cc( + fragment=''' + #define SDL_MAIN_HANDLED + #include + int main( void ) + { + SDL_Init( SDL_INIT_EVERYTHING ); + return 0; + }''', + msg = 'Checking for library SDL2 sanity', + use = 'SDL2', + execute = False) + except conf.errors.ConfigurationError: + conf.env.HAVE_SDL2 = 0 From 62995b076975a71250ff1375517cfd8c599bd07f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:03:26 +0300 Subject: [PATCH 141/205] engine: wscript: refactor, use sdl2.py to get configure SDL2, use shared get_subproject_name --- engine/wscript | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/engine/wscript b/engine/wscript index b0d53a74..73fc9c6a 100644 --- a/engine/wscript +++ b/engine/wscript @@ -4,10 +4,12 @@ from waflib import Logs import os +from fwgslib import get_subproject_name top = '.' def options(opt): + opt.load('sdl2') opt.add_option( '--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, help = 'build engine with BSP2 map support(recommended for Quake, breaks compability!)') @@ -20,47 +22,17 @@ def configure(conf): conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') else: - # TODO: add way to specify SDL2 path, move to separate function - try: - conf.check_cfg( - path='sdl2-config', - args='--cflags --libs', - package='', - msg='Checking for SDL2', - uselib_store='SDL2') - except conf.errors.ConfigurationError: - if(conf.options.SDL2_PATH): - conf.start_msg('Configuring SDL2 by provided path') - conf.env.HAVE_SDL2 = 1 - conf.env.INCLUDES_SDL2 = [ - os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include')), - os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL2')) - ] - libpath = 'lib' - if(conf.env.COMPILER_CC == 'msvc'): - libpath = 'lib/x86' - conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, libpath))] - conf.env.LIB_SDL2 = ['SDL2'] - conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_SDL2, conf.env.LIBPATH_SDL2, conf.env.INCLUDES_SDL2)) - else: - conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + conf.load('sdl2') + if not conf.env.HAVE_SDL2: + conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') - if(conf.options.SUPPORT_BSP2_FORMAT): + if conf.options.SUPPORT_BSP2_FORMAT: conf.env.append_unique('DEFINES', 'SUPPORT_BSP2_FORMAT') if conf.env.DEST_OS == 'win32': - conf.check( lib='user32' ) - conf.check( lib='shell32' ) - conf.check( lib='gdi32' ) - conf.check( lib='advapi32' ) - conf.check( lib='dbghelp' ) - conf.check( lib='psapi' ) conf.env.append_unique('DEFINES', 'DBGHELP') -def get_subproject_name(ctx): - return os.path.basename(os.path.realpath(str(ctx.path))) - def build(bld): bld.load_envs() bld.env = bld.all_envs[get_subproject_name(bld)] @@ -98,7 +70,7 @@ def build(bld): includes = ['common', 'server', 'client', 'client/vgui', '.', '../common', '../pm_shared' ] - if(bld.env.SINGLE_BINARY): + if bld.env.SINGLE_BINARY: bld( source = source, target = 'xash', From 6debd84a2ebb2d3b708f4134dad9b668347307cf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:03:51 +0300 Subject: [PATCH 142/205] game_launch: wscript: refactor, use shared get_subproject_name --- game_launch/wscript | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/game_launch/wscript b/game_launch/wscript index 9029ea2a..2e650bc9 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -4,6 +4,8 @@ from waflib import Logs import os +import sys +from fwgslib import get_subproject_name top = '.' @@ -12,18 +14,13 @@ def options(opt): return def configure(conf): - if(conf.env.SINGLE_BINARY): + if conf.env.SINGLE_BINARY: return # check for dedicated server build if not conf.env.DEDICATED: if conf.env.DEST_OS == 'win32': conf.load('winres') - conf.check(lib='user32') - conf.check(lib='shell32') - -def get_subproject_name(ctx): - return os.path.basename(os.path.realpath(str(ctx.path))) def build(bld): if bld.env.SINGLE_BINARY: From 9ce9fa8cb584c05fcd3c87b3850947125b17f177 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:05:31 +0300 Subject: [PATCH 143/205] vgui_support: wscript: refactor, use shared get_subproject_name, rename no-vgui to disable-vgui to follow automake-style convention, add vGUI library sanity check --- vgui_support/wscript | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index ae2a57f1..ef6f1bcd 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -4,6 +4,7 @@ from waflib import Logs import os +from fwgslib import get_subproject_name top = '.' @@ -11,10 +12,15 @@ def options(opt): opt.add_option( '--vgui', action = 'store', type='string', dest = 'VGUI_DEV', help = 'path to vgui-dev repo', default='' ) + opt.add_option( - '--no-vgui', action = 'store_true', dest = 'NO_VGUI', + '--disable-vgui', action = 'store_true', dest = 'NO_VGUI', help = 'disable vgui_support', default=False ) + opt.add_option( + '--skip-vgui-sanity-check', action = 'store_false', dest = 'VGUI_SANITY_CHECK', + help = 'Skip checking VGUI sanity', default=True ) + # stub return @@ -23,7 +29,7 @@ def configure(conf): if conf.options.DEDICATED or conf.options.NO_VGUI: return - conf.start_msg('Checking for VGUI') + conf.start_msg('Configuring VGUI by provided path') if not conf.options.VGUI_DEV: conf.end_msg('no') @@ -49,8 +55,23 @@ def configure(conf): conf.env.HAVE_VGUI = 1 conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) -def get_subproject_name(ctx): - return os.path.basename(os.path.realpath(str(ctx.path))) + if conf.env.HAVE_VGUI and conf.options.VGUI_SANITY_CHECK: + try: + conf.check_cxx( + fragment=''' + #include + #include + int main( int argc, char **argv ) + { + vgui::App *app = vgui::App::getInstance(); + app->main(argc, argv); + return 0; + }''', + msg = 'Checking for library VGUI sanity', + use = 'VGUI', + execute = False) + except conf.errors.ConfigurationError: + conf.fatal("Can't compile simple program. Check your path to vgui-dev repository.") def build(bld): bld.load_envs() From ede52d78084cb7ef39eaf42cbf3abc27d6d85e45 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:05:56 +0300 Subject: [PATCH 144/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index b476b796..f288c4b7 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit b476b79624d1d4af5b462ad42733ba675e439ef0 +Subproject commit f288c4b740289ad96710de772e769cc2a0731b7e From bbde1394e144d92714699a911646b8f5eacc283e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:08:03 +0300 Subject: [PATCH 145/205] wscript: move some code to our waflib, remove --no-gcc-colors, as newer GCC colorize output automatically, so there is no need in -fdisagnostic-colors=aloways flag, move common win32 libraries check here, better output for subprojects --- wscript | 145 +++++++++++++++++++------------------------------------- 1 file changed, 50 insertions(+), 95 deletions(-) diff --git a/wscript b/wscript index 0b12a0df..554a598b 100644 --- a/wscript +++ b/wscript @@ -4,48 +4,26 @@ from __future__ import print_function from waflib import Logs, Options -import os import sys +import os + +sys.path.append(os.path.realpath('scripts/waflib')) +# print(sys.path) +import fwgslib VERSION = '0.99' APPNAME = 'xash3d-fwgs' -SUBDIRS = [ 'game_launch', 'mainui', 'vgui_support', 'engine' ] +SUBDIRS = [ 'engine', 'game_launch', 'mainui', 'vgui_support' ] top = '.' -def get_git_version(): - # try grab the current version number from git - version = 'notset' - if os.path.exists('.git'): - try: - version = os.popen('git describe --dirty --always').read().strip() - except Exception as e: - pass - - if(len(version) == 0): - version = 'notset' - - return version - -def get_flags_by_compiler(flags, compiler): - out = [] - if compiler in flags: - out += flags[compiler] - elif 'default' in flags: - out += flags['default'] - return out - -def get_flags_by_type(flags, type, compiler): - out = [] - if 'common' in flags: - out += get_flags_by_compiler(flags['common'], compiler) - if type in flags: - out += get_flags_by_compiler(flags[type], compiler) - return out - def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': opt.load('msvc msvs') + + opt.add_option( + '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, + help = 'build type: debug, release or none(custom flags)') opt.add_option( '--dedicated', action = 'store_true', dest = 'DEDICATED', default = False, @@ -58,19 +36,7 @@ def options(opt): opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') - - opt.add_option( - '--no-gcc-colors', action = 'store_false', dest = 'GCC_COLORS', default = True, - help = 'do not enable gcc colors') - - opt.add_option( - '--sdl2', action='store', type='string', dest = 'SDL2_PATH', default = None, - help = 'SDL2 path to build(required for Windows)') - - opt.add_option( - '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, - help = 'build type: debug, release or none(custom flags)') - + opt.recurse(SUBDIRS) def configure(conf): @@ -83,42 +49,27 @@ def configure(conf): conf.fatal('Invalid build type. Valid are "debug", "release" or "none"') conf.end_msg(conf.options.BUILD_TYPE) + # Force XP compability, all build targets should add + # subsystem=bld.env.MSVC_SUBSYSTEM + # TODO: wrapper around bld.stlib, bld.shlib and so on? + conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC - conf.load('compiler_cxx compiler_c') + conf.load('compiler_cxx compiler_c gitversion') if sys.platform == 'win32': conf.load('msvc msvs') - # Check if we have 64-bit toolchain - conf.env.DEST_64BIT = False # predict state - try: - conf.check_cc( - fragment='''int main( void ) - { - int check[sizeof(void*) == 4 ? 1: -1]; - return 0; - }''', - msg = 'Checking if compiler create 32 bit code') - except conf.errors.ConfigurationError: - # Program not compiled, we have 64 bit - conf.env.DEST_64BIT = True - - - if(conf.env.DEST_64BIT): - if(not conf.options.ALLOW64): - flag = '-m32' - # Think different. - if(conf.env.DEST_OS == 'darwin'): - flag = '-arch i386' - conf.env.append_value('LINKFLAGS', [flag]) - conf.env.append_value('CFLAGS', [flag]) - conf.env.append_value('CXXFLAGS', [flag]) - Logs.info('NOTE: will build engine with 64-bit toolchain using %s' % flag) - else: - Logs.warn('WARNING: 64-bit engine may be unstable') + conf.env.BIT32_MANDATORY = not conf.options.ALLOW64 + conf.env.BIT32_ALLOW64 = conf.options.ALLOW64 + conf.load('force_32bit') + + if conf.env.DEST_SIZEOF_VOID_P == 4: + Logs.info('NOTE: will build engine for 32-bit target') + else: + Logs.warn('WARNING: 64-bit engine may be unstable') linker_flags = { 'common': { - 'msvc': ['/DEBUG'], + 'msvc': ['/DEBUG'], # always create PDB, doesn't affect result binaries 'default': ['-Wl,--no-undefined'] } } @@ -139,28 +90,35 @@ def configure(conf): } } - conf.env.append_unique('CFLAGS', get_flags_by_type( + conf.env.append_unique('CFLAGS', fwgslib.get_flags_by_type( compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) - conf.env.append_unique('CXXFLAGS', get_flags_by_type( + conf.env.append_unique('CXXFLAGS', fwgslib.get_flags_by_type( compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) - conf.env.append_unique('LINKFLAGS', get_flags_by_type( + conf.env.append_unique('LINKFLAGS', fwgslib.get_flags_by_type( linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) - # Force XP compability, all build targets should add - # subsystem=bld.env.MSVC_SUBSYSTEM - # TODO: wrapper around bld.stlib, bld.shlib and so on? - conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' - - if(conf.env.DEST_OS == 'linux'): - conf.check( lib='dl' ) - - if(conf.env.DEST_OS != 'win32'): - conf.check( lib='m' ) - conf.check( lib='pthread' ) - conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated + if conf.env.DEST_OS == 'linux': + conf.check( lib='dl' ) + + if conf.env.DEST_OS != 'win32': + conf.check( lib='m' ) + conf.check( lib='pthread' ) + else: + # Common Win32 libraries + # Don't check them more than once, to save time + # Usually, they are always available + # but we need them in uselib + conf.check( lib='user32' ) + conf.check( lib='shell32' ) + conf.check( lib='gdi32' ) + conf.check( lib='advapi32' ) + conf.check( lib='dbghelp' ) + conf.check( lib='psapi' ) + + # indicate if we are packaging for Linux/BSD if(not conf.options.WIN_INSTALL and conf.env.DEST_OS != 'win32' and @@ -169,18 +127,15 @@ def configure(conf): else: conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX - conf.start_msg('Checking git hash') - git_version = get_git_version() - conf.end_msg(git_version) - conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="' + git_version + '"') + conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="' + conf.env.GIT_VERSION if conf.env.GITVERSION else 'notset' + '"') for i in SUBDIRS: conf.setenv(i, conf.env) # derive new env from global one conf.env.ENVNAME = i - conf.msg(msg='Configuring ' + i, result='in progress', color='BLUE') + conf.msg(msg='--> ' + i, result='in progress', color='BLUE') # configure in standalone env conf.recurse(i) - conf.msg(msg='Configuring ' + i, result='done', color='BLUE') + conf.msg(msg='<-- ' + i, result='done', color='BLUE') conf.setenv('') def build(bld): From 08dd632d871848d91478ead765a6d49ec8b8aa99 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 08:13:27 +0300 Subject: [PATCH 146/205] wscript: fix git version definition --- wscript | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wscript b/wscript index 554a598b..fb84702e 100644 --- a/wscript +++ b/wscript @@ -20,7 +20,7 @@ def options(opt): opt.load('compiler_cxx compiler_c') if sys.platform == 'win32': opt.load('msvc msvs') - + opt.add_option( '--build-type', action='store', type='string', dest='BUILD_TYPE', default = None, help = 'build type: debug, release or none(custom flags)') @@ -36,7 +36,7 @@ def options(opt): opt.add_option( '--win-style-install', action = 'store_true', dest = 'WIN_INSTALL', default = False, help = 'install like Windows build, ignore prefix, useful for development') - + opt.recurse(SUBDIRS) def configure(conf): @@ -61,7 +61,7 @@ def configure(conf): conf.env.BIT32_MANDATORY = not conf.options.ALLOW64 conf.env.BIT32_ALLOW64 = conf.options.ALLOW64 conf.load('force_32bit') - + if conf.env.DEST_SIZEOF_VOID_P == 4: Logs.info('NOTE: will build engine for 32-bit target') else: @@ -127,7 +127,7 @@ def configure(conf): else: conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX - conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="' + conf.env.GIT_VERSION if conf.env.GITVERSION else 'notset' + '"') + conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="{0}"'.format(conf.env.GIT_VERSION if conf.env.GITVERSION else 'notset')) for i in SUBDIRS: conf.setenv(i, conf.env) # derive new env from global one From 28c47a5c868c8acf52e70d12f5fcd1ccb98fcbed Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 18:43:33 +0300 Subject: [PATCH 147/205] scripts: fix travis scripts after refactoring wscripts --- scripts/build_linux_engine.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index 2144e153..527c7262 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -29,7 +29,7 @@ make install DESTDIR=$TRAVIS_BUILD_DIR/SDL2_linux cd $TRAVIS_BUILD_DIR export CC="ccache gcc" export CXX="ccache g++" -./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --use-stb +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --enable-stb ./waf build -j2 # Build AppImage From 2072f5c2767b99bfe5ca34c3d632976d5e6fc065 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Dec 2018 18:44:13 +0300 Subject: [PATCH 148/205] scripts: fix travis scripts after refactoring wscript --- scripts/build_mingw_engine.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index 437f8890..e81c3b6e 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -7,7 +7,7 @@ export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" export WINRC="i686-w64-mingw32-windres" -./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --no-vgui --build-type=debug --verbose # can't compile VGUI support on MinGW, due to differnet C++ ABI +./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --disable-vgui --build-type=debug --verbose # can't compile VGUI support on MinGW, due to differnet C++ ABI ./waf build -o build-mingw -j2 --verbose cp $TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32//bin/SDL2.dll . # Install SDL2 cp vgui_support_bin/vgui_support.dll . From a580e98f28e54b0a706b17f0600afc05ca526c9f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 16 Dec 2018 17:01:12 +0300 Subject: [PATCH 149/205] waflib: force_32bit: try to fix passing additional flags to generate 32bit code on MacOSX --- scripts/waflib/force_32bit.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/scripts/waflib/force_32bit.py b/scripts/waflib/force_32bit.py index 65335691..efec6b11 100644 --- a/scripts/waflib/force_32bit.py +++ b/scripts/waflib/force_32bit.py @@ -37,22 +37,20 @@ def configure(conf): conf.env.DEST_SIZEOF_VOID_P = 8 else: if check_32bit(conf, 'Checking if \'{0}\' can target 32-bit'.format(conf.env.COMPILER_CC)): - conf.env.DEST_SIZEOF_VOID_P = 4 # predict state + conf.env.DEST_SIZEOF_VOID_P = 4 else: - flag = '-m32' + flags = ['-m32'] # Think different. if(conf.env.DEST_OS == 'darwin'): - flag = '-arch i386' + flags = ['-arch', 'i386'] env_stash = conf.env - conf.env.append_value('LINKFLAGS', [flag]) - conf.env.append_value('CFLAGS', [flag]) - conf.env.append_value('CXXFLAGS', [flag]) - if check_32bit(conf, '...trying with additional flags'.format(conf.env.COMPILER_CC)): + conf.env.append_value('LINKFLAGS', flags) + conf.env.append_value('CFLAGS', flags) + conf.env.append_value('CXXFLAGS', flags) + if check_32bit(conf, '...trying with additional flags'): conf.env.DEST_SIZEOF_VOID_P = 4 else: conf.env.DEST_SIZEOF_VOID_P = 8 conf.env = env_stash if getattr(conf.env, 'BIT32_MANDATORY') and conf.env.DEST_SIZEOF_VOID_P == 8: conf.fatal('Compiler can\'t create 32-bit code!') - -4 From 95ce9fc00ff90465419331eee6e719b8b816a57c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 16 Dec 2018 17:10:01 +0300 Subject: [PATCH 150/205] wscript: explicitly set language when checking library --- engine/wscript | 2 +- wscript | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/engine/wscript b/engine/wscript index 73fc9c6a..fa1af247 100644 --- a/engine/wscript +++ b/engine/wscript @@ -18,7 +18,7 @@ def configure(conf): # check for dedicated server build if conf.options.DEDICATED: if(conf.env.DEST_OS == 'linux'): - conf.check( lib='rt' ) + conf.check_cc( lib='rt' ) conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') else: diff --git a/wscript b/wscript index fb84702e..d5f39988 100644 --- a/wscript +++ b/wscript @@ -101,22 +101,22 @@ def configure(conf): conf.env.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated if conf.env.DEST_OS == 'linux': - conf.check( lib='dl' ) + conf.check_cc( lib='dl' ) if conf.env.DEST_OS != 'win32': - conf.check( lib='m' ) - conf.check( lib='pthread' ) + conf.check_cc( lib='m' ) + conf.check_cc( lib='pthread' ) else: # Common Win32 libraries # Don't check them more than once, to save time # Usually, they are always available # but we need them in uselib - conf.check( lib='user32' ) - conf.check( lib='shell32' ) - conf.check( lib='gdi32' ) - conf.check( lib='advapi32' ) - conf.check( lib='dbghelp' ) - conf.check( lib='psapi' ) + conf.check_cc( lib='user32' ) + conf.check_cc( lib='shell32' ) + conf.check_cc( lib='gdi32' ) + conf.check_cc( lib='advapi32' ) + conf.check_cc( lib='dbghelp' ) + conf.check_cc( lib='psapi' ) # indicate if we are packaging for Linux/BSD From 30c32b0088586ff133b4b02c62e736d4d90de9d8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 02:34:49 +0300 Subject: [PATCH 151/205] waflib: sdl2: fix finding sdl2 on osx --- scripts/waflib/sdl2.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scripts/waflib/sdl2.py b/scripts/waflib/sdl2.py index 080f8fc3..5cc28bc0 100644 --- a/scripts/waflib/sdl2.py +++ b/scripts/waflib/sdl2.py @@ -22,13 +22,18 @@ def options(opt): '--skip-sdl2-sanity-check', action='store_false', default = True, dest='SDL2_SANITY_CHECK', help = 'Skip checking SDL2 sanity') -def configure(conf): - if conf.options.SDL2_PATH: - conf.start_msg('Configuring SDL2 by provided path') - conf.env.HAVE_SDL2 = 1 +def sdl2_configure_path(conf, path): + conf.env.HAVE_SDL2 = 1 + if conf.env.DEST_OS == 'darwin': conf.env.INCLUDES_SDL2 = [ - os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include')), - os.path.abspath(os.path.join(conf.options.SDL2_PATH, 'include/SDL2')) + os.path.abspath(os.path.join(path, 'Headers')) + ] + conf.env.FRAMEWORKPATH_SDL2 = [path] + conf.env.FRAMEWORK_SDL2 = ['SDL2'] + else: + conf.env.INCLUDES_SDL2 = [ + os.path.abspath(os.path.join(path, 'include')), + os.path.abspath(os.path.join(path, 'include/SDL2')) ] libpath = 'lib' if conf.env.COMPILER_CC == 'msvc': @@ -36,8 +41,13 @@ def configure(conf): libpath = 'lib/x64' else: libpath = 'lib/' + conf.env.DEST_CPU - conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(conf.options.SDL2_PATH, libpath))] + conf.env.LIBPATH_SDL2 = [os.path.abspath(os.path.join(path, libpath))] conf.env.LIB_SDL2 = ['SDL2'] + +def configure(conf): + if conf.options.SDL2_PATH: + conf.start_msg('Configuring SDL2 by provided path') + sdl2_configure_path(conf, conf.options.SDL2_PATH) conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_SDL2, conf.env.LIBPATH_SDL2, conf.env.INCLUDES_SDL2)) else: try: From bd7e2fe88dc3ea364c7b135c73e6c2be19b8fe95 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 02:35:37 +0300 Subject: [PATCH 152/205] wscript: use no-undefined only on gcc --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index d5f39988..430338f2 100644 --- a/wscript +++ b/wscript @@ -70,7 +70,7 @@ def configure(conf): linker_flags = { 'common': { 'msvc': ['/DEBUG'], # always create PDB, doesn't affect result binaries - 'default': ['-Wl,--no-undefined'] + 'gcc': ['-Wl,--no-undefined'] } } From cfea381c8413d8a453d202ac9815e3e8fd1673bd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 02:36:14 +0300 Subject: [PATCH 153/205] vgui_support: wscript: fix finding VGUI on osx, simplify sanity check --- vgui_support/wscript | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index ef6f1bcd..a48ebfff 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -46,7 +46,8 @@ def configure(conf): if conf.env.DEST_OS == 'linux': conf.env.LIB_VGUI = [':vgui.so'] elif conf.env.DEST_OS == 'darwin': - conf.env.LIB_VGUI = ['vgui.dylib'] + conf.env.LIB_VGUI = ['vgui'] + conf.parse_flags('-Wl,-rpath,', 'VGUI') else: conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib'))] @@ -60,11 +61,8 @@ def configure(conf): conf.check_cxx( fragment=''' #include - #include int main( int argc, char **argv ) { - vgui::App *app = vgui::App::getInstance(); - app->main(argc, argv); return 0; }''', msg = 'Checking for library VGUI sanity', From e2606cf97f0e14a960d1b317bf295e7752b74529 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 02:53:00 +0300 Subject: [PATCH 154/205] scripts: stop executing script and print config.log, if waf was failed. --- scripts/build_linux_engine.sh | 6 ++++-- scripts/build_mingw_engine.sh | 6 ++++-- scripts/build_osx_engine.sh | 6 ++++-- scripts/lib.sh | 5 +++++ 4 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 scripts/lib.sh diff --git a/scripts/build_linux_engine.sh b/scripts/build_linux_engine.sh index 527c7262..9fb4b945 100755 --- a/scripts/build_linux_engine.sh +++ b/scripts/build_linux_engine.sh @@ -2,6 +2,8 @@ # Build custom SDL2 +. scripts/lib.sh + cd $TRAVIS_BUILD_DIR/SDL2_src export CC="ccache gcc -msse2 -march=i686 -m32 -ggdb -O2" ./configure \ @@ -29,8 +31,8 @@ make install DESTDIR=$TRAVIS_BUILD_DIR/SDL2_linux cd $TRAVIS_BUILD_DIR export CC="ccache gcc" export CXX="ccache g++" -./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --enable-stb -./waf build -j2 +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_linux --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug --enable-stb || die +./waf build -j2 || die # Build AppImage scripts/build_appimage.sh diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index e81c3b6e..78b7a6d6 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -1,5 +1,7 @@ #!/bin/bash +. scripts/lib.sh + # Build engine cd $TRAVIS_BUILD_DIR export CC="ccache i686-w64-mingw32-gcc" @@ -7,8 +9,8 @@ export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" export WINRC="i686-w64-mingw32-windres" -./waf configure -o build-mingw --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --disable-vgui --build-type=debug --verbose # can't compile VGUI support on MinGW, due to differnet C++ ABI -./waf build -o build-mingw -j2 --verbose +./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --disable-vgui --build-type=debug --verbose || die # can't compile VGUI support on MinGW, due to differnet C++ ABI +./waf build -j2 --verbose || die cp $TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32//bin/SDL2.dll . # Install SDL2 cp vgui_support_bin/vgui_support.dll . cp build-mingw/engine/xash.dll . diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh index d0ff01f9..0555ea92 100755 --- a/scripts/build_osx_engine.sh +++ b/scripts/build_osx_engine.sh @@ -1,5 +1,7 @@ #!/bin/bash +. scripts/lib.sh + # Build engine cd $TRAVIS_BUILD_DIR @@ -7,8 +9,8 @@ export CC="/usr/bin/clang" export CXX="/usr/bin/clang++" export CFLAGS="-m32" export CXXFLAGS="-m32" -python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug -python waf build -j2 +python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug || die +python waf build -j2 || die # mkdir -p pkg/ # cp engine/libxash.dylib game_launch/xash3d mainui/libxashmenu.dylib vgui_support/libvgui_support.dylib VGUI/vgui-dev-master/lib/vgui.dylib ../scripts/xash3d.sh # pkg/ # cp ~/Library/Frameworks/SDL2.framework/SDL2 pkg/libSDL2.dylib diff --git a/scripts/lib.sh b/scripts/lib.sh new file mode 100644 index 00000000..ee1bebd0 --- /dev/null +++ b/scripts/lib.sh @@ -0,0 +1,5 @@ +die() +{ + cat build/config.log + exit 1 +} From 28e6dec4b01fea8a9e0665207f8b15fd2caedb40 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 02:59:57 +0300 Subject: [PATCH 155/205] scripts: osx: fix path to SDL2 framework --- scripts/build_osx_engine.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_osx_engine.sh b/scripts/build_osx_engine.sh index 0555ea92..9ef3061e 100755 --- a/scripts/build_osx_engine.sh +++ b/scripts/build_osx_engine.sh @@ -9,7 +9,7 @@ export CC="/usr/bin/clang" export CXX="/usr/bin/clang++" export CFLAGS="-m32" export CXXFLAGS="-m32" -python waf configure --sdl2=~/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug || die +python waf configure --sdl2=$HOME/Library/Frameworks/SDL2.framework/ --vgui=$TRAVIS_BUILD_DIR/vgui-dev --build-type=debug || die python waf build -j2 || die # mkdir -p pkg/ # cp engine/libxash.dylib game_launch/xash3d mainui/libxashmenu.dylib vgui_support/libvgui_support.dylib VGUI/vgui-dev-master/lib/vgui.dylib ../scripts/xash3d.sh # pkg/ From 5d6d6c766e939f9601a53c8d29393914b3033475 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Dec 2018 03:11:41 +0300 Subject: [PATCH 156/205] scripts: sdl2: fix finding sdl2 on osx --- scripts/waflib/sdl2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/waflib/sdl2.py b/scripts/waflib/sdl2.py index 5cc28bc0..61490dc6 100644 --- a/scripts/waflib/sdl2.py +++ b/scripts/waflib/sdl2.py @@ -28,7 +28,7 @@ def sdl2_configure_path(conf, path): conf.env.INCLUDES_SDL2 = [ os.path.abspath(os.path.join(path, 'Headers')) ] - conf.env.FRAMEWORKPATH_SDL2 = [path] + conf.env.FRAMEWORKPATH_SDL2 = [os.path.dirname(path)] conf.env.FRAMEWORK_SDL2 = ['SDL2'] else: conf.env.INCLUDES_SDL2 = [ From aac44ef866fb2312a52d07b10daa103c30140596 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 19 Dec 2018 06:57:54 +0300 Subject: [PATCH 157/205] waflib: try to fix sdl2 searching on osx again --- scripts/waflib/sdl2.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/waflib/sdl2.py b/scripts/waflib/sdl2.py index 61490dc6..0f20fcd5 100644 --- a/scripts/waflib/sdl2.py +++ b/scripts/waflib/sdl2.py @@ -22,13 +22,19 @@ def options(opt): '--skip-sdl2-sanity-check', action='store_false', default = True, dest='SDL2_SANITY_CHECK', help = 'Skip checking SDL2 sanity') +def my_dirname(path): + # really dumb, will not work with /path/framework//, but still enough + if path[-1] == '/': + path = path[:-1] + return os.path.dirname(path) + def sdl2_configure_path(conf, path): conf.env.HAVE_SDL2 = 1 if conf.env.DEST_OS == 'darwin': conf.env.INCLUDES_SDL2 = [ os.path.abspath(os.path.join(path, 'Headers')) ] - conf.env.FRAMEWORKPATH_SDL2 = [os.path.dirname(path)] + conf.env.FRAMEWORKPATH_SDL2 = [my_dirname(path)] conf.env.FRAMEWORK_SDL2 = ['SDL2'] else: conf.env.INCLUDES_SDL2 = [ From ccac3e9cfc9424f9c9434941c9ceb809d8d71f3a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 19 Dec 2018 07:12:45 +0300 Subject: [PATCH 158/205] wscript: try to fix linking vgui on osx --- vgui_support/wscript | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vgui_support/wscript b/vgui_support/wscript index a48ebfff..a4996137 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -43,14 +43,14 @@ def configure(conf): conf.env.LIB_VGUI = ['vgui'] conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib/win32_vc6/'))] else: + libpath = os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib')) if conf.env.DEST_OS == 'linux': conf.env.LIB_VGUI = [':vgui.so'] + conf.env.LIBPATH_VGUI = [libpath] elif conf.env.DEST_OS == 'darwin': - conf.env.LIB_VGUI = ['vgui'] - conf.parse_flags('-Wl,-rpath,', 'VGUI') + conf.env.LDFLAGS_VGUI = [os.path.join(libpath, 'vgui.dylib')] else: conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) - conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'lib'))] conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.options.VGUI_DEV, 'include'))] conf.env.HAVE_VGUI = 1 From 71cae6b02b3d65eab4e1e1167c5ca507fc668006 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 09:44:51 +0300 Subject: [PATCH 159/205] waflib: add xcompile helper plugin intended to integrate with platform SDKs, like Android NDK --- scripts/waflib/xcompile.py | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 scripts/waflib/xcompile.py diff --git a/scripts/waflib/xcompile.py b/scripts/waflib/xcompile.py new file mode 100644 index 00000000..4a2ec5cb --- /dev/null +++ b/scripts/waflib/xcompile.py @@ -0,0 +1,139 @@ +# encoding: utf-8 +# xcompile.py -- crosscompiling utils +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from fwgslib import get_flags_by_compiler +import os +import sys + +# Output: +# CROSSCOMPILING -- set to true, if crosscompiling is enabled +# DEST_OS2 -- as some operating systems is built on top of another, it's better to not change DEST_OS, +# instead of this DEST_OS2 is defined with target value +# For example: android is built on top of linux and have many things in common, +# but it can't be considered as default GNU/Linux. +# Possible values: +# DEST_OS2 DEST_OS +# 'android' 'linux' + +class Android: + arch = None + toolchain = None + api = None + toolchain_path = None + ndk_home = None + + # TODO: Clang support? + # TODO: New Android NDK support? + # TODO: Crystax support? + # TODO: Support for everything else than linux-x86_64? + # TODO: Determine if I actually need to implement listed above + def is_arm(self): + return self.arch.startswith('armeabi') + + def gen_toolchain_path(self): + path = 'toolchains/' + if self.arch.startswith('x86'): + path += self.arch + '-' + self.toolchain + elif self.is_arm(): + path += 'arm-linux-androideabi-' + self.toolchain + else: + path += self.arch + '-linux-android-' + self.toolchain + path += '/prebuilt/linux-x86_64/bin/' + + if self.arch == 'x86': + path += 'i686-linux-android-' + elif self.is_arm(): + path += 'arm-linux-androideabi-' + else: + path += self.arch + '-linux-android-' + + return path + + def cc(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + 'gcc')) + + def cxx(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + 'g++')) + + def link(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + 'ld')) + + def sysroot(self): + arch = self.arch + if self.is_arm(): + arch = 'arm' + elif self.arch == 'aarch64': + arch = 'arm64' + path = 'platforms/android-{0}/arch-{1}'.format(self.api, arch) + + return os.path.abspath(os.path.join(self.ndk_home, path)) + + def cflags(self): + cflags = ['--sysroot={0}'.format(self.sysroot()), '-DANDROID'] + if self.is_arm(): + if self.arch.startswith('armeabi-v7a'): + cflags += ['-march=armv7-a', '-mfpu=vfpv3-d16'] + if self.arch == 'armeabi-v7a-hard': + cflags += ['-mhard-float', '-D_NDK_MATH_NO_SOFTFP=1'] + else: + cflags += ['-mfloat-abi=softfp'] + else: + cflags += ['-march=armv5te', '-mtune=xscale', '-msoft-float'] + return cflags + + def ldflags(self): + ldflags = ['--sysroot={0}'.format(self.sysroot())] + if self.is_arm(): + if self.arch.startswith('armeabi-v7a'): + ldflags += ['-march=armv7-a', '-Wl,--fix-cortex-a8'] + if self.arch == 'armeabi-v7a-hard': + ldflags += ['-Wl,--no-warn-mismatch', '-lm_hard'] + else: + ldflags += ['-march=armv5te'] + return ldflags + + def __init__(self, ndk_home, arch, toolchain, api): + self.ndk_home = ndk_home + self.arch = arch + self.toolchain = toolchain + self.api = api + self.toolchain_path = self.gen_toolchain_path() + +def options(opt): + android = opt.add_option_group('Android options') + android.add_option('--android', action='store', type='string', dest='ANDROID_OPTS', default=None, + help='enable building for android, format: --android=,,, example: --android=arm,4.9,26') + +def configure(conf): + if conf.options.ANDROID_OPTS: + android_ndk_path = os.getenv('ANDROID_NDK_HOME') + if not android_ndk_path: + conf.fatal('Set ANDROID_NDK_HOME environment variable pointing to the root of Android NDK!') + + values = conf.options.ANDROID_OPTS.split(',') + if len(values) != 3: + conf.fatal('Invalid --android paramater value!') + + android = Android(android_ndk_path, values[0], values[1], values[2]) + conf.options.ALLOW64 = True # skip pointer length check + conf.options.NO_VGUI = True # skip vgui + conf.environ['CC'] = android.cc() + conf.environ['CXX'] = android.cxx() + conf.environ['LD'] = android.link() + conf.env.CFLAGS += android.cflags() + conf.env.CXXFLAGS += android.cflags() + conf.env.LINKFLAGS += android.ldflags() + # conf.env.ANDROID_OPTS = android + conf.env.DEST_OS2 = 'android' +# else: +# conf.load('compiler_c compiler_cxx') # Use host compiler :) From 463a339a09c4d87c91180f22f41e420848677c0f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 09:48:22 +0300 Subject: [PATCH 160/205] wscript: initial support for building on Android --- engine/wscript | 17 +++++++++++------ mainui | 2 +- wscript | 9 ++++++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/engine/wscript b/engine/wscript index fa1af247..e1ba1026 100644 --- a/engine/wscript +++ b/engine/wscript @@ -21,7 +21,7 @@ def configure(conf): conf.check_cc( lib='rt' ) conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') - else: + elif conf.env.DEST_OS2 != 'android': # Android doesn't need SDL2 conf.load('sdl2') if not conf.env.HAVE_SDL2: conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') @@ -56,16 +56,21 @@ def build(bld): if bld.env.DEST_OS == 'linux': source += bld.path.ant_glob(['platform/linux/*.c']) - # add client files and sdl2 library + if bld.env.HAVE_SDL2: + libs.append('SDL2') + source += bld.path.ant_glob(['platform/sdl/*.c']) + + if bld.env.DEST_OS2 == 'android': + source += bld.path.ant_glob(['platform/android/*.c']) + + # add client files if not bld.env.DEDICATED: - libs.append( 'SDL2' ) source += bld.path.ant_glob([ 'client/*.c', 'client/vgui/*.c', - 'client/avi/*.c', - 'platform/sdl/*.c']) + 'client/avi/*.c']) else: - if(bld.env.DEST_OS == 'linux'): + if bld.env.DEST_OS == 'linux': libs.append('RT') includes = ['common', 'server', 'client', 'client/vgui', '.', '../common', '../pm_shared' ] diff --git a/mainui b/mainui index f288c4b7..5961ebe5 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit f288c4b740289ad96710de772e769cc2a0731b7e +Subproject commit 5961ebe5dfdcdc1052b20836bd6a85acd06ad22e diff --git a/wscript b/wscript index 430338f2..49eedea8 100644 --- a/wscript +++ b/wscript @@ -17,7 +17,7 @@ SUBDIRS = [ 'engine', 'game_launch', 'mainui', 'vgui_support' ] top = '.' def options(opt): - opt.load('compiler_cxx compiler_c') + opt.load('xcompile compiler_cxx compiler_c') if sys.platform == 'win32': opt.load('msvc msvs') @@ -54,10 +54,12 @@ def configure(conf): # TODO: wrapper around bld.stlib, bld.shlib and so on? conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC - conf.load('compiler_cxx compiler_c gitversion') + conf.load('xcompile compiler_c compiler_cxx gitversion') if sys.platform == 'win32': conf.load('msvc msvs') + # print(conf.options.ALLOW64) + conf.env.BIT32_MANDATORY = not conf.options.ALLOW64 conf.env.BIT32_ALLOW64 = conf.options.ALLOW64 conf.load('force_32bit') @@ -105,7 +107,8 @@ def configure(conf): if conf.env.DEST_OS != 'win32': conf.check_cc( lib='m' ) - conf.check_cc( lib='pthread' ) + if conf.env.DEST_OS2 != 'android': + conf.check_cc( lib='pthread' ) else: # Common Win32 libraries # Don't check them more than once, to save time From c3c09546134d5d97b56b819b50cd75b5de023a27 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 10:14:45 +0300 Subject: [PATCH 161/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 5961ebe5..348e477a 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 5961ebe5dfdcdc1052b20836bd6a85acd06ad22e +Subproject commit 348e477a8d404aee8146cf65b22b57bbdc4af73e From b1a06baf831fd5e67dffeb402e5df17550d4e228 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 10:15:08 +0300 Subject: [PATCH 162/205] wscript: link -llog on Android --- engine/wscript | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/wscript b/engine/wscript index e1ba1026..db158206 100644 --- a/engine/wscript +++ b/engine/wscript @@ -21,7 +21,9 @@ def configure(conf): conf.check_cc( lib='rt' ) conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') - elif conf.env.DEST_OS2 != 'android': # Android doesn't need SDL2 + elif conf.env.DEST_OS2 == 'android': # Android doesn't need SDL2 + conf.check_cc(lib='log') + else: conf.load('sdl2') if not conf.env.HAVE_SDL2: conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') @@ -61,7 +63,8 @@ def build(bld): source += bld.path.ant_glob(['platform/sdl/*.c']) if bld.env.DEST_OS2 == 'android': - source += bld.path.ant_glob(['platform/android/*.c']) + libs.append('LOG') + source += bld.path.ant_glob(['platform/android/*.c*']) # add client files if not bld.env.DEDICATED: From e7ddc6d6c58dcde8ee6d52dc15d2a250eac1bd75 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 10:15:44 +0300 Subject: [PATCH 163/205] engine: android: restore dlsym hack for old Android(<5.0) --- engine/platform/android/dlsym-weak.cpp | 94 ++++++++++++ engine/platform/android/dlsym-weak.h | 21 +++ engine/platform/android/lib_android.c | 4 +- engine/platform/android/linker.h | 202 +++++++++++++++++++++++++ 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 engine/platform/android/dlsym-weak.cpp create mode 100644 engine/platform/android/dlsym-weak.h create mode 100644 engine/platform/android/linker.h diff --git a/engine/platform/android/dlsym-weak.cpp b/engine/platform/android/dlsym-weak.cpp new file mode 100644 index 00000000..1aec2368 --- /dev/null +++ b/engine/platform/android/dlsym-weak.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008, 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __ANDROID__ +#include +#include "linker.h" + +static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) { + Elf_Sym* symtab = si->symtab; + const char* strtab = si->strtab; + + for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) { + Elf_Sym* s = symtab + n; + if (strcmp(strtab + s->st_name, name)) continue; + + /* only concern ourselves with global and weak symbol definitions */ + switch (ELF_ST_BIND(s->st_info)) { + case STB_GLOBAL: + case STB_WEAK: + if (s->st_shndx == SHN_UNDEF) { + continue; + } + return s; + } + } + + return NULL; +} + +static unsigned elfhash(const char* _name) { + const unsigned char* name = (const unsigned char*) _name; + unsigned h = 0, g; + + while(*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} + +/* This is used by dlsym(3). It performs symbol lookup only within the + specified soinfo object and not in any of its dependencies. + + TODO: Only looking in the specified soinfo seems wrong. dlsym(3) says + that it should do a breadth first search through the dependency + tree. This agrees with the ELF spec (aka System V Application + Binary Interface) where in Chapter 5 it discuss resolving "Shared + Object Dependencies" in breadth first search order. + */ +Elf_Sym* dlsym_handle_lookup(soinfo* si, const char* name) { + return soinfo_elf_lookup(si, elfhash(name), name); +} + +extern "C" void* dlsym_weak(void* handle, const char* symbol) { + + soinfo* found = NULL; + Elf_Sym* sym = NULL; + found = reinterpret_cast(handle); + sym = dlsym_handle_lookup(found, symbol); + + if (sym != NULL) { + return reinterpret_cast(sym->st_value + found->base/*load_bias*/); + } + __android_log_print(ANDROID_LOG_ERROR, "dlsym-weak", "Failed when looking up %s\n", symbol); + return NULL; +} +#endif diff --git a/engine/platform/android/dlsym-weak.h b/engine/platform/android/dlsym-weak.h new file mode 100644 index 00000000..b7f031c8 --- /dev/null +++ b/engine/platform/android/dlsym-weak.h @@ -0,0 +1,21 @@ +/* +dlsym-weak.h -- custom dlsym() function to override bionic libc bug on Android <5.0 +Copyright (C) 2015-2017 Flying With Gauss + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef DLSYM_WEAH_H +#define DLSYM_WEAK_H + +// ------------ dlsym-weak.cpp ------------ // +void* dlsym_weak(void* handle, const char* symbol); + +#endif diff --git a/engine/platform/android/lib_android.c b/engine/platform/android/lib_android.c index 2beda5e7..4e070e45 100644 --- a/engine/platform/android/lib_android.c +++ b/engine/platform/android/lib_android.c @@ -18,8 +18,8 @@ GNU General Public License for more details. #include "library.h" #include "filesystem.h" #include "server.h" -#include "platform/android/android_lib.h" -#include "platform/android/dlsym_weak.h" // Android < 5.0 +#include "platform/android/lib_android.h" +#include "platform/android/dlsym-weak.h" // Android < 5.0 void *ANDROID_LoadLibrary( const char *dllname ) { diff --git a/engine/platform/android/linker.h b/engine/platform/android/linker.h new file mode 100644 index 00000000..7d361cd0 --- /dev/null +++ b/engine/platform/android/linker.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINKER_H_ +#define _LINKER_H_ +#ifdef __ANDROID__ + +#include +#include +#include +#include + +#include + +// Returns the address of the page containing address 'x'. +#define PAGE_START(x) ((x) & PAGE_MASK) + +// Returns the offset of address 'x' in its page. +#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK) + +// Returns the address of the next page after address 'x', unless 'x' is +// itself at the start of a page. +#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE-1)) + +// Magic shared structures that GDB knows about. + +struct link_map_t { + uintptr_t l_addr; + char* l_name; + uintptr_t l_ld; + link_map_t* l_next; + link_map_t* l_prev; +}; + +// Values for r_debug->state +enum { + RT_CONSISTENT, + RT_ADD, + RT_DELETE +}; +#if 0 +struct r_debug { + int32_t r_version; + link_map_t* r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; +}; +#endif +#define FLAG_LINKED 0x00000001 +#define FLAG_EXE 0x00000004 // The main executable +#define FLAG_LINKER 0x00000010 // The linker itself + +#define SOINFO_NAME_LEN 128 + +typedef void (*linker_function_t)(); + +// Android uses REL for 32-bit but only uses RELA for 64-bit. +#if defined(__LP64__) +#define USE_RELA 1 +#endif + +struct soinfo { + public: + char name[SOINFO_NAME_LEN]; + const Elf_Phdr* phdr; + size_t phnum; + Elf_Addr entry; + Elf_Addr base; + unsigned size; + +#ifndef __LP64__ + uint32_t unused1; // DO NOT USE, maintained for compatibility. +#endif + + Elf_Dyn* dynamic; + +#ifndef __LP64__ + uint32_t unused2; // DO NOT USE, maintained for compatibility + uint32_t unused3; // DO NOT USE, maintained for compatibility +#endif + + soinfo* next; + unsigned flags; + + const char* strtab; + Elf_Sym* symtab; + + size_t nbucket; + size_t nchain; + unsigned* bucket; + unsigned* chain; + +#if !defined(__LP64__) + // This is only used by 32-bit MIPS, but needs to be here for + // all 32-bit architectures to preserve binary compatibility. + unsigned* plt_got; +#endif + +#if defined(USE_RELA) + Elf_Rela* plt_rela; + size_t plt_rela_count; + + Elf_Rela* rela; + size_t rela_count; +#else + Elf_Rel* plt_rel; + size_t plt_rel_count; + + Elf_Rel* rel; + size_t rel_count; +#endif + + linker_function_t* preinit_array; + size_t preinit_array_count; + + linker_function_t* init_array; + size_t init_array_count; + linker_function_t* fini_array; + size_t fini_array_count; + + linker_function_t init_func; + linker_function_t fini_func; + +#if defined(__arm__) + // ARM EABI section used for stack unwinding. + unsigned* ARM_exidx; + size_t ARM_exidx_count; +#elif defined(__mips__) + unsigned mips_symtabno; + unsigned mips_local_gotno; + unsigned mips_gotsym; +#endif + + size_t ref_count; + link_map_t link_map; + + bool constructors_called; + + // When you read a virtual address from the ELF file, add this + // value to get the corresponding address in the process' address space. + Elf_Addr load_bias; + +#if !defined(__LP64__) + bool has_text_relocations; +#endif + bool has_DT_SYMBOLIC; + + void CallConstructors(); + void CallDestructors(); + void CallPreInitConstructors(); + + private: + void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); + void CallFunction(const char* function_name, linker_function_t function); +}; + +extern soinfo libdl_info; + +void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); +soinfo* do_dlopen(const char* name, int flags); +int do_dlclose(soinfo* si); + +Elf_Sym* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start); +soinfo* find_containing_library(const void* addr); + +Elf_Sym* dladdr_find_symbol(soinfo* si, const void* addr); +Elf_Sym* dlsym_handle_lookup(soinfo* si, const char* name); + +void debuggerd_init(); +extern "C" void notify_gdb_of_libraries(); + +char* linker_get_error_buffer(); +size_t linker_get_error_buffer_size(); + +#endif +#endif From 621cdba53c50201435bce451cbdc08b49bc2a236 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Dec 2018 10:22:04 +0300 Subject: [PATCH 164/205] engine: android: restore OpenSL ES audio --- engine/platform/android/snd_opensles.c | 309 +++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 engine/platform/android/snd_opensles.c diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c new file mode 100644 index 00000000..46ba2a7f --- /dev/null +++ b/engine/platform/android/snd_opensles.c @@ -0,0 +1,309 @@ +/* +Copyright (C) 2015 SiPlus, Chasseur de bots + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "common.h" +#if XASH_SOUND == SOUND_OPENSLES +#include +#include "pthread.h" +#include "sound.h" + +extern convar_t *s_primary; +extern dma_t dma; + +static SLObjectItf snddma_android_engine = NULL; +static SLObjectItf snddma_android_outputMix = NULL; +static SLObjectItf snddma_android_player = NULL; +static SLBufferQueueItf snddma_android_bufferQueue; +static SLPlayItf snddma_android_play; + +static pthread_mutex_t snddma_android_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int snddma_android_pos; +static int snddma_android_size; + +static const SLInterfaceID *pSL_IID_ENGINE; +static const SLInterfaceID *pSL_IID_BUFFERQUEUE; +static const SLInterfaceID *pSL_IID_PLAY; +static SLresult SLAPIENTRY (*pslCreateEngine)( + SLObjectItf *pEngine, + SLuint32 numOptions, + const SLEngineOption *pEngineOptions, + SLuint32 numInterfaces, + const SLInterfaceID *pInterfaceIds, + const SLboolean * pInterfaceRequired +); + +void S_Activate( qboolean active ) +{ + if( !dma.initialized ) + return; + + if( active ) + { + memset( dma.buffer, 0, snddma_android_size * 2 ); + (*snddma_android_bufferQueue)->Enqueue( snddma_android_bufferQueue, dma.buffer, snddma_android_size ); + (*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_PLAYING ); + } + else + { + //if( s_globalfocus->integer ) + //return; + (*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_STOPPED ); + (*snddma_android_bufferQueue)->Clear( snddma_android_bufferQueue ); + } +} + +static void SNDDMA_Android_Callback( SLBufferQueueItf bq, void *context ) +{ + uint8_t *buffer2; + + pthread_mutex_lock( &snddma_android_mutex ); + + buffer2 = ( uint8_t * )dma.buffer + snddma_android_size; + (*bq)->Enqueue( bq, buffer2, snddma_android_size ); + memcpy( buffer2, dma.buffer, snddma_android_size ); + memset( dma.buffer, 0, snddma_android_size ); + snddma_android_pos += dma.samples; + + pthread_mutex_unlock( &snddma_android_mutex ); +} + +static const char *SNDDMA_Android_Init( void ) +{ + SLresult result; + + SLEngineItf engine; + + int freq; + + SLDataLocator_BufferQueue sourceLocator; + SLDataFormat_PCM sourceFormat; + SLDataSource source; + + SLDataLocator_OutputMix sinkLocator; + SLDataSink sink; + + SLInterfaceID interfaceID; + SLboolean interfaceRequired; + + int samples; + void *handle = dlopen( "libOpenSLES.so", RTLD_LAZY ); + + if( !handle ) + return "dlopen for libOpenSLES.so"; + + pslCreateEngine = dlsym( handle, "slCreateEngine" ); + + if( !pslCreateEngine ) + return "resolve slCreateEngine"; + + pSL_IID_ENGINE = dlsym( handle, "SL_IID_ENGINE" ); + + if( !pSL_IID_ENGINE ) + return "resolve SL_IID_ENGINE"; + + pSL_IID_PLAY = dlsym( handle, "SL_IID_PLAY" ); + + if( !pSL_IID_PLAY ) + return "resolve SL_IID_PLAY"; + + pSL_IID_BUFFERQUEUE = dlsym( handle, "SL_IID_BUFFERQUEUE" ); + + if( !pSL_IID_BUFFERQUEUE ) + return "resolve SL_IID_BUFFERQUEUE"; + + + result = pslCreateEngine( &snddma_android_engine, 0, NULL, 0, NULL, NULL ); + if( result != SL_RESULT_SUCCESS ) return "slCreateEngine"; + result = (*snddma_android_engine)->Realize( snddma_android_engine, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "engine->Realize"; + result = (*snddma_android_engine)->GetInterface( snddma_android_engine, *pSL_IID_ENGINE, &engine ); + if( result != SL_RESULT_SUCCESS ) return "engine->GetInterface(ENGINE)"; + + result = (*engine)->CreateOutputMix( engine, &snddma_android_outputMix, 0, NULL, NULL ); + if( result != SL_RESULT_SUCCESS ) return "engine->CreateOutputMix"; + result = (*snddma_android_outputMix)->Realize( snddma_android_outputMix, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "outputMix->Realize"; + + freq = SOUND_DMA_SPEED; + sourceLocator.locatorType = SL_DATALOCATOR_BUFFERQUEUE; + sourceLocator.numBuffers = 2; + sourceFormat.formatType = SL_DATAFORMAT_PCM; + sourceFormat.numChannels = 2; // always stereo, because engine supports only stereo + sourceFormat.samplesPerSec = freq * 1000; + sourceFormat.bitsPerSample = 16; // always 16 bit audio + sourceFormat.containerSize = sourceFormat.bitsPerSample; + sourceFormat.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; + sourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + source.pLocator = &sourceLocator; + source.pFormat = &sourceFormat; + + sinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + sinkLocator.outputMix = snddma_android_outputMix; + sink.pLocator = &sinkLocator; + sink.pFormat = NULL; + + interfaceID = *pSL_IID_BUFFERQUEUE; + interfaceRequired = SL_BOOLEAN_TRUE; + + result = (*engine)->CreateAudioPlayer( engine, &snddma_android_player, &source, &sink, 1, &interfaceID, &interfaceRequired ); + if( result != SL_RESULT_SUCCESS ) return "engine->CreateAudioPlayer"; + result = (*snddma_android_player)->Realize( snddma_android_player, SL_BOOLEAN_FALSE ); + if( result != SL_RESULT_SUCCESS ) return "player->Realize"; + result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_BUFFERQUEUE, &snddma_android_bufferQueue ); + if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(BUFFERQUEUE)"; + result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_PLAY, &snddma_android_play ); + if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(PLAY)"; + result = (*snddma_android_bufferQueue)->RegisterCallback( snddma_android_bufferQueue, SNDDMA_Android_Callback, NULL ); + if( result != SL_RESULT_SUCCESS ) return "bufferQueue->RegisterCallback"; + + samples = s_samplecount->value; + if( !samples ) + samples = 4096; + + dma.format.channels = sourceFormat.numChannels; + dma.samples = samples * sourceFormat.numChannels; + dma.format.speed = freq; + snddma_android_size = dma.samples * ( sourceFormat.bitsPerSample >> 3 ); + dma.buffer = Z_Malloc( snddma_android_size * 2 ); + dma.samplepos = 0; + // dma.sampleframes = dma.samples / dma.format.channels; + dma.format.width = 2; + if( !dma.buffer ) return "malloc"; + + //snddma_android_mutex = trap_Mutex_Create(); + + snddma_android_pos = 0; + dma.initialized = true; + + S_Activate( true ); + + return NULL; +} + +qboolean SNDDMA_Init( void *hwnd) +{ + const char *initError; + + Msg( "OpenSL ES audio device initializing...\n" ); + + initError = SNDDMA_Android_Init(); + if( initError ) + { + Msg( S_ERROR "SNDDMA_Init: %s failed.\n", initError ); + SNDDMA_Shutdown(); + return false; + } + + Msg( "OpenSL ES audio initialized.\n" ); + + return true; +} + +int SNDDMA_GetDMAPos( void ) +{ + return snddma_android_pos; +} + +void SNDDMA_Shutdown( void ) +{ + Msg( "Closing OpenSL ES audio device...\n" ); + + if( snddma_android_player ) + { + (*snddma_android_player)->Destroy( snddma_android_player ); + snddma_android_player = NULL; + } + if( snddma_android_outputMix ) + { + (*snddma_android_outputMix)->Destroy( snddma_android_outputMix ); + snddma_android_outputMix = NULL; + } + if( snddma_android_engine ) + { + (*snddma_android_engine)->Destroy( snddma_android_engine ); + snddma_android_engine = NULL; + } + + if( dma.buffer ) + { + Z_Free( dma.buffer ); + dma.buffer = NULL; + } + + //if( snddma_android_mutex ) + //trap_Mutex_Destroy( &snddma_android_mutex ); + + Msg( "OpenSL ES audio device shut down.\n" ); +} + +void SNDDMA_Submit( void ) +{ + pthread_mutex_unlock( &snddma_android_mutex ); +} + +void SNDDMA_BeginPainting( void ) +{ + pthread_mutex_lock( &snddma_android_mutex ); +} + + +/* +============== +SNDDMA_GetSoundtime + +update global soundtime +=============== +*/ +int SNDDMA_GetSoundtime( void ) +{ + static int buffers, oldsamplepos; + int samplepos, fullsamples; + + fullsamples = dma.samples / 2; + + // it is possible to miscount buffers + // if it has wrapped twice between + // calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos(); + + if( samplepos < oldsamplepos ) + { + buffers++; // buffer wrapped + + if( paintedtime > 0x40000000 ) + { + // time to chop things off to avoid 32 bit limits + buffers = 0; + paintedtime = fullsamples; + S_StopAllSounds( true ); + } + } + + oldsamplepos = samplepos; + + return (buffers * fullsamples + samplepos / 2); +} + +void S_PrintDeviceName( void ) +{ + Msg( "Audio: OpenSL\n" ); +} +#endif From 422e511aac3f15635aa9d5a497056f2f72d71fdf Mon Sep 17 00:00:00 2001 From: Josh K Date: Sun, 23 Dec 2018 07:32:13 -0500 Subject: [PATCH 165/205] Added the 'sleeptime' cvar and behavior from the 'Old Engine'. --- engine/common/host.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 0fd6dec7..45f9439f 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -54,6 +54,7 @@ convar_t *host_clientloaded; convar_t *host_limitlocal; convar_t *host_maxfps; convar_t *host_framerate; +convar_t *host_sleeptime; convar_t *con_gamemaps; convar_t *build, *ver; @@ -168,17 +169,18 @@ Host_CheckSleep */ void Host_CheckSleep( void ) { + int sleeptime = host_sleeptime->value; if( Host_IsDedicated() ) { // let the dedicated server some sleep - Sys_Sleep( 1 ); + Sys_Sleep( sleeptime ); } else { if( host.status == HOST_NOFOCUS ) { if( SV_Active() && CL_IsInGame( )) - Sys_Sleep( 1 ); // listenserver + Sys_Sleep( sleeptime ); // listenserver else Sys_Sleep( 20 ); // sleep 20 ms otherwise } else if( host.status == HOST_SLEEP ) @@ -186,6 +188,10 @@ void Host_CheckSleep( void ) // completely sleep in minimized state Sys_Sleep( 20 ); } + else + { + Sys_Sleep( sleeptime ); + } } } @@ -944,6 +950,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa host_maxfps = Cvar_Get( "fps_max", "72", FCVAR_ARCHIVE, "host fps upper limit" ); host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" ); + host_sleeptime = Cvar_Get( "sleeptime", "1", FCVAR_ARCHIVE, "milliseconds to sleep for each frame. higher values reduce fps accuracy" ); host_gameloaded = Cvar_Get( "host_gameloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded game.dll" ); host_clientloaded = Cvar_Get( "host_clientloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded client.dll" ); host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" ); From 626df3673c86c78ef57aee1f814bf8d0fec3b33b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 25 Dec 2018 06:12:01 +0300 Subject: [PATCH 166/205] Fix missing newline after exec userconfig.cfg --- engine/common/con_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 697fa80b..c670c61f 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1249,7 +1249,7 @@ void Host_WriteConfig( void ) if( jlook && ( jlook->state & 1 )) FS_Printf( f, "+jlook\n" ); - FS_Printf( f, "exec userconfig.cfg" ); + FS_Printf( f, "exec userconfig.cfg\n" ); CFG_END( f, "config.cfg" ); } From bf07a9e61e06802ea4cfe1acb41328447abd2dda Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 30 Dec 2018 01:43:07 +0300 Subject: [PATCH 167/205] Apply 4344 update --- common/bspfile.h | 1 + engine/client/cl_qparse.c | 6 +- engine/client/gl_alias.c | 30 ++++++++-- engine/client/gl_beams.c | 2 +- engine/client/gl_rsurf.c | 16 +++-- engine/client/gl_sprite.c | 5 ++ engine/common/common.h | 2 + engine/common/console.c | 19 ++++-- engine/common/filesystem.c | 1 + engine/common/imagelib/imagelib.h | 1 + engine/common/imagelib/img_utils.c | 93 ++++++++++++++++++++---------- engine/common/imagelib/img_wad.c | 27 +++++---- engine/common/mod_bmodel.c | 3 + 13 files changed, 148 insertions(+), 58 deletions(-) diff --git a/common/bspfile.h b/common/bspfile.h index 91d5c1fd..d5ecb458 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -132,6 +132,7 @@ BRUSH MODELS #define TEX_WORLD_LUXELS BIT( 1 ) // alternative lightmap matrix will be used (luxels per world units instead of luxels per texels) #define TEX_AXIAL_LUXELS BIT( 2 ) // force world luxels to axial positive scales #define TEX_EXTRA_LIGHTMAP BIT( 3 ) // bsp31 legacy - using 8 texels per luxel instead of 16 texels per luxel +#define TEX_SCROLL BIT( 6 ) // Doom special FX // ambient sound types enum diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c index f669ff2c..bcdf49ab 100644 --- a/engine/client/cl_qparse.c +++ b/engine/client/cl_qparse.c @@ -201,7 +201,11 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) i = MSG_ReadLong( msg ); if( i != PROTOCOL_VERSION_QUAKE ) - Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION_QUAKE ); + { + Con_Printf( "\n" S_ERROR "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION_QUAKE ); + CL_StopPlayback(); + Host_AbortCurrentFrame(); + } cl.maxclients = MSG_ReadByte( msg ); gametype = MSG_ReadByte( msg ); diff --git a/engine/client/gl_alias.c b/engine/client/gl_alias.c index 35cfe0dc..e84fbbec 100644 --- a/engine/client/gl_alias.c +++ b/engine/client/gl_alias.c @@ -433,12 +433,15 @@ rgbdata_t *Mod_CreateSkinData( model_t *mod, byte *data, int width, int height ) skin.palette = (byte *)&clgame.palette; skin.size = width * height; - for( i = 0; i < skin.width * skin.height; i++ ) + if( !Image_CustomPalette() ) { - if( data[i] > 224 && data[i] != 255 ) + for( i = 0; i < skin.width * skin.height; i++ ) { - SetBits( skin.flags, IMAGE_HAS_LUMA ); - break; + if( data[i] > 224 && data[i] != 255 ) + { + SetBits( skin.flags, IMAGE_HAS_LUMA ); + break; + } } } @@ -478,11 +481,14 @@ rgbdata_t *Mod_CreateSkinData( model_t *mod, byte *data, int width, int height ) void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size ) { string name, lumaname; + string checkname; rgbdata_t *pic; Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum ); Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum ); - pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); + Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum ); + if( !FS_FileExists( checkname, false ) || ( pic = FS_LoadImage( checkname, NULL, 0 )) == NULL ) + pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); m_pAliasHeader->gl_texturenum[skinnum][0] = m_pAliasHeader->gl_texturenum[skinnum][1] = @@ -1445,7 +1451,17 @@ void R_DrawAliasModel( cl_entity_t *e ) GL_Bind( GL_TEXTURE0, tr.whiteTexture ); else if( pinfo != NULL && pinfo->textures[skin] != 0 ) GL_Bind( GL_TEXTURE0, pinfo->textures[skin] ); // FIXME: allow remapping for skingroups someday - else GL_Bind( GL_TEXTURE0, m_pAliasHeader->gl_texturenum[skin][anim] ); + else + { + GL_Bind( GL_TEXTURE0, m_pAliasHeader->gl_texturenum[skin][anim] ); + + if( FBitSet( R_GetTexture( m_pAliasHeader->gl_texturenum[skin][anim] )->flags, TF_HAS_ALPHA )) + { + pglEnable( GL_ALPHA_TEST ); + pglAlphaFunc( GL_GREATER, 0.0f ); + tr.blend = 1.0f; + } + } pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); @@ -1472,6 +1488,8 @@ void R_DrawAliasModel( cl_entity_t *e ) R_AliasDrawLightTrace( e ); pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + pglAlphaFunc( GL_GREATER, DEFAULT_ALPHATEST ); + pglDisable( GL_ALPHA_TEST ); if( r_shadows.value ) { diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index a0b67d76..741e2a2c 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -2003,7 +2003,7 @@ void CL_ReadLineFile_f( void ) count++; - if( !R_BeamPoints( p1, p2, modelIndex, 99999, 2, 0, 255, 0, 0, 0, 255.0f, 0.0f, 0.0f )) + if( !R_BeamPoints( p1, p2, modelIndex, 0, 2, 0, 255, 0, 0, 0, 255.0f, 0.0f, 0.0f )) { if( !model || model->type != mod_sprite ) Con_Printf( S_ERROR "failed to load \"%s\"!\n", DEFAULT_LASERBEAM_PATH ); diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 7b69ae21..58239209 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -723,12 +723,20 @@ void DrawGLPoly( glpoly_t *p, float xScale, float yScale ) if( p->flags & SURF_CONVEYOR ) { - gl_texture_t *texture; - float flConveyorSpeed; + float flConveyorSpeed = 0.0f; float flRate, flAngle; + gl_texture_t *texture; - flConveyorSpeed = (e->curstate.rendercolor.g<<8|e->curstate.rendercolor.b) / 16.0f; - if( e->curstate.rendercolor.r ) flConveyorSpeed = -flConveyorSpeed; + if( CL_IsQuakeCompatible() && RI.currententity == clgame.entities ) + { + // same as doom speed + flConveyorSpeed = -35.0f; + } + else + { + flConveyorSpeed = (e->curstate.rendercolor.g<<8|e->curstate.rendercolor.b) / 16.0f; + if( e->curstate.rendercolor.r ) flConveyorSpeed = -flConveyorSpeed; + } texture = R_GetTexture( glState.currentTextures[glState.activeTMU] ); flRate = abs( flConveyorSpeed ) / (float)texture->srcWidth; diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index 67b5fa9e..93f1ae31 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -189,6 +189,11 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui psprite->radius = pinq1->boundingradius; psprite->synctype = pinq1->synctype; + // LordHavoc: hack to allow sprites to be non-fullbright + for( i = 0; i < MAX_QPATH && mod->name[i]; i++ ) + if( mod->name[i] == '!' ) + psprite->texFormat = SPR_ALPHTEST; + mod->mins[0] = mod->mins[1] = -pinq1->bounds[0] * 0.5f; mod->maxs[0] = mod->maxs[1] = pinq1->bounds[0] * 0.5f; mod->mins[2] = -pinq1->bounds[1] * 0.5f; diff --git a/engine/common/common.h b/engine/common/common.h index 4dc4f60e..3b44bd76 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -620,7 +620,9 @@ void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end, int void Image_PaletteTranslate( byte *palSrc, int top, int bottom, int pal_size ); void Image_SetForceFlags( uint flags ); // set image force flags on loading size_t Image_DXTGetLinearSize( int type, int width, int height, int depth ); +qboolean Image_CustomPalette( void ); void Image_ClearForceFlags( void ); +void Image_CheckPaletteQ1( void ); /* ======================================================================== diff --git a/engine/common/console.c b/engine/common/console.c index 59e39853..5ce4b85b 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -728,22 +728,31 @@ draw console single character */ static int Con_DrawGenericChar( int x, int y, int number, rgba_t color ) { - int width, height; - float s1, t1, s2, t2; - wrect_t *rc; + int width, height; + float s1, t1, s2, t2; + gl_texture_t *glt; + wrect_t *rc; number &= 255; if( !con.curFont || !con.curFont->valid ) return 0; -// if( number < 32 ) return 0; if( y < -con.curFont->charHeight ) return 0; rc = &con.curFont->fontRc[number]; + glt = R_GetTexture( con.curFont->hFontTexture ); + width = glt->srcWidth; + height = glt->srcHeight; - pglColor4ubv( color ); + if( !width || !height ) + return con.curFont->charWidths[number]; + + // don't apply color to fixed fonts it's already colored + if( con.curFont->type != FONT_FIXED || glt->format == GL_LUMINANCE8_ALPHA8 ) + pglColor4ubv( color ); + else pglColor4ub( 255, 255, 255, color[3] ); R_GetTextureParms( &width, &height, con.curFont->hFontTexture ); // calc rectangle diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index fa293745..edd89adf 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -1362,6 +1362,7 @@ void FS_LoadGameInfo( const char *rootfolder ) SI.GameInfo = SI.games[i]; FS_Rescan(); // create new filesystem + Image_CheckPaletteQ1 (); Host_InitDecals (); // reload decals } diff --git a/engine/common/imagelib/imagelib.h b/engine/common/imagelib/imagelib.h index 951103ce..0cc6717a 100644 --- a/engine/common/imagelib/imagelib.h +++ b/engine/common/imagelib/imagelib.h @@ -95,6 +95,7 @@ typedef struct imglib_s byte *tempbuffer; // for convert operations int cmd_flags; // global imglib flags int force_flags; // override cmd_flags + qboolean custom_palette; // custom palette was installed } imglib_t; /* diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 775b041f..934dfb54 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -177,6 +177,16 @@ byte *Image_Copy( size_t size ) return out; } +/* +================= +Image_CustomPalette +================= +*/ +qboolean Image_CustomPalette( void ) +{ + return image.custom_palette; +} + /* ================= Image_CheckFlag @@ -311,6 +321,58 @@ void Image_SetPalette( const byte *pal, uint *d_table ) } } +static void Image_ConvertPalTo24bit( rgbdata_t *pic ) +{ + byte *pal32, *pal24; + byte *converted; + int i; + + if( pic->type == PF_INDEXED_24 ) + return; // does nothing + + pal24 = converted = Mem_Malloc( host.imagepool, 768 ); + pal32 = pic->palette; + + for( i = 0; i < 256; i++, pal24 += 3, pal32 += 4 ) + { + pal24[0] = pal32[0]; + pal24[1] = pal32[1]; + pal24[2] = pal32[2]; + } + + Mem_Free( pic->palette ); + pic->palette = converted; + pic->type = PF_INDEXED_24; +} + +void Image_CopyPalette32bit( void ) +{ + if( image.palette ) return; // already created ? + image.palette = Mem_Malloc( host.imagepool, 1024 ); + memcpy( image.palette, image.d_currentpal, 1024 ); +} + +void Image_CheckPaletteQ1( void ) +{ + rgbdata_t *pic = FS_LoadImage( DEFAULT_INTERNAL_PALETTE, NULL, 0 ); + + if( pic && pic->size == 1024 ) + { + Image_ConvertPalTo24bit( pic ); + if( Image_ComparePalette( pic->palette ) == PAL_CUSTOM ) + { + image.d_rendermode = LUMP_NORMAL; + Con_DPrintf( "custom quake palette detected\n" ); + Image_SetPalette( pic->palette, d_8toQ1table ); + d_8toQ1table[255] = 0; // 255 is transparent + image.custom_palette = true; + q1palette_init = true; + } + } + + if( pic ) FS_FreeImage( pic ); +} + void Image_GetPaletteQ1( void ) { if( !q1palette_init ) @@ -376,37 +438,6 @@ void Image_GetPaletteLMP( const byte *pal, int rendermode ) } } -static void Image_ConvertPalTo24bit( rgbdata_t *pic ) -{ - byte *pal32, *pal24; - byte *converted; - int i; - - if( pic->type == PF_INDEXED_24 ) - return; // does nothing - - pal24 = converted = Mem_Malloc( host.imagepool, 768 ); - pal32 = pic->palette; - - for( i = 0; i < 256; i++, pal24 += 3, pal32 += 4 ) - { - pal24[0] = pal32[0]; - pal24[1] = pal32[1]; - pal24[2] = pal32[2]; - } - - Mem_Free( pic->palette ); - pic->palette = converted; - pic->type = PF_INDEXED_24; -} - -void Image_CopyPalette32bit( void ) -{ - if( image.palette ) return; // already created ? - image.palette = Mem_Malloc( host.imagepool, 1024 ); - memcpy( image.palette, image.d_currentpal, 1024 ); -} - void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end, int pal_size ) { float r, g, b; diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 954e3c8c..308eafac 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -261,7 +261,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize ) return Image_LoadPAL( name, buffer, filesize ); // id software trick (image without header) - if( image.hint != IL_HINT_HL && Q_stristr( name, "conchars" )) + if( Q_stristr( name, "conchars" ) && filesize == 16384 ) { image.width = image.height = 128; rendermode = LUMP_QUAKE1; @@ -293,10 +293,14 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize ) { int numcolors; - if( fin[0] == 255 ) + for( i = 0; i < pixels; i++ ) { - image.flags |= IMAGE_HAS_ALPHA; - rendermode = LUMP_MASKED; + if( fin[i] == 255 ) + { + image.flags |= IMAGE_HAS_ALPHA; + rendermode = LUMP_MASKED; + break; + } } pal = fin + pixels; numcolors = *(short *)pal; @@ -418,14 +422,17 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) hl_texture = false; // check for luma and alpha pixels - for( i = 0; i < image.width * image.height; i++ ) + if( !image.custom_palette ) { - if( fin[i] > 224 && fin[i] != 255 ) + for( i = 0; i < image.width * image.height; i++ ) { - // don't apply luma to water surfaces because they have no lightmap - if( mip.name[0] != '*' && mip.name[0] != '!' ) - image.flags |= IMAGE_HAS_LUMA; - break; + if( fin[i] > 224 && fin[i] != 255 ) + { + // don't apply luma to water surfaces because they have no lightmap + if( mip.name[0] != '*' && mip.name[0] != '!' ) + image.flags |= IMAGE_HAS_LUMA; + break; + } } } diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index deec5856..4073efa2 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2195,6 +2195,9 @@ static void Mod_LoadSurfaces( dbspmodel_t *bmod ) if( !Q_strncmp( tex->name, "scroll", 6 )) SetBits( out->flags, SURF_CONVEYOR ); + if( FBitSet( out->texinfo->flags, TEX_SCROLL )) + SetBits( out->flags, SURF_CONVEYOR ); + // g-cont. added a combined conveyor-transparent if( !Q_strncmp( tex->name, "{scroll", 7 )) SetBits( out->flags, SURF_CONVEYOR|SURF_TRANSPARENT ); From 245533deface0065a7d026b57344b5b34b4d660b Mon Sep 17 00:00:00 2001 From: iZarif Date: Sun, 30 Dec 2018 21:25:41 +0400 Subject: [PATCH 168/205] wscript: SUBDIRS loop refactor --- wscript | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wscript b/wscript index 49eedea8..ba1f2d65 100644 --- a/wscript +++ b/wscript @@ -142,5 +142,4 @@ def configure(conf): conf.setenv('') def build(bld): - for i in SUBDIRS: - bld.recurse(SUBDIRS) + bld.recurse(SUBDIRS) From 908082097f7b8cd9167be8efcc76c617291e5642 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 7 Jan 2019 03:59:56 +0300 Subject: [PATCH 169/205] wscript: error on implicit function declaration by default on any build type. Remove redutant Options import. --- wscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wscript b/wscript index ba1f2d65..73568c79 100644 --- a/wscript +++ b/wscript @@ -3,7 +3,7 @@ # a1batross, mittorn, 2018 from __future__ import print_function -from waflib import Logs, Options +from waflib import Logs import sys import os @@ -79,7 +79,7 @@ def configure(conf): compiler_c_cxx_flags = { 'common': { 'msvc': ['/D_USING_V110_SDK71_'], - 'default': ['-g'] + 'default': ['-g', '-Werror=implicit-function-declaration'] }, 'release': { 'msvc': ['/Zi', '/O2'], From 80fc7209edf2512fec4c0ecb2f084181d23676ee Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 7 Jan 2019 04:07:01 +0300 Subject: [PATCH 170/205] sound: move related function declarations to platform header. Remove unused. --- engine/client/s_main.c | 1 + engine/client/sound.h | 16 ---------------- engine/common/common.h | 1 - engine/platform/platform.h | 16 ++++++++++++++++ engine/platform/sdl/events.c | 4 ++-- engine/platform/sdl/s_sdl.c | 15 +++------------ 6 files changed, 22 insertions(+), 31 deletions(-) diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 8e1d3dac..e8345344 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -19,6 +19,7 @@ GNU General Public License for more details. #include "con_nprint.h" #include "gl_local.h" #include "pm_local.h" +#include "platform/platform.h" #define SND_CLIP_DISTANCE 1000.0f diff --git a/engine/client/sound.h b/engine/client/sound.h index 7a2194db..9aade622 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -237,22 +237,6 @@ typedef struct int source; // may be game, menu, etc } bg_track_t; -/* -==================================================================== - - SYSTEM SPECIFIC FUNCTIONS - -==================================================================== -*/ -// initializes cycling through a DMA buffer and returns information on it -qboolean SNDDMA_Init( void *hInst ); -int SNDDMA_GetSoundtime( void ); -void SNDDMA_Shutdown( void ); -void SNDDMA_BeginPainting( void ); -void SNDDMA_Submit( void ); -void SNDDMA_LockSound( void ); -void SNDDMA_UnlockSound( void ); - //==================================================================== #define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS) diff --git a/engine/common/common.h b/engine/common/common.h index bdc349fd..301b140a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -1080,7 +1080,6 @@ void Cmd_Null_f( void ); // soundlib shared exports qboolean S_Init( void ); void S_Shutdown( void ); -void S_Activate( qboolean active ); void S_StopSound( int entnum, int channel, const char *soundname ); int S_GetCurrentStaticSounds( soundlist_t *pout, int size ); void S_StopBackgroundTrack( void ); diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 551f05a8..9506fbef 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -90,6 +90,22 @@ int R_MaxVideoModes(); vidmode_t*R_GetVideoMode( int num ); void* GL_GetProcAddress( const char *name ); // RenderAPI requirement +/* +============================================================================== + AUDIO INPUT/OUTPUT + +============================================================================== +*/ +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init( void *hInst ); +int SNDDMA_GetSoundtime( void ); +void SNDDMA_Shutdown( void ); +void SNDDMA_BeginPainting( void ); +void SNDDMA_Submit( void ); +void SNDDMA_Activate( qboolean active ); // pause audio +// void SNDDMA_PrintDeviceName( void ); // unused +// void SNDDMA_LockSound( void ); // unused +// void SNDDMA_UnlockSound( void ); // unused #endif // PLATFORM_H diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index ee8523b0..d7f1d70a 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -408,7 +408,7 @@ static void SDLash_EventFilter( SDL_Event *event ) IN_ActivateMouse(true); if( snd_mute_losefocus->value ) { - S_Activate( true ); + SNDDMA_Activate( true ); } host.force_draw_version = true; host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; @@ -427,7 +427,7 @@ static void SDLash_EventFilter( SDL_Event *event ) IN_DeactivateMouse(); if( snd_mute_losefocus->value ) { - S_Activate( false ); + SNDDMA_Activate( false ); } host.force_draw_version = true; host.force_draw_version_time = host.realtime + 2; diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 7908678c..0c68a64b 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -14,6 +14,7 @@ GNU General Public License for more details. */ #include "common.h" +#include "platform/platform.h" #if XASH_SOUND == SOUND_SDL #include "sound.h" @@ -238,23 +239,13 @@ void SNDDMA_Shutdown( void ) /* =========== -S_PrintDeviceName -=========== -*/ -void S_PrintDeviceName( void ) -{ - Msg( "Audio: SDL (driver: %s)\n", SDL_GetCurrentAudioDriver( ) ); -} - -/* -=========== -S_Activate +SNDDMA_Activate Called when the main window gains or loses focus. The window have been destroyed and recreated between a deactivate and an activate. =========== */ -void S_Activate( qboolean active ) +void SNDDMA_Activate( qboolean active ) { SDL_PauseAudioDevice( sdl_dev, !active ); } From 9935e2c8d3d06e9e96ff3405edf5a76cd50f7e5f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 7 Jan 2019 04:09:55 +0300 Subject: [PATCH 171/205] sound: sdl: set pulseaudio environment variables regardless of target OS, because PA is crossplatform --- engine/platform/sdl/s_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 0c68a64b..a9e16faf 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -74,10 +74,10 @@ qboolean SNDDMA_Init( void *hInst ) return false; } -#ifdef __linux__ + // even if we don't have PA + // we still can safely set env variables setenv( "PULSE_PROP_application.name", GI->title, 1 ); setenv( "PULSE_PROP_media.role", "game", 1 ); -#endif memset( &desired, 0, sizeof( desired ) ); desired.freq = SOUND_DMA_SPEED; From ac77bab96760d84fca3c86622d6c416b1e801189 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 7 Jan 2019 04:17:40 +0300 Subject: [PATCH 172/205] sound: opensles: same fix applied for OpenSLES/Android backend --- engine/platform/android/snd_opensles.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index 46ba2a7f..1b47716b 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" +#include "platform.h" #if XASH_SOUND == SOUND_OPENSLES #include #include "pthread.h" @@ -50,7 +51,7 @@ static SLresult SLAPIENTRY (*pslCreateEngine)( const SLboolean * pInterfaceRequired ); -void S_Activate( qboolean active ) +void SNDDMA_Activate( qboolean active ) { if( !dma.initialized ) return; @@ -301,9 +302,4 @@ int SNDDMA_GetSoundtime( void ) return (buffers * fullsamples + samplepos / 2); } - -void S_PrintDeviceName( void ) -{ - Msg( "Audio: OpenSL\n" ); -} #endif From ed05519c60cfe085f61dc6d9d7d674dad7adeb6b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Jan 2019 23:20:35 +0300 Subject: [PATCH 173/205] host: rearrange conditionals in Host_CalcFPS for better readability --- engine/common/host.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 45f9439f..d464bd25 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -170,6 +170,9 @@ Host_CheckSleep void Host_CheckSleep( void ) { int sleeptime = host_sleeptime->value; + + if( host.frametime !) + if( Host_IsDedicated() ) { // let the dedicated server some sleep @@ -443,9 +446,12 @@ double Host_CalcFPS( void ) { double fps = 0.0; - // NOTE: we should play demos with same fps as it was recorded + if( Host_IsDedicated() ) + { + fps = sys_ticrate.value; + } #ifndef XASH_DEDICATED - if( CL_IsPlaybackDemo() || CL_IsRecordDemo( )) + else if( CL_IsPlaybackDemo() || CL_IsRecordDemo( )) // NOTE: we should play demos with same fps as it was recorded { fps = CL_GetDemoFramerate(); } @@ -454,18 +460,11 @@ double Host_CalcFPS( void ) fps = host_maxfps->value; } else -#endif - if( Host_IsDedicated() ) - { - fps = sys_ticrate.value; - } - else { fps = host_maxfps->value; fps = bound( MIN_FPS, fps, MAX_FPS ); } -#ifndef XASH_DEDICATED // probably left part of this condition is redundant :-) if( host.type != HOST_DEDICATED && Host_IsLocalGame( ) && !CL_IsTimeDemo( )) { From 5fda638edf19a339acebde0404639fb3943e162b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 02:48:56 +0300 Subject: [PATCH 174/205] host: fix compiling error --- engine/common/host.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index d464bd25..24f968cc 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -171,8 +171,6 @@ void Host_CheckSleep( void ) { int sleeptime = host_sleeptime->value; - if( host.frametime !) - if( Host_IsDedicated() ) { // let the dedicated server some sleep From c8035c1adf743d452a855423c04d6a348dea2265 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 02:53:41 +0300 Subject: [PATCH 175/205] host: fix implicit declaration on Win32 builds --- engine/common/host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/common/host.c b/engine/common/host.c index 24f968cc..14465290 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -983,6 +983,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa if( Host_IsDedicated() ) { #ifdef _WIN32 + void Wcon_InitConsoleCommands( void ) // con_win.c Wcon_InitConsoleCommands (); #endif From 0409107ee2df4c7aa0db0bd30a8d85fe3ef2d542 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 02:56:24 +0300 Subject: [PATCH 176/205] scripts: appimage: fix generating extras.pak --- engine/common/host.c | 1 - engine/common/system.h | 1 + scripts/build_appimage.sh | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 14465290..24f968cc 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -983,7 +983,6 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa if( Host_IsDedicated() ) { #ifdef _WIN32 - void Wcon_InitConsoleCommands( void ) // con_win.c Wcon_InitConsoleCommands (); #endif diff --git a/engine/common/system.h b/engine/common/system.h index 713a6fe2..f00a4c0b 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -117,6 +117,7 @@ int Sys_LogFileNo( void ); // con_win.c // #ifdef _WIN32 +void Wcon_InitConsoleCommands( void ); void Wcon_ShowConsole( qboolean show ); void Wcon_Print( const char *pMsg ); void Wcon_Init( void ); diff --git a/scripts/build_appimage.sh b/scripts/build_appimage.sh index 874eb4c6..4b57b000 100755 --- a/scripts/build_appimage.sh +++ b/scripts/build_appimage.sh @@ -6,12 +6,11 @@ APPDIR=$APP-i386.AppDir mkdir -p $APPDIR # Generate extras.pak -python3 scripts/makepak.py extras.pak +python3 scripts/makepak.py xash-extras/ $APPDIR/extras.pak # Copy all needed files cp SDL2_linux/lib/libSDL2-2.0.so.0 $APPDIR/ cp vgui-dev/lib/vgui.so $APPDIR/ -cp extras.pak $APPDIR/extras.pak cp build/engine/libxash.so \ build/mainui/libmenu.so \ build/vgui_support/libvgui_support.so \ From 196c311113e3e8c695c4c74a594a4f1d643f9f06 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 03:13:03 +0300 Subject: [PATCH 177/205] wcon: fix implicit declaration --- engine/common/system.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/common/system.h b/engine/common/system.h index f00a4c0b..87e42903 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -119,13 +119,14 @@ int Sys_LogFileNo( void ); #ifdef _WIN32 void Wcon_InitConsoleCommands( void ); void Wcon_ShowConsole( qboolean show ); -void Wcon_Print( const char *pMsg ); void Wcon_Init( void ); void Wcon_CreateConsole( void ); void Wcon_DestroyConsole( void ); void Wcon_DisableInput( void ); void Wcon_Clear( void ); char *Wcon_Input( void ); +void Wcon_WinPrint( const char *pMsg ); +void Wcon_RegisterHotkeys( void ); #endif // text messages From 7205f2d4faf075946371d766b0019472784a3337 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 13 Jan 2019 18:19:35 +0500 Subject: [PATCH 178/205] sound: sdl: use SDL_setenv for crossplatform --- engine/platform/sdl/s_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index a9e16faf..1dfd362b 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -76,8 +76,8 @@ qboolean SNDDMA_Init( void *hInst ) // even if we don't have PA // we still can safely set env variables - setenv( "PULSE_PROP_application.name", GI->title, 1 ); - setenv( "PULSE_PROP_media.role", "game", 1 ); + SDL_setenv( "PULSE_PROP_application.name", GI->title, 1 ); + SDL_setenv( "PULSE_PROP_media.role", "game", 1 ); memset( &desired, 0, sizeof( desired ) ); desired.freq = SOUND_DMA_SPEED; From 0ca0dc973107ed236ca5e251ec0c8431f64ecb79 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 13 Jan 2019 18:26:00 +0500 Subject: [PATCH 179/205] travis: fix packaging mingw build --- scripts/build_mingw_engine.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/build_mingw_engine.sh b/scripts/build_mingw_engine.sh index 78b7a6d6..50d5c445 100755 --- a/scripts/build_mingw_engine.sh +++ b/scripts/build_mingw_engine.sh @@ -9,11 +9,12 @@ export CXX="ccache i686-w64-mingw32-g++" export CFLAGS="-static-libgcc -no-pthread" export CXXFLAGS="-static-libgcc -static-libstdc++" export WINRC="i686-w64-mingw32-windres" +rm -rf build # clean build directory ./waf configure --sdl2=$TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32/ --disable-vgui --build-type=debug --verbose || die # can't compile VGUI support on MinGW, due to differnet C++ ABI ./waf build -j2 --verbose || die cp $TRAVIS_BUILD_DIR/SDL2_mingw/i686-w64-mingw32//bin/SDL2.dll . # Install SDL2 cp vgui_support_bin/vgui_support.dll . -cp build-mingw/engine/xash.dll . -cp build-mingw/mainui/menu.dll . -cp build-mingw/game_launch/xash3d.exe . +cp build/engine/xash.dll . +cp build/mainui/menu.dll . +cp build/game_launch/xash3d.exe . 7z a -t7z $TRAVIS_BUILD_DIR/xash3d-mingw.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on *.dll *.exe From 9da413b2cc3629d77539b7bb2b4004c052bc78fb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 13 Jan 2019 16:46:12 +0300 Subject: [PATCH 180/205] waf: add clang compilation database support --- scripts/waflib/clang_compilation_database.py | 85 ++++++++++++++++++++ wscript | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 scripts/waflib/clang_compilation_database.py diff --git a/scripts/waflib/clang_compilation_database.py b/scripts/waflib/clang_compilation_database.py new file mode 100644 index 00000000..4d9b5e27 --- /dev/null +++ b/scripts/waflib/clang_compilation_database.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Christoph Koke, 2013 + +""" +Writes the c and cpp compile commands into build/compile_commands.json +see http://clang.llvm.org/docs/JSONCompilationDatabase.html + +Usage: + + def configure(conf): + conf.load('compiler_cxx') + ... + conf.load('clang_compilation_database') +""" + +import sys, os, json, shlex, pipes +from waflib import Logs, TaskGen, Task + +Task.Task.keep_last_cmd = True + +@TaskGen.feature('c', 'cxx') +@TaskGen.after_method('process_use') +def collect_compilation_db_tasks(self): + "Add a compilation database entry for compiled tasks" + try: + clang_db = self.bld.clang_compilation_database_tasks + except AttributeError: + clang_db = self.bld.clang_compilation_database_tasks = [] + self.bld.add_post_fun(write_compilation_database) + + tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y) + for task in getattr(self, 'compiled_tasks', []): + if isinstance(task, tup): + clang_db.append(task) + +def write_compilation_database(ctx): + "Write the clang compilation database as JSON" + database_file = ctx.bldnode.make_node('compile_commands.json') + Logs.info('Build commands will be stored in %s', database_file.path_from(ctx.path)) + try: + root = json.load(database_file) + except IOError: + root = [] + clang_db = dict((x['file'], x) for x in root) + for task in getattr(ctx, 'clang_compilation_database_tasks', []): + try: + cmd = task.last_cmd + except AttributeError: + continue + directory = getattr(task, 'cwd', ctx.variant_dir) + f_node = task.inputs[0] + filename = os.path.relpath(f_node.abspath(), directory) + entry = { + "directory": directory, + "arguments": cmd, + "file": filename, + } + clang_db[filename] = entry + root = list(clang_db.values()) + database_file.write(json.dumps(root, indent=2)) + +# Override the runnable_status function to do a dummy/dry run when the file doesn't need to be compiled. +# This will make sure compile_commands.json is always fully up to date. +# Previously you could end up with a partial compile_commands.json if the build failed. +for x in ('c', 'cxx'): + if x not in Task.classes: + continue + + t = Task.classes[x] + + def runnable_status(self): + def exec_command(cmd, **kw): + pass + + run_status = self.old_runnable_status() + if run_status == Task.SKIP_ME: + setattr(self, 'old_exec_command', getattr(self, 'exec_command', None)) + setattr(self, 'exec_command', exec_command) + self.run() + setattr(self, 'exec_command', getattr(self, 'old_exec_command', None)) + return run_status + + setattr(t, 'old_runnable_status', getattr(t, 'runnable_status', None)) + setattr(t, 'runnable_status', runnable_status) diff --git a/wscript b/wscript index 73568c79..353b272b 100644 --- a/wscript +++ b/wscript @@ -54,7 +54,7 @@ def configure(conf): # TODO: wrapper around bld.stlib, bld.shlib and so on? conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC - conf.load('xcompile compiler_c compiler_cxx gitversion') + conf.load('xcompile compiler_c compiler_cxx gitversion clang_compilation_database') if sys.platform == 'win32': conf.load('msvc msvs') From a297a0b5ade47adf6745eac9c927f3904ec4b253 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 13 Jan 2019 16:46:50 +0300 Subject: [PATCH 181/205] wscript: fix git commit hash --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 353b272b..78003a23 100644 --- a/wscript +++ b/wscript @@ -130,7 +130,7 @@ def configure(conf): else: conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX - conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="{0}"'.format(conf.env.GIT_VERSION if conf.env.GITVERSION else 'notset')) + conf.env.append_unique('DEFINES', 'XASH_BUILD_COMMIT="{0}"'.format(conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset')) for i in SUBDIRS: conf.setenv(i, conf.env) # derive new env from global one From 1e5f12a0ae4bf595a7cc35d31b60164ee8ab2e05 Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 25 Jan 2019 20:37:11 +0700 Subject: [PATCH 182/205] Fix crash on broken model --- engine/client/gl_rlight.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/gl_rlight.c b/engine/client/gl_rlight.c index a60485de..0ca73a54 100644 --- a/engine/client/gl_rlight.c +++ b/engine/client/gl_rlight.c @@ -104,7 +104,7 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node ) msurface_t *surf; int i; - if( node->contents < 0 ) + if( !node || node->contents < 0 ) return; dist = PlaneDiff( light->origin, node->plane ); @@ -487,4 +487,4 @@ colorVec R_LightPoint( const vec3_t p0 ) VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); return R_LightVec( p0, p1, NULL, NULL ); -} \ No newline at end of file +} From 6a02a571c1e5e3ff3d2e79c5063ce7708461809d Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 25 Jan 2019 20:53:08 +0700 Subject: [PATCH 183/205] Legacy protocol compatibility (cl_legacymode) --- engine/client/cl_events.c | 4 +- engine/client/cl_frame.c | 7 +- engine/client/cl_main.c | 30 +- engine/client/cl_parse.c | 813 ++++++++++++++++++++++++++++++++++++- engine/client/cl_tent.c | 4 +- engine/client/client.h | 4 + engine/common/net_buffer.c | 2 +- engine/common/net_encode.c | 71 ++-- engine/common/protocol.h | 14 + pm_shared/pm_movevars.h | 6 +- 10 files changed, 908 insertions(+), 47 deletions(-) diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index 2d9bb4f3..75c0516b 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -393,7 +393,7 @@ void CL_ParseEvent( sizebuf_t *msg ) event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS ); if( MSG_ReadOneBit( msg )) - packet_index = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + packet_index = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_ENTITY_BITS:MAX_ENTITY_BITS ); else packet_index = -1; if( MSG_ReadOneBit( msg )) @@ -492,4 +492,4 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa args.bparam2 = bparam2; CL_QueueEvent( flags, eventindex, delay, &args ); -} \ No newline at end of file +} diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index be1f5a73..2d2f4e70 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -721,7 +721,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) CL_WriteDemoJumpTime(); // sentinel count. save it for debug checking - count = ( MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1 ); + count = cls.legacymode?MSG_ReadWord( msg ) : ( MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1 ); newframe = &cl.frames[cl.parsecountmod]; // allocate parse entities @@ -795,9 +795,8 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) while( 1 ) { - newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); - if( newnum == LAST_EDICT ) break; // end of packet entities - + newnum = cls.legacymode ? MSG_ReadWord( msg ) : MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + if( newnum == (cls.legacymode?0:LAST_EDICT) ) break; // end of packet entities if( MSG_CheckOverflow( msg )) Host_Error( "CL_ParsePacketEntities: overflow\n" ); player = CL_IsPlayerIndex( newnum ); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f9992548..4e35d462 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -69,6 +69,7 @@ convar_t *cl_lw; convar_t *cl_charset; convar_t *cl_trace_messages; convar_t *hud_utf8; +convar_t *cl_legacymode; // // userinfo @@ -1015,7 +1016,11 @@ void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); + /// TODO: identification for legacy mode + if( cls.legacymode ) + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", 48, Q_atoi(qport), cls.challenge, cls.userinfo ); + else + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); cls.timestart = Sys_DoubleTime(); } @@ -1098,9 +1103,10 @@ void CL_CheckForResend( void ) Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry ); - if( cl_test_bandwidth.value ) + if( !cls.legacymode && cl_test_bandwidth.value ) Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size ); - else Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" ); + else + Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" ); } resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index ) @@ -1371,6 +1377,8 @@ This is also called on Host_Error, so it shouldn't cause any errors */ void CL_Disconnect( void ) { + cls.legacymode = cl_legacymode->value; + if( cls.state == ca_disconnected ) return; @@ -1437,12 +1445,15 @@ void CL_LocalServers_f( void ) Con_Printf( "Scanning for servers on the local network area...\n" ); NET_Config( true ); // allow remote - + + if( cls.state == ca_disconnected ) + cls.legacymode = cl_legacymode->value; + // send a broadcast packet adr.type = NA_BROADCAST; adr.port = MSG_BigShort( PORT_SERVER ); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION ); } #define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" @@ -1460,6 +1471,9 @@ void CL_InternetServers_f( void ) NET_Config( true ); // allow remote + if( cls.state == ca_disconnected ) + cls.legacymode = cl_legacymode->value; + Con_Printf( "Scanning for servers on the internet area...\n" ); Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version @@ -1935,7 +1949,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( clgame.request_type == NET_REQUEST_GAMEUI ) { NET_Config( true ); // allow remote - Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION ); } } @@ -2016,6 +2030,7 @@ void CL_ReadNetMessage( void ) // run special handler for quake demos if( cls.demoplayback == DEMO_QUAKE1 ) CL_ParseQuakeMessage( &net_message, true ); + else if( cls.legacymode ) CL_ParseLegacyServerMessage( &net_message, true ); else CL_ParseServerMessage( &net_message, true ); cl.send_reply = true; } @@ -2617,6 +2632,7 @@ void CL_InitLocal( void ) hud_scale = Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" ); Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" ); cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" ); + cl_legacymode = Cvar_Get( "cl_legacymode", "0", 0, "legacy mode compatibility" ); Cvar_Get( "lastdemo", "", FCVAR_ARCHIVE, "last played demo" ); // these two added to shut up CS 1.5 about 'unknown' commands @@ -2676,6 +2692,8 @@ void CL_InitLocal( void ) Cmd_AddCommand ("reconnect", CL_Reconnect_f, "reconnect to current level" ); Cmd_AddCommand ("rcon", CL_Rcon_f, "sends a command to the server console (rcon_password and rcon_address required)" ); + Cmd_AddCommand ("precache", CL_LegacyPrecache_f, "legacy server compatibility" ); + } //============================================================================ diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index fee43b53..955e223b 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -353,6 +353,7 @@ void CL_ParseStaticEntity( sizebuf_t *msg ) R_AddEfrags( ent ); // add link } + /* ================== CL_WeaponAnim @@ -1127,7 +1128,7 @@ void CL_ParseClientData( sizebuf_t *msg ) if( !MSG_ReadOneBit( msg )) break; // read the weapon idx - idx = MSG_ReadUBitLong( msg, MAX_WEAPON_BITS ); + idx = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_WEAPON_BITS:MAX_WEAPON_BITS ); MSG_ReadWeaponData( msg, &from_wd[idx], &to_wd[idx], cl.mtime[0] ); } @@ -1297,16 +1298,40 @@ void CL_RegisterUserMessage( sizebuf_t *msg ) int svc_num, size; svc_num = MSG_ReadByte( msg ); - size = MSG_ReadWord( msg ); + size = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); pszName = MSG_ReadString( msg ); // important stuff - if( size == 0xFFFF ) size = -1; + if( size == (cls.legacymode?0xFF:0xFFFF) ) size = -1; svc_num = bound( 0, svc_num, 255 ); CL_LinkUserMessage( pszName, svc_num, size ); } +/* +================ +CL_RegisterUserMessage + +register new user message or update existing +================ +*/ +/* +void CL_LegacyRegisterUserMessage( sizebuf_t *msg ) +{ + char *pszName; + int svc_num, size; + + svc_num = MSG_ReadByte( msg ); + size = MSG_ReadByte( msg ); + pszName = MSG_ReadString( msg ); + + // important stuff + if( size == 0xFF ) size = -1; + svc_num = bound( 0, svc_num, 255 ); + + CL_LinkUserMessage( pszName, svc_num, size ); +} +*/ /* ================ CL_UpdateUserinfo @@ -1933,7 +1958,12 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num ) iSize = clgame.msg[i].size; // message with variable sizes receive an actual size as first byte - if( iSize == -1 ) iSize = MSG_ReadWord( msg ); + if( iSize == -1 ) iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); + if( iSize >= MAX_USERMSG_LENGTH ) + { + Msg("WTF??? %d %d\n", i, svc_num ); + return; + } // parse user message into buffer MSG_ReadBytes( msg, pbuf, iSize ); @@ -2271,3 +2301,778 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) } } } + +/* +================== +CL_ParseBaseline +================== +*/ +void CL_LegacyParseBaseline( sizebuf_t *msg ) +{ + int i, newnum; + entity_state_t nullstate; + qboolean player; + cl_entity_t *ent; + + Delta_InitClient (); // finalize client delta's + + memset( &nullstate, 0, sizeof( nullstate )); + + newnum = MSG_ReadWord( msg ); + player = CL_IsPlayerIndex( newnum ); + + if( newnum >= clgame.maxEntities ) + Host_Error( "CL_AllocEdict: no free edicts\n" ); + + ent = CL_EDICT_NUM( newnum ); + memset( &ent->prevstate, 0, sizeof( ent->prevstate )); + ent->index = newnum; + + MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f ); + +} + +/* +================== +CL_ParseServerData +================== +*/ +void CL_ParseLegacyServerData( sizebuf_t *msg ) +{ + string gamefolder; + qboolean background; + int i; + + Con_Reportf( "Legacy serverdata packet received.\n" ); + + cls.timestart = Sys_DoubleTime(); + + cls.demowaiting = false; // server is changed + //clgame.load_sequence++; // now all hud sprites are invalid + + // wipe the client_t struct + if( !cls.changelevel && !cls.changedemo ) + CL_ClearState (); + cls.state = ca_connected; + + // parse protocol version number + i = MSG_ReadLong( msg ); + //cls.serverProtocol = i; + + if( i != 48 ) + Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); + + cl.servercount = MSG_ReadLong( msg ); + cl.checksum = MSG_ReadLong( msg ); + cl.playernum = MSG_ReadByte( msg ); + cl.maxclients = MSG_ReadByte( msg ); + clgame.maxEntities = MSG_ReadWord( msg ); + clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 ); + clgame.maxModels = 512; + Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); + Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); + background = MSG_ReadOneBit( msg ); + Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_STRING ); + host.features = (uint)MSG_ReadLong( msg ); + + // Re-init hud video, especially if we changed game directories + clgame.dllFuncs.pfnVidInit(); + + if( Con_FixedFont( )) + { + // seperate the printfs so the server message can have a color + Con_Print( "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n" ); + Con_Print( va( "%c%s\n\n", 2, clgame.maptitle )); + } + + // multiplayer game? + if( cl.maxclients > 1 ) + { + // allow console in multiplayer games + host.allow_console = true; + + // loading user settings + CSCR_LoadDefaultCVars( "user.scr" ); + + if( r_decals->value > mp_decals.value ) + Cvar_SetValue( "r_decals", mp_decals.value ); + } + else Cvar_Reset( "r_decals" ); + + // set the background state + if( cls.demoplayback && ( cls.demonum != -1 )) + { + // re-init mouse + host.mouse_visible = false; + cl.background = true; + } + else cl.background = background; + + if( cl.background ) // tell the game parts about background state + Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); + else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); + + if( !cls.changelevel ) + { + // continue playing if we are changing level + S_StopBackgroundTrack (); + } + + if( !cls.changedemo ) + UI_SetActiveMenu( cl.background ); + else if( !cls.demoplayback ) + Key_SetKeyDest( key_menu ); + + // don't reset cursor in background mode + if( cl.background ) + IN_MouseRestorePos(); + + // will be changed later + cl.viewentity = cl.playernum + 1; + gameui.globals->maxClients = cl.maxclients; + Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle )); + + if( !cls.changelevel && !cls.changedemo ) + CL_InitEdicts (); // re-arrange edicts + + // get splash name + if( cls.demoplayback && ( cls.demonum != -1 )) + Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, glState.wideScreen ? "16x9" : "4x3" )); + else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, glState.wideScreen ? "16x9" : "4x3" )); + Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar + + if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background ) + { + if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true )) + Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen + cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime) + } + + for( i = 0; i < MAX_CLIENTS; i++ ) + COM_ClearCustomizationList( &cl.players[i].customdata, true ); + CL_CreateCustomizationList(); + + memset( &clgame.movevars, 0, sizeof( clgame.movevars )); + memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); + memset( &clgame.centerPrint, 0, sizeof( clgame.centerPrint )); + cl.video_prepped = false; + cl.audio_prepped = false; +} + +/* +================== +CL_ParseStaticEntity + +static client entity +================== +*/ +void CL_LegacyParseStaticEntity( sizebuf_t *msg ) +{ + int i; + entity_state_t state; + cl_entity_t *ent; + + memset( &state, 0, sizeof( state )); + state.modelindex = MSG_ReadShort( msg ); + state.sequence = MSG_ReadByte( msg ); + state.frame = MSG_ReadByte( msg ); + state.colormap = MSG_ReadWord( msg ); + state.skin = MSG_ReadByte( msg ); + + for( i = 0; i < 3; i++ ) + { + state.origin[i] = MSG_ReadCoord( msg ); + state.angles[i] = MSG_ReadBitAngle( msg, 16 ); + } + + state.rendermode = MSG_ReadByte( msg ); + + if( state.rendermode != kRenderNormal ) + { + state.renderamt = MSG_ReadByte( msg ); + state.rendercolor.r = MSG_ReadByte( msg ); + state.rendercolor.g = MSG_ReadByte( msg ); + state.rendercolor.b = MSG_ReadByte( msg ); + state.renderfx = MSG_ReadByte( msg ); + } + + i = clgame.numStatics; + if( i >= MAX_STATIC_ENTITIES ) + { + Con_Printf( S_ERROR "MAX_STATIC_ENTITIES limit exceeded!\n" ); + return; + } + + ent = &clgame.static_entities[i]; + clgame.numStatics++; + + // all states are same + ent->baseline = ent->curstate = ent->prevstate = state; + ent->index = 0; // static entities doesn't has the numbers + + // statics may be respawned in game e.g. for demo recording + if( cls.state == ca_connected || cls.state == ca_validate ) + ent->trivial_accept = INVALID_HANDLE; + + // setup the new static entity + VectorCopy( ent->curstate.origin, ent->origin ); + VectorCopy( ent->curstate.angles, ent->angles ); + ent->model = CL_ModelHandle( state.modelindex ); + ent->curstate.framerate = 1.0f; + CL_ResetLatchedVars( ent, true ); + + if( ent->curstate.rendermode == kRenderNormal && ent->model != NULL ) + { + // auto 'solid' faces + if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && Host_IsQuakeCompatible( )) + { + ent->curstate.rendermode = kRenderTransAlpha; + ent->curstate.renderamt = 255; + } + } + + R_AddEfrags( ent ); // add link +} + + + +void CL_LegacyParseSoundPacket( sizebuf_t *msg, qboolean is_ambient ) +{ + vec3_t pos; + int chan, sound; + float volume, attn; + int flags, pitch, entnum; + sound_t handle = 0; + + flags = MSG_ReadWord( msg ); + if( flags & SND_LEGACY_LARGE_INDEX ) + { + sound = MSG_ReadWord( msg ); + flags &= ~SND_LEGACY_LARGE_INDEX; + } + else + sound = MSG_ReadByte( msg ); + chan = MSG_ReadByte( msg ); + + if( FBitSet( flags, SND_VOLUME )) + volume = (float)MSG_ReadByte( msg ) / 255.0f; + else volume = VOL_NORM; + + if( FBitSet( flags, SND_ATTENUATION )) + attn = (float)MSG_ReadByte( msg ) / 64.0f; + else attn = ATTN_NONE; + + if( FBitSet( flags, SND_PITCH )) + pitch = MSG_ReadByte( msg ); + else pitch = PITCH_NORM; + + // entity reletive + entnum = MSG_ReadWord( msg ); + + // positioned in space + MSG_ReadVec3Coord( msg, pos ); + + if( FBitSet( flags, SND_SENTENCE )) + { + char sentenceName[32]; + + //if( FBitSet( flags, SND_SEQUENCE )) + //Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS ); + //else + Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); + + handle = S_RegisterSound( sentenceName ); + } + else handle = cl.sound_index[sound]; // see precached sound + + if( !cl.audio_prepped ) + return; // too early + + // g-cont. sound and ambient sound have only difference with channel + if( is_ambient ) + { + S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags ); + } + else + { + S_StartSound( pos, entnum, chan, handle, volume, attn, pitch, flags ); + } +} +/* +================ +CL_PrecacheSound + +prceache sound from server +================ +*/ +void CL_LegacyPrecacheSound( sizebuf_t *msg ) +{ + int soundIndex; + + soundIndex = MSG_ReadUBitLong( msg, MAX_SOUND_BITS ); + + if( soundIndex < 0 || soundIndex >= MAX_SOUNDS ) + Host_Error( "CL_PrecacheSound: bad soundindex %i\n", soundIndex ); + + Q_strncpy( cl.sound_precache[soundIndex], MSG_ReadString( msg ), sizeof( cl.sound_precache[0] )); + + // when we loading map all resources is precached sequentially + //if( !cl.audio_prepped ) return; + + cl.sound_index[soundIndex] = S_RegisterSound( cl.sound_precache[soundIndex] ); +} + +void CL_LegacyPrecacheModel( sizebuf_t *msg ) +{ + int modelIndex; + string model; + + modelIndex = MSG_ReadUBitLong( msg, MAX_LEGACY_MODEL_BITS ); + + if( modelIndex < 0 || modelIndex >= MAX_MODELS ) + Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex ); + + Q_strncpy( model, MSG_ReadString( msg ), MAX_STRING ); + //Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] )); + + // when we loading map all resources is precached sequentially + //if( !cl.video_prepped ) return; + if( modelIndex == 1 && !cl.worldmodel ) + { + CL_ClearWorld (); + + cl.models[modelIndex] = cl.worldmodel = Mod_LoadWorld( model, true ); + return; + + } + + //Mod_RegisterModel( cl.model_precache[modelIndex], modelIndex ); + + cl.models[modelIndex] = Mod_ForName( model, false, false ); + cl.nummodels = Q_max( cl.nummodels, modelIndex ); +} + +void CL_LegacyPrecacheEvent( sizebuf_t *msg ) +{ + int eventIndex; + + eventIndex = MSG_ReadUBitLong( msg, MAX_EVENT_BITS ); + + if( eventIndex < 0 || eventIndex >= MAX_EVENTS ) + Host_Error( "CL_PrecacheEvent: bad eventindex %i\n", eventIndex ); + + Q_strncpy( cl.event_precache[eventIndex], MSG_ReadString( msg ), sizeof( cl.event_precache[0] )); + + // can be set now + CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex ); +} + + +void CL_LegacyUpdateUserinfo( sizebuf_t *msg ) +{ + int slot, id = 0; + qboolean active; + player_info_t *player; + + slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS ); + + if( slot >= MAX_CLIENTS ) + Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" ); + + //id = MSG_ReadLong( msg ); // unique user ID + player = &cl.players[slot]; + active = MSG_ReadOneBit( msg ) ? true : false; + + if( active ) + { + Q_strncpy( player->userinfo, MSG_ReadString( msg ), sizeof( player->userinfo )); + Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); + Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); + player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); + player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); + player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" )); + //MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey )); + + if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t )); + } + else memset( player, 0, sizeof( *player )); +} + +/* +===================== +CL_ParseLegacyServerMessage + +dispatch messages +===================== +*/ +void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) +{ + size_t bufStart, playerbytes; + int cmd, param1, param2; + int old_background; + const char *s; + + cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame + CL_Parse_Debug( true ); // begin parsing + + if( normal_message ) + { + // assume no entity/player update this packet + if( cls.state == ca_active ) + { + cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].valid = false; + cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = false; + } + else + { + CL_ResetFrame( &cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK] ); + } + } + + // parse the message + while( 1 ) + { + if( MSG_CheckOverflow( msg )) + { + Host_Error( "CL_ParseServerMessage: overflow!\n" ); + return; + } + + // mark start position + bufStart = MSG_GetNumBytesRead( msg ); + + // end of message (align bits) + if( MSG_GetNumBitsLeft( msg ) < 8 ) + break; + + cmd = MSG_ReadServerCmd( msg ); + + // record command for debugging spew on parse problem + CL_Parse_RecordCommand( cmd, bufStart ); + + // other commands + switch( cmd ) + { + case svc_bad: + Host_Error( "svc_bad\n" ); + break; + case svc_nop: + // this does nothing + break; + case svc_disconnect: + CL_Drop (); + Host_AbortCurrentFrame (); + break; + case svc_legacy_event: + CL_ParseEvent( msg ); + cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_legacy_changing: + old_background = cl.background; + if( MSG_ReadOneBit( msg )) + { + cls.changelevel = true; + S_StopAllSounds( true ); + + Con_Printf( "Server changing, reconnecting\n" ); + + if( cls.demoplayback ) + { + SCR_BeginLoadingPlaque( cl.background ); + cls.changedemo = true; + } + + CL_ClearState (); + CL_InitEdicts (); // re-arrange edicts + } + else Con_Printf( "Server disconnected, reconnecting\n" ); + + if( cls.demoplayback ) + { + cl.background = (cls.demonum != -1) ? true : false; + cls.state = ca_connected; + } + else + { + // g-cont. local client skip the challenge + if( SV_Active( )) + cls.state = ca_disconnected; + else cls.state = ca_connecting; + cl.background = old_background; + cls.connect_time = MAX_HEARTBEAT; + } + break; + case svc_setview: + CL_ParseViewEntity( msg ); + break; + case svc_sound: + CL_LegacyParseSoundPacket( msg, false ); + cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_legacy_ambientsound: + CL_LegacyParseSoundPacket( msg, true ); + cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; + + break; + case svc_time: + CL_ParseServerTime( msg ); + break; + case svc_print: + Con_Printf( "%s", MSG_ReadString( msg )); + break; + case svc_stufftext: + s = MSG_ReadString( msg ); +#ifdef HACKS_RELATED_HLMODS + // dsiable Cry Of Fear antisave protection + if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS ) + break; // too early +#endif + if( !Q_strcmp(s, "cmd getresourcelist\n") ) + Cbuf_AddText("cmd continueloading\n"); + else + { + Con_Reportf( "Stufftext: %s", s ); + Cbuf_AddText( s ); + } + break; + case svc_setangle: + CL_ParseSetAngle( msg ); + break; + case svc_serverdata: + Cbuf_Execute(); // make sure any stuffed commands are done + CL_ParseLegacyServerData( msg ); + break; + case svc_lightstyle: + CL_ParseLightStyle( msg ); + break; + case svc_updateuserinfo: + CL_LegacyUpdateUserinfo( msg ); + break; + case svc_deltatable: + Delta_ParseTableField( msg ); + break; + case svc_clientdata: + CL_ParseClientData( msg ); + cl.frames[cl.parsecountmod].graphdata.client += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_resource: + CL_ParseResource( msg ); + break; + case svc_pings: + CL_UpdateUserPings( msg ); + break; + case svc_particle: + CL_ParseParticles( msg ); + break; + case svc_restoresound: + CL_ParseRestoreSoundPacket( msg ); + cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_spawnstatic: + CL_ParseStaticEntity( msg ); + break; + case svc_event_reliable: + CL_ParseReliableEvent( msg ); + cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_spawnbaseline: + CL_LegacyParseBaseline( msg ); + break; + case svc_temp_entity: + CL_ParseTempEntity( msg ); + cl.frames[cl.parsecountmod].graphdata.tentities += MSG_GetNumBytesRead( msg ) - bufStart; + break; + case svc_setpause: + cl.paused = ( MSG_ReadOneBit( msg ) != 0 ); + break; + case svc_signonnum: + CL_ParseSignon( msg ); + break; + case svc_centerprint: + CL_CenterPrint( MSG_ReadString( msg ), 0.25f ); + break; + case svc_intermission: + cl.intermission = 1; + break; + case svc_legacy_modelindex: + CL_LegacyPrecacheModel( msg ); + + break; + case svc_legacy_soundindex: + CL_LegacyPrecacheSound( msg ); + break; + case svc_cdtrack: + param1 = MSG_ReadByte( msg ); + param1 = bound( 1, param1, MAX_CDTRACKS ); // tracknum + param2 = MSG_ReadByte( msg ); + param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum + S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0, false ); + break; + case svc_restore: + CL_ParseRestore( msg ); + break; + case svc_legacy_eventindex: + //CL_ParseFinaleCutscene( msg, 3 ); + CL_LegacyPrecacheEvent(msg); + break; + case svc_weaponanim: + param1 = MSG_ReadByte( msg ); // iAnim + param2 = MSG_ReadByte( msg ); // body + CL_WeaponAnim( param1, param2 ); + break; + case svc_bspdecal: + CL_ParseStaticDecal( msg ); + break; + case svc_roomtype: + param1 = MSG_ReadShort( msg ); + Cvar_SetValue( "room_type", param1 ); + break; + case svc_addangle: + CL_ParseAddAngle( msg ); + break; + case svc_usermessage: + CL_RegisterUserMessage( msg ); + break; + case svc_packetentities: + playerbytes = CL_ParsePacketEntities( msg, false ); + cl.frames[cl.parsecountmod].graphdata.players += playerbytes; + cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes; + break; + case svc_deltapacketentities: + playerbytes = CL_ParsePacketEntities( msg, true ); + cl.frames[cl.parsecountmod].graphdata.players += playerbytes; + cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes; + break; + case svc_legacy_chokecount: + { + int i, j; + i = MSG_ReadByte( msg ); + j = cls.netchan.incoming_acknowledged - 1; + for( ; i > 0 && j > cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP; j-- ) + { + if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 ) + { + cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0; + i--; + } + } + break; + } + //cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = true; + //cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0; + break; + case svc_resourcelist: + CL_ParseResourceList( msg ); + break; + case svc_deltamovevars: + CL_ParseMovevars( msg ); + break; + case svc_resourcerequest: + CL_ParseResourceRequest( msg ); + break; + case svc_customization: + CL_ParseCustomization( msg ); + break; + case svc_crosshairangle: + CL_ParseCrosshairAngle( msg ); + break; + case svc_soundfade: + CL_ParseSoundFade( msg ); + break; + case svc_filetxferfailed: + CL_ParseFileTransferFailed( msg ); + break; + case svc_hltv: + CL_ParseHLTV( msg ); + break; + case svc_director: + CL_ParseDirector( msg ); + break; + case svc_voiceinit: + CL_ParseVoiceInit( msg ); + break; + case svc_voicedata: + CL_ParseVoiceData( msg ); + break; + case svc_resourcelocation: + CL_ParseResLocation( msg ); + break; + case svc_querycvarvalue: + CL_ParseCvarValue( msg ); + break; + case svc_querycvarvalue2: + CL_ParseCvarValue2( msg ); + break; + default: + CL_ParseUserMessage( msg, cmd ); + cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart; + break; + } + } + + cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - cls.starting_count; + CL_Parse_Debug( false ); // done + + // we don't know if it is ok to save a demo message until + // after we have parsed the frame + if( !cls.demoplayback ) + { + if( cls.demorecording && !cls.demowaiting ) + { + CL_WriteDemoMessage( false, cls.starting_count, msg ); + } + else if( cls.state != ca_active ) + { + CL_WriteDemoMessage( true, cls.starting_count, msg ); + } + } +} + +void CL_LegacyPrecache_f( void ) +{ + int spawncount, i; + model_t *mod; + + if( !cls.legacymode ) + return; + + spawncount = Q_atoi( Cmd_Argv( 1 )); + + Con_Printf( "Setting up renderer...\n" ); + + // load tempent sprites (glowshell, muzzleflashes etc) + CL_LoadClientSprites (); + + // invalidate all decal indexes + memset( cl.decal_index, 0, sizeof( cl.decal_index )); + cl.video_prepped = true; + cl.audio_prepped = true; + if( clgame.entities ) + clgame.entities->model = cl.worldmodel; + + // tell rendering system we have a new set of models. + R_NewMap (); + + CL_SetupOverviewParams(); + + if( clgame.drawFuncs.R_NewMap != NULL ) + clgame.drawFuncs.R_NewMap(); + + // release unused SpriteTextures + for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ ) + { + if( mod->needload == NL_UNREFERENCED && COM_CheckString( mod->name )) + Mod_UnloadSpriteModel( mod ); + } + +// Mod_FreeUnused (); + + if( host_developer.value <= DEV_NONE ) + Con_ClearNotify(); // clear any lines of console text + + // done with all resources, issue prespawn command. + // Include server count in case server disconnects and changes level during d/l + MSG_BeginClientCmd( &cls.netchan.message, clc_stringcmd ); + MSG_WriteString( &cls.netchan.message, va( "begin %i", spawncount )); + cls.signon = SIGNONS; +} diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 20d548e2..d9314195 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2024,7 +2024,7 @@ void CL_ParseTempEntity( sizebuf_t *msg ) { sizebuf_t buf; byte pbuf[256]; - int iSize = MSG_ReadWord( msg ); + int iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); int type, color, count, flags; int decalIndex, modelIndex, entityIndex; float scale, life, frameRate, vel, random; @@ -3124,4 +3124,4 @@ void CL_ClearEffects( void ) CL_ClearViewBeams (); CL_ClearParticles (); CL_ClearLightStyles (); -} \ No newline at end of file +} diff --git a/engine/client/client.h b/engine/client/client.h index 18fcd300..522d5268 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -661,6 +661,7 @@ typedef struct file_t *demoheader; // contain demo startup info in case we record a demo on this level qboolean internetservers_wait; // internetservers is waiting for dns request qboolean internetservers_pending; // internetservers is waiting for dns request + qboolean legacymode; // one-way 48 protocol compatibility } client_static_t; #ifdef __cplusplus @@ -873,6 +874,9 @@ _inline cl_entity_t *CL_EDICT_NUM( int n ) // cl_parse.c // void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ); +void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ); +void CL_LegacyPrecache_f( void ); + void CL_ParseTempEntity( sizebuf_t *msg ); void CL_StartResourceDownloading( const char *pszMessage, qboolean bCustom ); qboolean CL_DispatchUserMessage( const char *pszName, int iSize, void *pbuf ); diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index b6355403..abba581c 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -645,7 +645,7 @@ qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes ) char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine ) { - static char string[2048]; + static char string[4096]; int l = 0, c; do diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 043baf10..556e9bec 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -81,6 +81,10 @@ static const delta_field_t pm_fields[] = { PHYS_DEF( skyvec_z ) }, { PHYS_DEF( fog_settings ) }, { PHYS_DEF( wateralpha ) }, +{ PHYS_DEF( skydir_x ) }, +{ PHYS_DEF( skydir_y ) }, +{ PHYS_DEF( skydir_z ) }, +{ PHYS_DEF( skyangle ) }, { NULL }, }; @@ -511,12 +515,23 @@ void Delta_ParseTableField( sizebuf_t *msg ) tableIndex = MSG_ReadUBitLong( msg, 4 ); dt = Delta_FindStructByIndex( tableIndex ); - - Assert( dt != NULL ); + if( !dt ) + Host_Error( "Delta_ParseTableField: not initialized" ); nameIndex = MSG_ReadUBitLong( msg, 8 ); // read field name index - Assert( nameIndex >= 0 && nameIndex < dt->maxFields ); + if( !( nameIndex >= 0 && nameIndex < dt->maxFields ) ) + { + Con_Reportf( "Delta_ParseTableField: wrong nameIndex %d for table %s, ignoring\n", nameIndex, dt->pName ); + MSG_ReadUBitLong( msg, 10 ); + MSG_ReadUBitLong( msg, 5 ) + 1; + if( MSG_ReadOneBit( msg )) + MSG_ReadFloat( msg ); + if( MSG_ReadOneBit( msg )) + MSG_ReadFloat( msg ); + return; + } pName = dt->pInfo[nameIndex].name; + flags = MSG_ReadUBitLong( msg, 10 ); bits = MSG_ReadUBitLong( msg, 5 ) + 1; @@ -1600,7 +1615,7 @@ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, f *to = *from; - if( !MSG_ReadOneBit( msg )) + if( !cls.legacymode && !MSG_ReadOneBit( msg )) return; // we have no changes // process fields @@ -1839,29 +1854,31 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state Host_Error( "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType ); } - if( MSG_ReadOneBit( msg )) - baseline_offset = MSG_ReadSBitLong( msg, 7 ); - - if( baseline_offset != 0 ) + if( !cls.legacymode ) { - if( delta_type == DELTA_STATIC ) - { - int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset )); - from = &clgame.static_entities[backup].baseline; - } - else if( baseline_offset > 0 ) - { - int backup = cls.next_client_entities - baseline_offset; - from = &cls.packet_entities[backup % cls.num_client_entities]; - } - else - { - baseline_offset = abs( baseline_offset ); - if( baseline_offset < cl.instanced_baseline_count ) - from = &cl.instanced_baseline[baseline_offset]; - } - } + if( MSG_ReadOneBit( msg )) + baseline_offset = MSG_ReadSBitLong( msg, 7 ); + if( baseline_offset != 0 ) + { + if( delta_type == DELTA_STATIC ) + { + int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset )); + from = &clgame.static_entities[backup].baseline; + } + else if( baseline_offset > 0 ) + { + int backup = cls.next_client_entities - baseline_offset; + from = &cls.packet_entities[backup % cls.num_client_entities]; + } + else + { + baseline_offset = abs( baseline_offset ); + if( baseline_offset < cl.instanced_baseline_count ) + from = &cl.instanced_baseline[baseline_offset]; + } + } + } // g-cont. probably is redundant *to = *from; @@ -1869,7 +1886,7 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state to->entityType = MSG_ReadUBitLong( msg, 2 ); to->number = number; - if( FBitSet( to->entityType, ENTITY_BEAM )) + if( cls.legacymode?(to->entityType == ENTITY_BEAM):FBitSet(to->entityType, ENTITY_BEAM) ) { dt = Delta_FindStruct( "custom_entity_state_t" ); } @@ -2004,4 +2021,4 @@ void Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber ) return; dt->pFields[fieldNumber].bInactive = true; -} \ No newline at end of file +} diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 1bff7a55..6564b571 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -243,4 +243,18 @@ GNU General Public License for more details. extern const char *svc_strings[svc_lastmsg+1]; extern const char *clc_strings[clc_lastmsg+1]; +// legacy protocol definitons +#define PROTOCOL_LEGACY_VERSION 48 +#define svc_legacy_modelindex 31 // [index][modelpath] +#define svc_legacy_soundindex 28 // [index][soundpath] +#define svc_legacy_eventindex 34 // [index][eventname] +#define svc_legacy_ambientsound 29 +#define svc_legacy_chokecount 42 // old client specified count, new just sends svc_choke +#define svc_legacy_event 27 // playback event queue +#define svc_legacy_changing 3 // changelevel by server request + +#define SND_LEGACY_LARGE_INDEX (1<<2) // a send sound as short +#define MAX_LEGACY_ENTITY_BITS 12 +#define MAX_LEGACY_WEAPON_BITS 5 +#define MAX_LEGACY_MODEL_BITS 11 #endif//NET_PROTOCOL_H diff --git a/pm_shared/pm_movevars.h b/pm_shared/pm_movevars.h index 4cf2ba3f..713a7f89 100644 --- a/pm_shared/pm_movevars.h +++ b/pm_shared/pm_movevars.h @@ -43,8 +43,12 @@ struct movevars_s int features; // engine features that shared across network int fog_settings; // Global fog settings (packed color+density) float wateralpha; // World water alpha 1.0 - solid 0.0 - transparent + float skydir_x; // skybox rotate direction + float skydir_y; // + float skydir_z; // + float skyangle; // skybox rotate angle }; extern movevars_t movevars; -#endif \ No newline at end of file +#endif From 75643895ef4221aa67351231604f538a7a7f8ed4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 02:36:34 +0300 Subject: [PATCH 184/205] vgui_support: switch to free miniutl --- .gitmodules | 3 + vgui_support/miniutl | 1 + vgui_support/utlmemory.h | 368 ----------- vgui_support/utlrbtree.h | 1289 -------------------------------------- vgui_support/utlvector.h | 605 ------------------ vgui_support/vgui_main.h | 2 - vgui_support/wscript | 4 +- 7 files changed, 6 insertions(+), 2266 deletions(-) create mode 160000 vgui_support/miniutl delete mode 100644 vgui_support/utlmemory.h delete mode 100644 vgui_support/utlrbtree.h delete mode 100644 vgui_support/utlvector.h diff --git a/.gitmodules b/.gitmodules index f275c17e..80f6b447 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "mainui"] path = mainui url = https://github.com/FWGS/mainui_cpp +[submodule "vgui_support/miniutl"] + path = vgui_support/miniutl + url = https://github.com/FWGS/miniutl diff --git a/vgui_support/miniutl b/vgui_support/miniutl new file mode 160000 index 00000000..c4d1446a --- /dev/null +++ b/vgui_support/miniutl @@ -0,0 +1 @@ +Subproject commit c4d1446a973acf80885ab5c0ca0207eb24beca32 diff --git a/vgui_support/utlmemory.h b/vgui_support/utlmemory.h deleted file mode 100644 index 027a3bd6..00000000 --- a/vgui_support/utlmemory.h +++ /dev/null @@ -1,368 +0,0 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// $Header: $ -// $NoKeywords: $ -// -// A growable memory class. -//============================================================================= - -#ifndef UTLMEMORY_H -#define UTLMEMORY_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "port.h" -#include -#ifdef NO_STL -template -void *operator new(size_t count, T *ptr) { -return ptr; -} -#elif defined _WIN32 -#include -#else -#include -#endif -#include -#include -//----------------------------------------------------------------------------- -// Methods to invoke the constructor, copy constructor, and destructor -//----------------------------------------------------------------------------- - -template -inline void Construct( T* pMemory ) -{ - new( pMemory ) T; -} - -template -inline void CopyConstruct( T* pMemory, T const& src ) -{ - new( pMemory ) T(src); -} - -template -inline void Destruct( T* pMemory ) -{ - pMemory->~T(); - -#ifdef _DEBUG - memset( pMemory, 0xDD, sizeof(T) ); -#endif -} - -#pragma warning (disable:4100) -#pragma warning (disable:4514) - -//----------------------------------------------------------------------------- -// The CUtlMemory class: -// A growable memory class which doubles in size by default. -//----------------------------------------------------------------------------- -template< class T > -class CUtlMemory -{ -public: - // constructor, destructor - CUtlMemory( int nGrowSize = 0, int nInitSize = 0 ); - CUtlMemory( T* pMemory, int numElements ); - ~CUtlMemory(); - - // element access - T& operator[]( int i ); - T const& operator[]( int i ) const; - T& Element( int i ); - T const& Element( int i ) const; - - // Can we use this index? - bool IsIdxValid( int i ) const; - - // Gets the base address (can change when adding elements!) - T* Base(); - T const* Base() const; - - // Attaches the buffer to external memory.... - void SetExternalBuffer( T* pMemory, int numElements ); - - // Size - int NumAllocated() const; - int Count() const; - - // Grows the memory, so that at least allocated + num elements are allocated - void Grow( int num = 1 ); - - // Makes sure we've got at least this much memory - void EnsureCapacity( int num ); - - // Memory deallocation - void Purge(); - - // is the memory externally allocated? - bool IsExternallyAllocated() const; - - // Set the size by which the memory grows - void SetGrowSize( int size ); - -private: - enum - { - EXTERNAL_BUFFER_MARKER = -1 - }; - - T* m_pMemory; - int m_nAllocationCount; - int m_nGrowSize; -}; - - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- -template< class T > -CUtlMemory::CUtlMemory( int nGrowSize, int nInitAllocationCount ) : m_pMemory(0), - m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize ) -{ - Assert( (nGrowSize >= 0) && (nGrowSize != EXTERNAL_BUFFER_MARKER) ); - if (m_nAllocationCount) - { - m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); - } -} - -template< class T > -CUtlMemory::CUtlMemory( T* pMemory, int numElements ) : m_pMemory(pMemory), - m_nAllocationCount( numElements ) -{ - // Special marker indicating externally supplied memory - m_nGrowSize = EXTERNAL_BUFFER_MARKER; -} - -template< class T > -CUtlMemory::~CUtlMemory() -{ - Purge(); -} - - -//----------------------------------------------------------------------------- -// Attaches the buffer to external memory.... -//----------------------------------------------------------------------------- -template< class T > -void CUtlMemory::SetExternalBuffer( T* pMemory, int numElements ) -{ - // Blow away any existing allocated memory - Purge(); - - m_pMemory = pMemory; - m_nAllocationCount = numElements; - - // Indicate that we don't own the memory - m_nGrowSize = EXTERNAL_BUFFER_MARKER; -} - - -//----------------------------------------------------------------------------- -// element access -//----------------------------------------------------------------------------- -template< class T > -inline T& CUtlMemory::operator[]( int i ) -{ - Assert( IsIdxValid(i) ); - return m_pMemory[i]; -} - -template< class T > -inline T const& CUtlMemory::operator[]( int i ) const -{ - Assert( IsIdxValid(i) ); - return m_pMemory[i]; -} - -template< class T > -inline T& CUtlMemory::Element( int i ) -{ - Assert( IsIdxValid(i) ); - return m_pMemory[i]; -} - -template< class T > -inline T const& CUtlMemory::Element( int i ) const -{ - Assert( IsIdxValid(i) ); - return m_pMemory[i]; -} - - -//----------------------------------------------------------------------------- -// is the memory externally allocated? -//----------------------------------------------------------------------------- -template< class T > -bool CUtlMemory::IsExternallyAllocated() const -{ - return m_nGrowSize == EXTERNAL_BUFFER_MARKER; -} - - -template< class T > -void CUtlMemory::SetGrowSize( int nSize ) -{ - Assert( (nSize >= 0) && (nSize != EXTERNAL_BUFFER_MARKER) ); - m_nGrowSize = nSize; -} - - -//----------------------------------------------------------------------------- -// Gets the base address (can change when adding elements!) -//----------------------------------------------------------------------------- -template< class T > -inline T* CUtlMemory::Base() -{ - return m_pMemory; -} - -template< class T > -inline T const* CUtlMemory::Base() const -{ - return m_pMemory; -} - - -//----------------------------------------------------------------------------- -// Size -//----------------------------------------------------------------------------- -template< class T > -inline int CUtlMemory::NumAllocated() const -{ - return m_nAllocationCount; -} - -template< class T > -inline int CUtlMemory::Count() const -{ - return m_nAllocationCount; -} - - -//----------------------------------------------------------------------------- -// Is element index valid? -//----------------------------------------------------------------------------- -template< class T > -inline bool CUtlMemory::IsIdxValid( int i ) const -{ - return (i >= 0) && (i < m_nAllocationCount); -} - - -//----------------------------------------------------------------------------- -// Grows the memory -//----------------------------------------------------------------------------- -template< class T > -void CUtlMemory::Grow( int num ) -{ - Assert( num > 0 ); - - if (IsExternallyAllocated()) - { - // Can't grow a buffer whose memory was externally allocated - Assert(0); - return; - } - - // Make sure we have at least numallocated + num allocations. - // Use the grow rules specified for this memory (in m_nGrowSize) - int nAllocationRequested = m_nAllocationCount + num; - while (m_nAllocationCount < nAllocationRequested) - { - if ( m_nAllocationCount != 0 ) - { - if (m_nGrowSize) - { - m_nAllocationCount += m_nGrowSize; - } - else - { - m_nAllocationCount += m_nAllocationCount; - } - } - else - { - // Compute an allocation which is at least as big as a cache line... - m_nAllocationCount = (31 + sizeof(T)) / sizeof(T); - Assert(m_nAllocationCount != 0); - } - } - - if (m_pMemory) - { - T* pTempMemory = ( T* )realloc( m_pMemory, m_nAllocationCount * sizeof( T ) ); - - if( !pTempMemory ) - return; - - m_pMemory = pTempMemory; - } - else - { - m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); - } -} - - -//----------------------------------------------------------------------------- -// Makes sure we've got at least this much memory -//----------------------------------------------------------------------------- -template< class T > -inline void CUtlMemory::EnsureCapacity( int num ) -{ - if (m_nAllocationCount >= num) - return; - - if (IsExternallyAllocated()) - { - // Can't grow a buffer whose memory was externally allocated - Assert(0); - return; - } - - m_nAllocationCount = num; - if (m_pMemory) - { - T* pTempMemory = ( T* )realloc( m_pMemory, m_nAllocationCount * sizeof( T ) ); - - if( !pTempMemory ) - return; - - m_pMemory = pTempMemory; - } - else - { - m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); - } -} - - -//----------------------------------------------------------------------------- -// Memory deallocation -//----------------------------------------------------------------------------- -template< class T > -void CUtlMemory::Purge() -{ - if (!IsExternallyAllocated()) - { - if (m_pMemory) - { - free( (void*)m_pMemory ); - m_pMemory = 0; - } - m_nAllocationCount = 0; - } -} - - -#endif//UTLMEMORY_H diff --git a/vgui_support/utlrbtree.h b/vgui_support/utlrbtree.h deleted file mode 100644 index a0cfd8a7..00000000 --- a/vgui_support/utlrbtree.h +++ /dev/null @@ -1,1289 +0,0 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Header: $ -// $NoKeywords: $ -//============================================================================= - -#ifndef UTLRBTREE_H -#define UTLRBTREE_H - -#include "port.h" -#include "utlmemory.h" -//----------------------------------------------------------------------------- -// Tool to generate a default compare function for any type that implements -// operator<, including all simple types -//----------------------------------------------------------------------------- - -template -class CDefOps -{ -public: - static bool LessFunc( const T &lhs, const T &rhs ) { return ( lhs < rhs ); } -}; - -#define DefLessFunc( type ) CDefOps::LessFunc - -//------------------------------------- - -inline bool StringLessThan( const char * const &lhs, const char * const &rhs) { return ( strcmp( lhs, rhs) < 0 ); } -inline bool CaselessStringLessThan( const char * const &lhs, const char * const &rhs ) { return ( strcasecmp( lhs, rhs) < 0 ); } - -//------------------------------------- -// inline these two templates to stop multiple definitions of the same code -template <> inline bool CDefOps::LessFunc( const char * const &lhs, const char * const &rhs ) { return StringLessThan( lhs, rhs ); } -template <> inline bool CDefOps::LessFunc( char * const &lhs, char * const &rhs ) { return StringLessThan( lhs, rhs ); } - -//------------------------------------- - -template -void SetDefLessFunc( RBTREE_T &RBTree ) -{ -#ifdef _WIN32 - RBTree.SetLessFunc( DefLessFunc( RBTREE_T::KeyType_t ) ); -#elif _LINUX - RBTree.SetLessFunc( DefLessFunc( typename RBTREE_T::KeyType_t ) ); -#endif -} - -//----------------------------------------------------------------------------- -// A red-black binary search tree -//----------------------------------------------------------------------------- - -template -class CUtlRBTree -{ -public: - // Less func typedef - // Returns true if the first parameter is "less" than the second - typedef bool (*LessFunc_t)( T const &, T const & ); - - typedef T KeyType_t; - typedef T ElemType_t; - typedef I IndexType_t; - - // constructor, destructor - // Left at growSize = 0, the memory will first allocate 1 element and double in size - // at each increment. - // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below - CUtlRBTree( int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0 ); - ~CUtlRBTree( ); - - // gets particular elements - T& Element( I i ); - T const &Element( I i ) const; - T& operator[]( I i ); - T const &operator[]( I i ) const; - - // Gets the root - I Root() const; - - // Num elements - unsigned int Count() const; - - // Max "size" of the vector - I MaxElement() const; - - // Gets the children - I Parent( I i ) const; - I LeftChild( I i ) const; - I RightChild( I i ) const; - - // Tests if a node is a left or right child - bool IsLeftChild( I i ) const; - bool IsRightChild( I i ) const; - - // Tests if root or leaf - bool IsRoot( I i ) const; - bool IsLeaf( I i ) const; - - // Checks if a node is valid and in the tree - bool IsValidIndex( I i ) const; - - // Checks if the tree as a whole is valid - bool IsValid() const; - - // Invalid index - static I InvalidIndex(); - - // returns the tree depth (not a very fast operation) - int Depth( I node ) const; - int Depth() const; - - // Sets the less func - void SetLessFunc( LessFunc_t func ); - - // Allocation method - I NewNode(); - - // Insert method (inserts in order) - I Insert( T const &insert ); - void Insert( const T *pArray, int nItems ); - - // Find method - I Find( T const &search ) const; - - // Remove methods - void RemoveAt( I i ); - bool Remove( T const &remove ); - void RemoveAll( ); - - // Allocation, deletion - void FreeNode( I i ); - - // Iteration - I FirstInorder() const; - I NextInorder( I i ) const; - I PrevInorder( I i ) const; - I LastInorder() const; - - I FirstPreorder() const; - I NextPreorder( I i ) const; - I PrevPreorder( I i ) const; - I LastPreorder( ) const; - - I FirstPostorder() const; - I NextPostorder( I i ) const; - - // If you change the search key, this can be used to reinsert the - // element into the tree. - void Reinsert( I elem ); - -protected: - enum NodeColor_t - { - RED = 0, - BLACK - }; - - struct Links_t - { - I m_Left; - I m_Right; - I m_Parent; - I m_Tag; - }; - - struct Node_t : public Links_t - { - T m_Data; - }; - - // Sets the children - void SetParent( I i, I parent ); - void SetLeftChild( I i, I child ); - void SetRightChild( I i, I child ); - void LinkToParent( I i, I parent, bool isLeft ); - - // Gets at the links - Links_t const &Links( I i ) const; - Links_t &Links( I i ); - - // Checks if a link is red or black - bool IsRed( I i ) const; - bool IsBlack( I i ) const; - - // Sets/gets node color - NodeColor_t Color( I i ) const; - void SetColor( I i, NodeColor_t c ); - - // operations required to preserve tree balance - void RotateLeft(I i); - void RotateRight(I i); - void InsertRebalance(I i); - void RemoveRebalance(I i); - - // Insertion, removal - I InsertAt( I parent, bool leftchild ); - - // copy constructors not allowed - CUtlRBTree( CUtlRBTree const &tree ); - - // Inserts a node into the tree, doesn't copy the data in. - void FindInsertionPosition( T const &insert, I &parent, bool &leftchild ); - - // Remove and add back an element in the tree. - void Unlink( I elem ); - void Link( I elem ); - - // Used for sorting. - LessFunc_t m_LessFunc; - - CUtlMemory m_Elements; - I m_Root; - I m_NumElements; - I m_FirstFree; - I m_TotalElements; - - Node_t* m_pElements; - - void ResetDbgInfo() - { - m_pElements = (Node_t*)m_Elements.Base(); - } -}; - - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -template -CUtlRBTree::CUtlRBTree( int growSize, int initSize, LessFunc_t lessfunc ) : - m_Elements( growSize, initSize ), - m_LessFunc( lessfunc ), - m_Root( InvalidIndex() ), - m_NumElements( 0 ), m_TotalElements( 0 ), - m_FirstFree( InvalidIndex() ) -{ - ResetDbgInfo(); -} - -template -CUtlRBTree::~CUtlRBTree() -{ -} - -//----------------------------------------------------------------------------- -// gets particular elements -//----------------------------------------------------------------------------- - -template -inline T &CUtlRBTree::Element( I i ) -{ - return m_Elements[i].m_Data; -} - -template -inline T const &CUtlRBTree::Element( I i ) const -{ - return m_Elements[i].m_Data; -} - -template -inline T &CUtlRBTree::operator[]( I i ) -{ - return Element(i); -} - -template -inline T const &CUtlRBTree::operator[]( I i ) const -{ - return Element(i); -} - -//----------------------------------------------------------------------------- -// -// various accessors -// -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Gets the root -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::Root() const -{ - return m_Root; -} - -//----------------------------------------------------------------------------- -// Num elements -//----------------------------------------------------------------------------- - -template -inline unsigned int CUtlRBTree::Count() const -{ - return (unsigned int)m_NumElements; -} - -//----------------------------------------------------------------------------- -// Max "size" of the vector -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::MaxElement() const -{ - return (I)m_TotalElements; -} - - -//----------------------------------------------------------------------------- -// Gets the children -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::Parent( I i ) const -{ - return Links(i).m_Parent; -} - -template -inline I CUtlRBTree::LeftChild( I i ) const -{ - return Links(i).m_Left; -} - -template -inline I CUtlRBTree::RightChild( I i ) const -{ - return Links(i).m_Right; -} - -//----------------------------------------------------------------------------- -// Tests if a node is a left or right child -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsLeftChild( I i ) const -{ - return LeftChild(Parent(i)) == i; -} - -template -inline bool CUtlRBTree::IsRightChild( I i ) const -{ - return RightChild(Parent(i)) == i; -} - - -//----------------------------------------------------------------------------- -// Tests if root or leaf -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsRoot( I i ) const -{ - return i == m_Root; -} - -template -inline bool CUtlRBTree::IsLeaf( I i ) const -{ - return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex()); -} - - -//----------------------------------------------------------------------------- -// Checks if a node is valid and in the tree -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsValidIndex( I i ) const -{ - return LeftChild(i) != i; -} - - -//----------------------------------------------------------------------------- -// Invalid index -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::InvalidIndex() -{ - return (I)~0; -} - - -//----------------------------------------------------------------------------- -// returns the tree depth (not a very fast operation) -//----------------------------------------------------------------------------- - -template -inline int CUtlRBTree::Depth() const -{ - return Depth(Root()); -} - -//----------------------------------------------------------------------------- -// Sets the children -//----------------------------------------------------------------------------- - -template -inline void CUtlRBTree::SetParent( I i, I parent ) -{ - Links(i).m_Parent = parent; -} - -template -inline void CUtlRBTree::SetLeftChild( I i, I child ) -{ - Links(i).m_Left = child; -} - -template -inline void CUtlRBTree::SetRightChild( I i, I child ) -{ - Links(i).m_Right = child; -} - -//----------------------------------------------------------------------------- -// Gets at the links -//----------------------------------------------------------------------------- -static const int s_Sentinel[4] = {-1, -1, -1, 1}; -template -inline typename CUtlRBTree::Links_t const &CUtlRBTree::Links( I i ) const -{ - // Sentinel node, makes life easier - - return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : - *(Links_t*)&s_Sentinel; -} - -template -inline typename CUtlRBTree::Links_t &CUtlRBTree::Links( I i ) -{ - Assert(i != InvalidIndex()); - return *(Links_t *)&m_Elements[i]; -} - -//----------------------------------------------------------------------------- -// Checks if a link is red or black -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsRed( I i ) const -{ - return (Links(i).m_Tag == RED); -} - -template -inline bool CUtlRBTree::IsBlack( I i ) const -{ - return (Links(i).m_Tag == BLACK); -} - - -//----------------------------------------------------------------------------- -// Sets/gets node color -//----------------------------------------------------------------------------- - -template -inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color( I i ) const -{ - return (NodeColor_t)Links(i).m_Tag; -} - -template -inline void CUtlRBTree::SetColor( I i, typename CUtlRBTree::NodeColor_t c ) -{ - Links(i).m_Tag = (I)c; -} - -//----------------------------------------------------------------------------- -// Allocates/ deallocates nodes -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::NewNode() -{ - I newElem; - - // Nothing in the free list; add. - if (m_FirstFree == InvalidIndex()) - { - if (m_Elements.NumAllocated() == m_TotalElements) - m_Elements.Grow(); - newElem = m_TotalElements++; - } - else - { - newElem = m_FirstFree; - m_FirstFree = RightChild(m_FirstFree); - } - -#ifdef _DEBUG - // reset links to invalid.... - Links_t &node = Links(newElem); - node.m_Left = node.m_Right = node.m_Parent = InvalidIndex(); -#endif - - Construct( &Element(newElem) ); - ResetDbgInfo(); - - return newElem; -} - -template -void CUtlRBTree::FreeNode( I i ) -{ - Assert( IsValidIndex(i) && (i != InvalidIndex()) ); - Destruct( &Element(i) ); - SetLeftChild( i, i ); // indicates it's in not in the tree - SetRightChild( i, m_FirstFree ); - m_FirstFree = i; -} - - -//----------------------------------------------------------------------------- -// Rotates node i to the left -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RotateLeft(I elem) -{ - I rightchild = RightChild(elem); - SetRightChild( elem, LeftChild(rightchild) ); - if (LeftChild(rightchild) != InvalidIndex()) - SetParent( LeftChild(rightchild), elem ); - - if (rightchild != InvalidIndex()) - SetParent( rightchild, Parent(elem) ); - if (!IsRoot(elem)) - { - if (IsLeftChild(elem)) - SetLeftChild( Parent(elem), rightchild ); - else - SetRightChild( Parent(elem), rightchild ); - } - else - m_Root = rightchild; - - SetLeftChild( rightchild, elem ); - if (elem != InvalidIndex()) - SetParent( elem, rightchild ); -} - - -//----------------------------------------------------------------------------- -// Rotates node i to the right -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RotateRight(I elem) -{ - I leftchild = LeftChild(elem); - SetLeftChild( elem, RightChild(leftchild) ); - if (RightChild(leftchild) != InvalidIndex()) - SetParent( RightChild(leftchild), elem ); - - if (leftchild != InvalidIndex()) - SetParent( leftchild, Parent(elem) ); - if (!IsRoot(elem)) - { - if (IsRightChild(elem)) - SetRightChild( Parent(elem), leftchild ); - else - SetLeftChild( Parent(elem), leftchild ); - } - else - m_Root = leftchild; - - SetRightChild( leftchild, elem ); - if (elem != InvalidIndex()) - SetParent( elem, leftchild ); -} - - -//----------------------------------------------------------------------------- -// Rebalances the tree after an insertion -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::InsertRebalance(I elem) -{ - while ( !IsRoot(elem) && (Color(Parent(elem)) == RED) ) - { - I parent = Parent(elem); - I grandparent = Parent(parent); - - /* we have a violation */ - if (IsLeftChild(parent)) - { - I uncle = RightChild(grandparent); - if (IsRed(uncle)) - { - /* uncle is RED */ - SetColor(parent, BLACK); - SetColor(uncle, BLACK); - SetColor(grandparent, RED); - elem = grandparent; - } - else - { - /* uncle is BLACK */ - if (IsRightChild(elem)) - { - /* make x a left child, will change parent and grandparent */ - elem = parent; - RotateLeft(elem); - parent = Parent(elem); - grandparent = Parent(parent); - } - /* recolor and rotate */ - SetColor(parent, BLACK); - SetColor(grandparent, RED); - RotateRight(grandparent); - } - } - else - { - /* mirror image of above code */ - I uncle = LeftChild(grandparent); - if (IsRed(uncle)) - { - /* uncle is RED */ - SetColor(parent, BLACK); - SetColor(uncle, BLACK); - SetColor(grandparent, RED); - elem = grandparent; - } - else - { - /* uncle is BLACK */ - if (IsLeftChild(elem)) - { - /* make x a right child, will change parent and grandparent */ - elem = parent; - RotateRight(parent); - parent = Parent(elem); - grandparent = Parent(parent); - } - /* recolor and rotate */ - SetColor(parent, BLACK); - SetColor(grandparent, RED); - RotateLeft(grandparent); - } - } - } - SetColor( m_Root, BLACK ); -} - - -//----------------------------------------------------------------------------- -// Insert a node into the tree -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::InsertAt( I parent, bool leftchild ) -{ - I i = NewNode(); - LinkToParent( i, parent, leftchild ); - ++m_NumElements; - return i; -} - -template -void CUtlRBTree::LinkToParent( I i, I parent, bool isLeft ) -{ - Links_t &elem = Links(i); - elem.m_Parent = parent; - elem.m_Left = elem.m_Right = InvalidIndex(); - elem.m_Tag = RED; - - /* insert node in tree */ - if (parent != InvalidIndex()) - { - if (isLeft) - Links(parent).m_Left = i; - else - Links(parent).m_Right = i; - } - else - { - m_Root = i; - } - - InsertRebalance(i); - - Assert(IsValid()); -} - -//----------------------------------------------------------------------------- -// Rebalance the tree after a deletion -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveRebalance(I elem) -{ - while (elem != m_Root && IsBlack(elem)) - { - I parent = Parent(elem); - - // If elem is the left child of the parent - if (elem == LeftChild(parent)) - { - // Get our sibling - I sibling = RightChild(parent); - if (IsRed(sibling)) - { - SetColor(sibling, BLACK); - SetColor(parent, RED); - RotateLeft(parent); - - // We may have a new parent now - parent = Parent(elem); - sibling = RightChild(parent); - } - if ( (IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling))) ) - { - if (sibling != InvalidIndex()) - SetColor(sibling, RED); - elem = parent; - } - else - { - if (IsBlack(RightChild(sibling))) - { - SetColor(LeftChild(sibling), BLACK); - SetColor(sibling, RED); - RotateRight(sibling); - - // rotation may have changed this - parent = Parent(elem); - sibling = RightChild(parent); - } - SetColor( sibling, Color(parent) ); - SetColor( parent, BLACK ); - SetColor( RightChild(sibling), BLACK ); - RotateLeft( parent ); - elem = m_Root; - } - } - else - { - // Elem is the right child of the parent - I sibling = LeftChild(parent); - if (IsRed(sibling)) - { - SetColor(sibling, BLACK); - SetColor(parent, RED); - RotateRight(parent); - - // We may have a new parent now - parent = Parent(elem); - sibling = LeftChild(parent); - } - if ( (IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling))) ) - { - if (sibling != InvalidIndex()) - SetColor( sibling, RED ); - elem = parent; - } - else - { - if (IsBlack(LeftChild(sibling))) - { - SetColor( RightChild(sibling), BLACK ); - SetColor( sibling, RED ); - RotateLeft( sibling ); - - // rotation may have changed this - parent = Parent(elem); - sibling = LeftChild(parent); - } - SetColor( sibling, Color(parent) ); - SetColor( parent, BLACK ); - SetColor( LeftChild(sibling), BLACK ); - RotateRight( parent ); - elem = m_Root; - } - } - } - SetColor( elem, BLACK ); -} - -template -void CUtlRBTree::Unlink( I elem ) -{ - if ( elem != InvalidIndex() ) - { - I x, y; - - if ((LeftChild(elem) == InvalidIndex()) || - (RightChild(elem) == InvalidIndex())) - { - /* y has a NIL node as a child */ - y = elem; - } - else - { - /* find tree successor with a NIL node as a child */ - y = RightChild(elem); - while (LeftChild(y) != InvalidIndex()) - y = LeftChild(y); - } - - /* x is y's only child */ - if (LeftChild(y) != InvalidIndex()) - x = LeftChild(y); - else - x = RightChild(y); - - /* remove y from the parent chain */ - if (x != InvalidIndex()) - SetParent( x, Parent(y) ); - if (!IsRoot(y)) - { - if (IsLeftChild(y)) - SetLeftChild( Parent(y), x ); - else - SetRightChild( Parent(y), x ); - } - else - m_Root = x; - - // need to store this off now, we'll be resetting y's color - NodeColor_t ycolor = Color(y); - if (y != elem) - { - // Standard implementations copy the data around, we cannot here. - // Hook in y to link to the same stuff elem used to. - SetParent( y, Parent(elem) ); - SetRightChild( y, RightChild(elem) ); - SetLeftChild( y, LeftChild(elem) ); - - if (!IsRoot(elem)) - if (IsLeftChild(elem)) - SetLeftChild( Parent(elem), y ); - else - SetRightChild( Parent(elem), y ); - else - m_Root = y; - - if (LeftChild(y) != InvalidIndex()) - SetParent( LeftChild(y), y ); - if (RightChild(y) != InvalidIndex()) - SetParent( RightChild(y), y ); - - SetColor( y, Color(elem) ); - } - - if ((x != InvalidIndex()) && (ycolor == BLACK)) - RemoveRebalance(x); - } -} - -template -void CUtlRBTree::Link( I elem ) -{ - if ( elem != InvalidIndex() ) - { - I parent; - bool leftchild; - - FindInsertionPosition( Element( elem ), parent, leftchild ); - - LinkToParent( elem, parent, leftchild ); - } -} - -//----------------------------------------------------------------------------- -// Delete a node from the tree -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveAt(I elem) -{ - if ( elem != InvalidIndex() ) - { - Unlink( elem ); - - FreeNode(elem); - --m_NumElements; - } -} - - -//----------------------------------------------------------------------------- -// remove a node in the tree -//----------------------------------------------------------------------------- - -template bool CUtlRBTree::Remove( T const &search ) -{ - I node = Find( search ); - if (node != InvalidIndex()) - { - RemoveAt(node); - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// Removes all nodes from the tree -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveAll() -{ - // Just iterate through the whole list and add to free list - // much faster than doing all of the rebalancing - // also, do it so the free list is pointing to stuff in order - // to get better cache coherence when re-adding stuff to this tree. - I prev = InvalidIndex(); - for (int i = (int)m_TotalElements; --i >= 0; ) - { - I idx = (I)i; - if (IsValidIndex(idx)) - Destruct( &Element(idx) ); - SetRightChild( idx, prev ); - SetLeftChild( idx, idx ); - prev = idx; - } - m_FirstFree = m_TotalElements ? (I)0 : InvalidIndex(); - m_Root = InvalidIndex(); - m_NumElements = 0; -} - - -//----------------------------------------------------------------------------- -// iteration -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::FirstInorder() const -{ - I i = m_Root; - while (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); - return i; -} - -template -I CUtlRBTree::NextInorder( I i ) const -{ - Assert(IsValidIndex(i)); - - if (RightChild(i) != InvalidIndex()) - { - i = RightChild(i); - while (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); - return i; - } - - I parent = Parent(i); - while (IsRightChild(i)) - { - i = parent; - if (i == InvalidIndex()) break; - parent = Parent(i); - } - return parent; -} - -template -I CUtlRBTree::PrevInorder( I i ) const -{ - Assert(IsValidIndex(i)); - - if (LeftChild(i) != InvalidIndex()) - { - i = LeftChild(i); - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); - return i; - } - - I parent = Parent(i); - while (IsLeftChild(i)) - { - i = parent; - if (i == InvalidIndex()) break; - parent = Parent(i); - } - return parent; -} - -template -I CUtlRBTree::LastInorder() const -{ - I i = m_Root; - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); - return i; -} - -template -I CUtlRBTree::FirstPreorder() const -{ - return m_Root; -} - -template -I CUtlRBTree::NextPreorder( I i ) const -{ - if (LeftChild(i) != InvalidIndex()) - return LeftChild(i); - - if (RightChild(i) != InvalidIndex()) - return RightChild(i); - - I parent = Parent(i); - while( parent != InvalidIndex()) - { - if (IsLeftChild(i) && (RightChild(parent) != InvalidIndex())) - return RightChild(parent); - i = parent; - parent = Parent(parent); - } - return InvalidIndex(); -} - -template -I CUtlRBTree::PrevPreorder( I i ) const -{ - Assert(0); // not implemented yet - return InvalidIndex(); -} - -template -I CUtlRBTree::LastPreorder() const -{ - I i = m_Root; - while (1) - { - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); - - if (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); - else - break; - } - return i; -} - -template -I CUtlRBTree::FirstPostorder() const -{ - I i = m_Root; - while (!IsLeaf(i)) - { - if (LeftChild(i)) - i = LeftChild(i); - else - i = RightChild(i); - } - return i; -} - -template -I CUtlRBTree::NextPostorder( I i ) const -{ - I parent = Parent(i); - if (parent == InvalidIndex()) - return InvalidIndex(); - - if (IsRightChild(i)) - return parent; - - if (RightChild(parent) == InvalidIndex()) - return parent; - - i = RightChild(parent); - while (!IsLeaf(i)) - { - if (LeftChild(i)) - i = LeftChild(i); - else - i = RightChild(i); - } - return i; -} - - -template -void CUtlRBTree::Reinsert( I elem ) -{ - Unlink( elem ); - Link( elem ); -} - - -//----------------------------------------------------------------------------- -// returns the tree depth (not a very fast operation) -//----------------------------------------------------------------------------- -#ifdef max -#undef max -#endif -#define max(a,b) (a)>(b)?(a):(b) - -template -int CUtlRBTree::Depth( I node ) const -{ - if (node == InvalidIndex()) - return 0; - - int depthright = Depth( RightChild(node) ); - int depthleft = Depth( LeftChild(node) ); - return max(depthright, depthleft) + 1; -} - - -//----------------------------------------------------------------------------- -// Makes sure the tree is valid after every operation -//----------------------------------------------------------------------------- - -template -bool CUtlRBTree::IsValid() const -{ - if ( !Count() ) - return true; - - if (( Root() >= MaxElement()) || ( Parent( Root() ) != InvalidIndex() )) - goto InvalidTree; - -#ifdef UTLTREE_PARANOID - - // First check to see that mNumEntries matches reality. - // count items on the free list - int numFree = 0; - int curr = m_FirstFree; - while (curr != InvalidIndex()) - { - ++numFree; - curr = RightChild(curr); - if ( (curr > MaxElement()) && (curr != InvalidIndex()) ) - goto InvalidTree; - } - if (MaxElement() - numFree != Count()) - goto InvalidTree; - - // iterate over all elements, looking for validity - // based on the self pointers - int numFree2 = 0; - for (curr = 0; curr < MaxElement(); ++curr) - { - if (!IsValidIndex(curr)) - ++numFree2; - else - { - int right = RightChild(curr); - int left = LeftChild(curr); - if ((right == left) && (right != InvalidIndex()) ) - goto InvalidTree; - - if (right != InvalidIndex()) - { - if (!IsValidIndex(right)) - goto InvalidTree; - if (Parent(right) != curr) - goto InvalidTree; - if (IsRed(curr) && IsRed(right)) - goto InvalidTree; - } - - if (left != InvalidIndex()) - { - if (!IsValidIndex(left)) - goto InvalidTree; - if (Parent(left) != curr) - goto InvalidTree; - if (IsRed(curr) && IsRed(left)) - goto InvalidTree; - } - } - } - if (numFree2 != numFree) - goto InvalidTree; - -#endif // UTLTREE_PARANOID - - return true; - -InvalidTree: - return false; -} - - -//----------------------------------------------------------------------------- -// Sets the less func -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::SetLessFunc( typename CUtlRBTree::LessFunc_t func ) -{ - if (!m_LessFunc) - m_LessFunc = func; - else - { - // need to re-sort the tree here.... - Assert(0); - } -} - - -//----------------------------------------------------------------------------- -// inserts a node into the tree -//----------------------------------------------------------------------------- - -// Inserts a node into the tree, doesn't copy the data in. -template -void CUtlRBTree::FindInsertionPosition( T const &insert, I &parent, bool &leftchild ) -{ - Assert( m_LessFunc != NULL ); - - /* find where node belongs */ - I current = m_Root; - parent = InvalidIndex(); - leftchild = false; - while (current != InvalidIndex()) - { - parent = current; - if (m_LessFunc( insert, Element(current) )) - { - leftchild = true; current = LeftChild(current); - } - else - { - leftchild = false; current = RightChild(current); - } - } -} - -template -I CUtlRBTree::Insert( T const &insert ) -{ - // use copy constructor to copy it in - I parent; - bool leftchild; - FindInsertionPosition( insert, parent, leftchild ); - I newNode = InsertAt( parent, leftchild ); - CopyConstruct( &Element( newNode ), insert ); - return newNode; -} - - -template -void CUtlRBTree::Insert( const T *pArray, int nItems ) -{ - while ( nItems-- ) - { - Insert( *pArray++ ); - } -} - -//----------------------------------------------------------------------------- -// finds a node in the tree -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::Find( T const &search ) const -{ - Assert( m_LessFunc != NULL ); - - I current = m_Root; - while (current != InvalidIndex()) - { - if (m_LessFunc( search, Element(current) )) - current = LeftChild(current); - else if (m_LessFunc( Element(current), search )) - current = RightChild(current); - else - break; - } - return current; -} - -#endif//UTLRBTREE_H diff --git a/vgui_support/utlvector.h b/vgui_support/utlvector.h deleted file mode 100644 index 3cd4ddb6..00000000 --- a/vgui_support/utlvector.h +++ /dev/null @@ -1,605 +0,0 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// $Header: $ -// $NoKeywords: $ -// -// A growable array class that maintains a free list and keeps elements -// in the same location -//============================================================================= - -#ifndef UTLVECTOR_H -#define UTLVECTOR_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "port.h" -#include -#include "utlmemory.h" - - -//----------------------------------------------------------------------------- -// The CUtlVector class: -// A growable array class which doubles in size by default. -// It will always keep all elements consecutive in memory, and may move the -// elements around in memory (via a realloc) when elements are inserted or -// removed. Clients should therefore refer to the elements of the vector -// by index (they should *never* maintain pointers to elements in the vector). -//----------------------------------------------------------------------------- - -template< class T > -class CUtlVector -{ -public: - typedef T ElemType_t; - - // constructor, destructor - CUtlVector( int growSize = 0, int initSize = 0 ); - CUtlVector( T* pMemory, int numElements ); - ~CUtlVector(); - - // Copy the array. - CUtlVector& operator=( const CUtlVector &other ); - - // element access - T& operator[]( int i ); - T const& operator[]( int i ) const; - T& Element( int i ); - T const& Element( int i ) const; - - // Gets the base address (can change when adding elements!) - T* Base(); - T const* Base() const; - - // Returns the number of elements in the vector - // SIZE IS DEPRECATED! - int Count() const; - int Size() const; // don't use me! - - // Is element index valid? - bool IsValidIndex( int i ) const; - static int InvalidIndex( void ); - - // Adds an element, uses default constructor - int AddToHead(); - int AddToTail(); - int InsertBefore( int elem ); - int InsertAfter( int elem ); - - // Adds an element, uses copy constructor - int AddToHead( T const& src ); - int AddToTail( T const& src ); - int InsertBefore( int elem, T const& src ); - int InsertAfter( int elem, T const& src ); - - // Adds multiple elements, uses default constructor - int AddMultipleToHead( int num ); - int AddMultipleToTail( int num, const T *pToCopy=NULL ); - int InsertMultipleBefore( int elem, int num, const T *pToCopy=NULL ); // If pToCopy is set, then it's an array of length 'num' and - int InsertMultipleAfter( int elem, int num ); - - // Calls RemoveAll() then AddMultipleToTail. - void SetSize( int size ); - void SetCount( int count ); - - // Calls SetSize and copies each element. - void CopyArray( T const *pArray, int size ); - - // Add the specified array to the tail. - int AddVectorToTail( CUtlVector const &src ); - - // Finds an element (element needs operator== defined) - int Find( T const& src ) const; - - bool HasElement( T const& src ); - - // Makes sure we have enough memory allocated to store a requested # of elements - void EnsureCapacity( int num ); - - // Makes sure we have at least this many elements - void EnsureCount( int num ); - - // Element removal - void FastRemove( int elem ); // doesn't preserve order - void Remove( int elem ); // preserves order, shifts elements - void FindAndRemove( T const& src ); // removes first occurrence of src, preserves order, shifts elements - void RemoveMultiple( int elem, int num ); // preserves order, shifts elements - void RemoveAll(); // doesn't deallocate memory - - // Memory deallocation - void Purge(); - - // Purges the list and calls delete on each element in it. - void PurgeAndDeleteElements(); - - // Set the size by which it grows when it needs to allocate more memory. - void SetGrowSize( int size ); - -protected: - // Can't copy this unless we explicitly do it! - CUtlVector( CUtlVector const& vec ) { Assert(0); } - - // Grows the vector - void GrowVector( int num = 1 ); - - // Shifts elements.... - void ShiftElementsRight( int elem, int num = 1 ); - void ShiftElementsLeft( int elem, int num = 1 ); - - // For easier access to the elements through the debugger - void ResetDbgInfo(); - - CUtlMemory m_Memory; - int m_Size; - - // For easier access to the elements through the debugger - // it's in release builds so this can be used in libraries correctly - T *m_pElements; -}; - - -//----------------------------------------------------------------------------- -// For easier access to the elements through the debugger -//----------------------------------------------------------------------------- - -template< class T > -inline void CUtlVector::ResetDbgInfo() -{ - m_pElements = m_Memory.Base(); -} - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -template< class T > -inline CUtlVector::CUtlVector( int growSize, int initSize ) : - m_Memory(growSize, initSize), m_Size(0) -{ - ResetDbgInfo(); -} - -template< class T > -inline CUtlVector::CUtlVector( T* pMemory, int numElements ) : - m_Memory(pMemory, numElements), m_Size(0) -{ - ResetDbgInfo(); -} - -template< class T > -inline CUtlVector::~CUtlVector() -{ - Purge(); -} - -template -inline CUtlVector& CUtlVector::operator=( const CUtlVector &other ) -{ - CopyArray( other.Base(), other.Count() ); - return *this; -} - -//----------------------------------------------------------------------------- -// element access -//----------------------------------------------------------------------------- - -template< class T > -inline T& CUtlVector::operator[]( int i ) -{ - Assert( IsValidIndex(i) ); - return m_Memory[i]; -} - -template< class T > -inline T const& CUtlVector::operator[]( int i ) const -{ - Assert( IsValidIndex(i) ); - return m_Memory[i]; -} - -template< class T > -inline T& CUtlVector::Element( int i ) -{ - Assert( IsValidIndex(i) ); - return m_Memory[i]; -} - -template< class T > -inline T const& CUtlVector::Element( int i ) const -{ - Assert( IsValidIndex(i) ); - return m_Memory[i]; -} - - -//----------------------------------------------------------------------------- -// Gets the base address (can change when adding elements!) -//----------------------------------------------------------------------------- - -template< class T > -inline T* CUtlVector::Base() -{ - return m_Memory.Base(); -} - -template< class T > -inline T const* CUtlVector::Base() const -{ - return m_Memory.Base(); -} - -//----------------------------------------------------------------------------- -// Count -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::Size() const -{ - return m_Size; -} - -template< class T > -inline int CUtlVector::Count() const -{ - return m_Size; -} - - -//----------------------------------------------------------------------------- -// Is element index valid? -//----------------------------------------------------------------------------- - -template< class T > -inline bool CUtlVector::IsValidIndex( int i ) const -{ - return (i >= 0) && (i < m_Size); -} - - -//----------------------------------------------------------------------------- -// Returns in invalid index -//----------------------------------------------------------------------------- -template< class T > -inline int CUtlVector::InvalidIndex( void ) -{ - return -1; -} - - -//----------------------------------------------------------------------------- -// Grows the vector -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::GrowVector( int num ) -{ - if (m_Size + num - 1 >= m_Memory.NumAllocated()) - { - m_Memory.Grow( m_Size + num - m_Memory.NumAllocated() ); - } - - m_Size += num; - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// Makes sure we have enough memory allocated to store a requested # of elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::EnsureCapacity( int num ) -{ - m_Memory.EnsureCapacity(num); - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// Makes sure we have at least this many elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::EnsureCount( int num ) -{ - if (Count() < num) - AddMultipleToTail( num - Count() ); -} - - -//----------------------------------------------------------------------------- -// Shifts elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::ShiftElementsRight( int elem, int num ) -{ - Assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); - int numToMove = m_Size - elem - num; - if ((numToMove > 0) && (num > 0)) - memmove( &Element(elem+num), &Element(elem), numToMove * sizeof(T) ); -} - -template< class T > -void CUtlVector::ShiftElementsLeft( int elem, int num ) -{ - Assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); - int numToMove = m_Size - elem - num; - if ((numToMove > 0) && (num > 0)) - { - memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) ); - -#ifdef _DEBUG - memset( &Element(m_Size-num), 0xDD, num * sizeof(T) ); -#endif - } -} - -//----------------------------------------------------------------------------- -// Adds an element, uses default constructor -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::AddToHead() -{ - return InsertBefore(0); -} - -template< class T > -inline int CUtlVector::AddToTail() -{ - return InsertBefore( m_Size ); -} - -template< class T > -inline int CUtlVector::InsertAfter( int elem ) -{ - return InsertBefore( elem + 1 ); -} - -template< class T > -int CUtlVector::InsertBefore( int elem ) -{ - // Can insert at the end - Assert( (elem == Count()) || IsValidIndex(elem) ); - - GrowVector(); - ShiftElementsRight(elem); - Construct( &Element(elem) ); - return elem; -} - - -//----------------------------------------------------------------------------- -// Adds an element, uses copy constructor -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::AddToHead( T const& src ) -{ - return InsertBefore( 0, src ); -} - -template< class T > -inline int CUtlVector::AddToTail( T const& src ) -{ - return InsertBefore( m_Size, src ); -} - -template< class T > -inline int CUtlVector::InsertAfter( int elem, T const& src ) -{ - return InsertBefore( elem + 1, src ); -} - -template< class T > -int CUtlVector::InsertBefore( int elem, T const& src ) -{ - // Can insert at the end - Assert( (elem == Count()) || IsValidIndex(elem) ); - - GrowVector(); - ShiftElementsRight(elem); - CopyConstruct( &Element(elem), src ); - return elem; -} - - -//----------------------------------------------------------------------------- -// Adds multiple elements, uses default constructor -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::AddMultipleToHead( int num ) -{ - return InsertMultipleBefore( 0, num ); -} - -template< class T > -inline int CUtlVector::AddMultipleToTail( int num, const T *pToCopy ) -{ - return InsertMultipleBefore( m_Size, num, pToCopy ); -} - -template< class T > -int CUtlVector::InsertMultipleAfter( int elem, int num ) -{ - return InsertMultipleBefore( elem + 1, num ); -} - - -template< class T > -void CUtlVector::SetCount( int count ) -{ - RemoveAll(); - AddMultipleToTail( count ); -} - -template< class T > -inline void CUtlVector::SetSize( int size ) -{ - SetCount( size ); -} - -template< class T > -void CUtlVector::CopyArray( T const *pArray, int size ) -{ - SetSize( size ); - for( int i=0; i < size; i++ ) - (*this)[i] = pArray[i]; -} - -template< class T > -int CUtlVector::AddVectorToTail( CUtlVector const &src ) -{ - int base = Count(); - - // Make space. - AddMultipleToTail( src.Count() ); - - // Copy the elements. - for ( int i=0; i < src.Count(); i++ ) - (*this)[base + i] = src[i]; - - return base; -} - -template< class T > -inline int CUtlVector::InsertMultipleBefore( int elem, int num, const T *pToInsert ) -{ - if( num == 0 ) - return elem; - - // Can insert at the end - Assert( (elem == Count()) || IsValidIndex(elem) ); - - GrowVector(num); - ShiftElementsRight(elem, num); - - // Invoke default constructors - for (int i = 0; i < num; ++i) - Construct( &Element(elem+i) ); - - // Copy stuff in? - if ( pToInsert ) - { - for ( int i=0; i < num; i++ ) - { - Element( elem+i ) = pToInsert[i]; - } - } - - return elem; -} - -//----------------------------------------------------------------------------- -// Finds an element (element needs operator== defined) -//----------------------------------------------------------------------------- -template< class T > -int CUtlVector::Find( T const& src ) const -{ - for ( int i = 0; i < Count(); ++i ) - { - if (Element(i) == src) - return i; - } - return -1; -} - -template< class T > -bool CUtlVector::HasElement( T const& src ) -{ - return ( Find(src) >= 0 ); -} - -//----------------------------------------------------------------------------- -// Element removal -//----------------------------------------------------------------------------- - -template< class T > -void CUtlVector::FastRemove( int elem ) -{ - Assert( IsValidIndex(elem) ); - - Destruct( &Element(elem) ); - if (m_Size > 0) - { - memcpy( &Element(elem), &Element(m_Size-1), sizeof(T) ); - --m_Size; - } -} - -template< class T > -void CUtlVector::Remove( int elem ) -{ - Destruct( &Element(elem) ); - ShiftElementsLeft(elem); - --m_Size; -} - -template< class T > -void CUtlVector::FindAndRemove( T const& src ) -{ - int elem = Find( src ); - if ( elem != -1 ) - { - Remove( elem ); - } -} - -template< class T > -void CUtlVector::RemoveMultiple( int elem, int num ) -{ - Assert( IsValidIndex(elem) ); - Assert( elem + num <= Count() ); - - for (int i = elem + num; --i >= elem; ) - Destruct(&Element(i)); - - ShiftElementsLeft(elem, num); - m_Size -= num; -} - -template< class T > -void CUtlVector::RemoveAll() -{ - for (int i = m_Size; --i >= 0; ) - Destruct(&Element(i)); - - m_Size = 0; -} - - -//----------------------------------------------------------------------------- -// Memory deallocation -//----------------------------------------------------------------------------- - -template< class T > -void CUtlVector::Purge() -{ - RemoveAll(); - m_Memory.Purge( ); - ResetDbgInfo(); -} - - -template -inline void CUtlVector::PurgeAndDeleteElements() -{ - for( int i=0; i < m_Size; i++ ) - delete Element(i); - - Purge(); -} - - -template< class T > -void CUtlVector::SetGrowSize( int size ) -{ - m_Memory.SetGrowSize( size ); -} - - -#endif//UTLVECTOR_H diff --git a/vgui_support/vgui_main.h b/vgui_support/vgui_main.h index 5bed5d8d..0dd39ecf 100644 --- a/vgui_support/vgui_main.h +++ b/vgui_support/vgui_main.h @@ -25,8 +25,6 @@ from your version. #ifndef VGUI_MAIN_H #define VGUI_MAIN_H -#define Assert(x) - #ifdef _WIN32 #include #else diff --git a/vgui_support/wscript b/vgui_support/wscript index a4996137..e9e987f2 100644 --- a/vgui_support/wscript +++ b/vgui_support/wscript @@ -86,9 +86,9 @@ def build(bld): libs.append('VGUI') - source = bld.path.ant_glob(['*.cpp']) + source = bld.path.ant_glob(['*.cpp', 'miniutl/utlvector.cpp', 'miniutl/utlmemory.cpp']) - includes = [ '.', '../common', '../engine' ] + includes = [ '.', 'miniutl/', '../common', '../engine' ] bld.shlib( source = source, From 8c668b29c649d2a4f6080b74c5a7c26301dedc9a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 12 Jan 2019 02:36:43 +0300 Subject: [PATCH 185/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 348e477a..c08a8752 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 348e477a8d404aee8146cf65b22b57bbdc4af73e +Subproject commit c08a875249efa5061d2c993b2ba341ec68208627 From 2da65ab122c2d88721d943f16672a75ffd0ba478 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Jan 2019 02:07:15 +0300 Subject: [PATCH 186/205] Update submodules --- mainui | 2 +- vgui_support/miniutl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mainui b/mainui index c08a8752..56f300a5 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit c08a875249efa5061d2c993b2ba341ec68208627 +Subproject commit 56f300a532af53c6933eaeab417abb920820a73d diff --git a/vgui_support/miniutl b/vgui_support/miniutl index c4d1446a..a3c114a8 160000 --- a/vgui_support/miniutl +++ b/vgui_support/miniutl @@ -1 +1 @@ -Subproject commit c4d1446a973acf80885ab5c0ca0207eb24beca32 +Subproject commit a3c114a8b47574ba351f026fb059a3f26123bd79 From 042d2e436cca3c96f6f61ad4af5dd95397787276 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 26 Jan 2019 14:27:30 +0300 Subject: [PATCH 187/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 56f300a5..5d0b0e62 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 56f300a532af53c6933eaeab417abb920820a73d +Subproject commit 5d0b0e62b049ad45896405f3c20712d5feae9dea From 5da11291a28559d01ba29716edddfa290b065957 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 26 Jan 2019 18:54:23 +0300 Subject: [PATCH 188/205] legacymode: support server scanning --- engine/client/cl_main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 4e35d462..9b44d148 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1453,7 +1453,7 @@ void CL_LocalServers_f( void ) adr.type = NA_BROADCAST; adr.port = MSG_BigShort( PORT_SERVER ); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); } #define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" @@ -1603,6 +1603,13 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) CL_FixupColorStringsForInfoString( s, infostring ); + if( cl_legacymode->value && Q_strstr( infostring, "wrong version" ) ) + { + Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION ); + Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); + return; + } + if( !COM_CheckString( Info_ValueForKey( infostring, "gamedir" ))) { Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); @@ -1949,7 +1956,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( clgame.request_type == NET_REQUEST_GAMEUI ) { NET_Config( true ); // allow remote - Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); } } From 1162e5a65b1754c29e0117758e4cdb59acbf4d86 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 26 Jan 2019 20:47:19 +0300 Subject: [PATCH 189/205] legacymode: retry connecting using legacy protocol automatically --- engine/client/cl_main.c | 47 +++++++++++++++++++++++++++++------------ engine/client/client.h | 1 + engine/common/sys_con.c | 2 +- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 9b44d148..ae4b0147 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -69,7 +69,6 @@ convar_t *cl_lw; convar_t *cl_charset; convar_t *cl_trace_messages; convar_t *hud_utf8; -convar_t *cl_legacymode; // // userinfo @@ -1016,11 +1015,19 @@ void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); - /// TODO: identification for legacy mode if( cls.legacymode ) - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", 48, Q_atoi(qport), cls.challenge, cls.userinfo ); + { + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", + PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo ); + Con_Printf( "Trying to connect by legacy protocol\n" ); + } else + { Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); + Con_Printf( "Trying to connect by modern protocol\n" ); + } + + cls.timestart = Sys_DoubleTime(); } @@ -1171,8 +1178,14 @@ CL_Connect_f void CL_Connect_f( void ) { string server; + qboolean legacyconnect = false; - if( Cmd_Argc() != 2 ) + // hidden hint to connect by using legacy protocol + if( Cmd_Argc() == 3 ) + { + legacyconnect = !Q_strcmp( Cmd_Argv( 2 ), "legacy" ); + } + else if( Cmd_Argc() != 2 ) { Con_Printf( S_USAGE "connect \n" ); return; @@ -1192,6 +1205,7 @@ void CL_Connect_f( void ) Key_SetKeyDest( key_console ); cls.state = ca_connecting; + cls.legacymode = legacyconnect; Q_strncpy( cls.servername, server, sizeof( cls.servername )); cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately cls.max_fragment_size = FRAGMENT_MAX_SIZE; // guess a we can establish connection with maximum fragment size @@ -1377,7 +1391,7 @@ This is also called on Host_Error, so it shouldn't cause any errors */ void CL_Disconnect( void ) { - cls.legacymode = cl_legacymode->value; + cls.legacymode = false; if( cls.state == ca_disconnected ) return; @@ -1446,9 +1460,6 @@ void CL_LocalServers_f( void ) Con_Printf( "Scanning for servers on the local network area...\n" ); NET_Config( true ); // allow remote - if( cls.state == ca_disconnected ) - cls.legacymode = cl_legacymode->value; - // send a broadcast packet adr.type = NA_BROADCAST; adr.port = MSG_BigShort( PORT_SERVER ); @@ -1471,9 +1482,6 @@ void CL_InternetServers_f( void ) NET_Config( true ); // allow remote - if( cls.state == ca_disconnected ) - cls.legacymode = cl_legacymode->value; - Con_Printf( "Scanning for servers on the internet area...\n" ); Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version @@ -1603,7 +1611,7 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) CL_FixupColorStringsForInfoString( s, infostring ); - if( cl_legacymode->value && Q_strstr( infostring, "wrong version" ) ) + if( Q_strstr( infostring, "wrong version" ) ) { Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION ); Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); @@ -1815,6 +1823,14 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // print command from somewhere Con_Printf( "%s", MSG_ReadString( msg )); } + else if( !Q_strcmp( c, "errormsg" )) + { + args = MSG_ReadString( msg ); + if( !Q_strcmp( args, "Server uses protocol version 48.\n" )) + { + cls.legacyserver = from; + } + } else if( !Q_strcmp( c, "testpacket" )) { byte recv_buf[NET_MAX_FRAGMENT]; @@ -1894,6 +1910,12 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // a disconnect message from the server, which will happen if the server // dropped the connection but it is still getting packets from us CL_Disconnect_f(); + + if( NET_CompareAdr( from, cls.legacyserver )) + { + Cbuf_AddText( va( "connect %s legacy\n", NET_AdrToString( from ))); + memset( &cls.legacyserver, 0, sizeof( cls.legacyserver )); + } } else if( !Q_strcmp( c, "f" )) { @@ -2639,7 +2661,6 @@ void CL_InitLocal( void ) hud_scale = Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" ); Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" ); cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" ); - cl_legacymode = Cvar_Get( "cl_legacymode", "0", 0, "legacy mode compatibility" ); Cvar_Get( "lastdemo", "", FCVAR_ARCHIVE, "last played demo" ); // these two added to shut up CS 1.5 about 'unknown' commands diff --git a/engine/client/client.h b/engine/client/client.h index 522d5268..a281512d 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -662,6 +662,7 @@ typedef struct qboolean internetservers_wait; // internetservers is waiting for dns request qboolean internetservers_pending; // internetservers is waiting for dns request qboolean legacymode; // one-way 48 protocol compatibility + netadr_t legacyserver; } client_static_t; #ifdef __cplusplus diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index c772545d..b97a5c4e 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -22,7 +22,7 @@ GNU General Public License for more details. #define XASH_COLORIZE_CONSOLE // use with caution, running engine in Qt Creator may cause a freeze in read() call // I was never encountered this bug anywhere else, so still enable by default -#define XASH_USE_SELECT +// #define XASH_USE_SELECT #endif #ifdef XASH_USE_SELECT From 1866c007898ea9a9214adfa506077c2cc6226a0b Mon Sep 17 00:00:00 2001 From: mittorn Date: Sat, 26 Jan 2019 17:53:03 +0000 Subject: [PATCH 190/205] legacymode: fix dedicated server build --- engine/common/net_encode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 556e9bec..4610d729 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -1603,6 +1603,7 @@ Read the clientdata */ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, float timebase ) { +#ifndef XASH_DEDICATED delta_t *pField; delta_info_t *dt; int i; @@ -1623,6 +1624,7 @@ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, f { Delta_ReadField( msg, pField, from, to, timebase ); } +#endif } /* From b534422aede830e70cb4c50ae8aae949372fceca Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 27 Jan 2019 02:06:06 +0700 Subject: [PATCH 191/205] Clean delta when getting delta from server --- engine/common/net_encode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 4610d729..44a13f6f 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -543,7 +543,8 @@ void Delta_ParseTableField( sizebuf_t *msg ) post_mul = MSG_ReadFloat( msg ); // delta encoders it's already initialized on this machine (local game) - if( delta_init ) return; + if( delta_init ) + Delta_Shutdown(); // add field to table Delta_AddField( dt->pName, pName, flags, bits, mul, post_mul ); From 5e65df3c2ad99855492ce1c2d3fa2b780e05cfed Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 27 Jan 2019 02:47:43 +0700 Subject: [PATCH 192/205] legacymode: fix numFields for movevars_t --- engine/common/net_encode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 44a13f6f..5cbedf89 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -837,6 +837,7 @@ void Delta_Init( void ) Delta_AddField( "movevars_t", "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f ); + dt->numFields = NUM_FIELDS( pm_fields ) - 4; // now done dt->bInitialized = true; From 1a6fd72d19c03f5ef673d2b583b01fbae9a5f55a Mon Sep 17 00:00:00 2001 From: mittorn Date: Sun, 27 Jan 2019 02:48:09 +0700 Subject: [PATCH 193/205] legacymode: fix choke counter --- engine/client/cl_parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 955e223b..d4f2e8c4 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -2952,6 +2952,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) { if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 ) { + cl.frames[j & CL_UPDATE_MASK].choked = true; cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0; i--; } From cb7820b45b109b31c0bdd86513df592b9f6d5343 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 26 Jan 2019 23:53:20 +0300 Subject: [PATCH 194/205] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 5d0b0e62..995ed39b 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 5d0b0e62b049ad45896405f3c20712d5feae9dea +Subproject commit 995ed39b9a88a20c650df624c309f7cee3ecfdee From 103a2fccaab26afd14fee88491aae81bd0a79adb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Jan 2019 02:26:23 +0300 Subject: [PATCH 195/205] sdl: vid: rework safegl option --- engine/client/gl_local.h | 15 +++-- engine/platform/sdl/vid_sdl.c | 112 +++++++++++++++++----------------- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 0f89427b..fcc77848 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -609,17 +609,20 @@ typedef struct typedef enum { - SAFE_NO, - SAFE_NOACC, - SAFE_NODEPTH, - SAFE_NOATTRIB, - SAFE_DONTCARE + SAFE_NO = 0, + SAFE_NOMSAA, // skip msaa + SAFE_NOACC, // don't set acceleration flag + SAFE_NOSTENCIL, // don't set stencil bits + SAFE_NOALPHA, // don't set alpha bits + SAFE_NODEPTH, // don't set depth bits + SAFE_NOCOLOR, // don't set color bits + SAFE_DONTCARE // ignore everything, let SDL/EGL decide } safe_context_t; typedef struct { void* context; // handle to GL rendering context - safe_context_t safe; + int safe; int desktopBitsPixel; int desktopWidth; diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 50081756..4c62d37b 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -585,14 +585,14 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !host.hWnd ) { - Con_Reportf( S_ERROR "VID_CreateWindow: couldn't create '%s': %s\n", wndname, SDL_GetError()); + Con_Reportf( S_ERROR "VID_CreateWindow: couldn't create '%s': %s\n", wndname, SDL_GetError()); - // remove MSAA, if it present, because - // window creating may fail on GLX visual choose - if( gl_wgl_msaa_samples->value || glw_state.safe >= 0 ) + // skip some attribs in hope that context creating will not fail + if( glw_state.safe >= SAFE_NO ) { - Cvar_Set( "gl_wgl_msaa_samples", "0" ); - glw_state.safe++; + if( !gl_wgl_msaa_samples->value && glw_state.safe + 1 == SAFE_NOMSAA ) + glw_state.safe += 2; // no need to skip msaa, if we already disabled it + else glw_state.safe++; GL_SetupAttributes(); // re-choose attributes // try again @@ -719,11 +719,9 @@ static void GL_SetupAttributes( void ) SDL_GL_ResetAttributes(); - #ifdef XASH_GLES SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES ); SDL_GL_SetAttribute( SDL_GL_CONTEXT_EGL, 1 ); - #ifdef XASH_NANOGL SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 ); SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 ); @@ -731,7 +729,6 @@ static void GL_SetupAttributes( void ) SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 ); SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 ); #endif - #else // GL1.x #ifndef XASH_GL_STATIC if( Sys_CheckParm( "-gldebug" ) ) @@ -754,77 +751,89 @@ static void GL_SetupAttributes( void ) } #endif // XASH_GLES - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - if( glw_state.safe > SAFE_DONTCARE ) { - glw_state.safe = -1; + glw_state.safe = -1; // can't retry anymore, can only shutdown engine return; } - if( glw_state.safe > SAFE_NO ) - Msg("Trying safe opengl mode %d\n", glw_state.safe ); + Msg( "Trying safe opengl mode %d\n", glw_state.safe ); - if( glw_state.safe >= SAFE_NOACC ) + if( glw_state.safe == SAFE_DONTCARE ) + return; + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + + if( glw_state.safe < SAFE_NOACC ) SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ); - Msg ("bpp %d\n", glw_state.desktopBitsPixel ); + Msg( "bpp %d\n", glw_state.desktopBitsPixel ); + + if( glw_state.safe < SAFE_NOSTENCIL ) + SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, gl_stencilbits->value ); + + if( glw_state.safe < SAFE_NOALPHA ) + SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); if( glw_state.safe < SAFE_NODEPTH ) SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); - else if( glw_state.safe < 5 ) + else SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 8 ); - - if( glw_state.safe < SAFE_NOATTRIB ) + if( glw_state.safe < SAFE_NOCOLOR ) { if( glw_state.desktopBitsPixel >= 24 ) { - if( glw_state.desktopBitsPixel == 32 ) - SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); - SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); } - else + else if( glw_state.desktopBitsPixel >= 16 ) { SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 6 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 ); } + else + { + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 3 ); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 3 ); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 2 ); + } } - if( glw_state.safe >= SAFE_DONTCARE ) - return; - - SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, gl_stencilbits->value ); - - switch( (int)gl_wgl_msaa_samples->value ) + if( glw_state.safe < SAFE_NOMSAA ) { - case 2: - case 4: - case 8: - case 16: - samples = gl_wgl_msaa_samples->value; - break; - default: - samples = 0; // don't use, because invalid parameter is passed - } + switch( (int)gl_wgl_msaa_samples->value ) + { + case 2: + case 4: + case 8: + case 16: + samples = gl_wgl_msaa_samples->value; + break; + default: + samples = 0; // don't use, because invalid parameter is passed + } - if( samples ) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples); + if( samples ) + { + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, samples ); - glConfig.max_multisamples = samples; + glConfig.max_multisamples = samples; + } + else + { + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 0 ); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0 ); + + glConfig.max_multisamples = 0; + } } else { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); - - glConfig.max_multisamples = 0; + Cvar_Set( "gl_wgl_msaa_samples", "0" ); } } @@ -849,14 +858,7 @@ qboolean R_Init_Video( void ) glw_state.desktopHeight = displayMode.h; if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) ) - { - glw_state.safe = Q_atoi( safe ); - if( glw_state.safe < SAFE_NOACC || glw_state.safe > SAFE_DONTCARE ) - glw_state.safe = SAFE_DONTCARE; - } - - if( glw_state.safe < SAFE_NO || glw_state.safe > SAFE_DONTCARE ) - return false; + glw_state.safe = bound( SAFE_NO, Q_atoi( safe ), SAFE_DONTCARE ); GL_SetupAttributes(); From 0801922466416e64541bfa67630811d5e89d3e42 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Jan 2019 02:27:12 +0300 Subject: [PATCH 196/205] cvar: don't change cvars without FCVAR_GLCONFIG variable during opengl.cfg reading --- engine/client/vid_common.c | 6 +++--- engine/common/cvar.c | 28 +++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 357f04a2..ad942cec 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -439,7 +439,7 @@ void GL_InitCommands( void ) r_speeds = Cvar_Get( "r_speeds", "0", FCVAR_ARCHIVE, "shows renderer speeds" ); r_fullbright = Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); r_norefresh = Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); - r_showtree = Cvar_Get( "r_showtree", "0", FCVAR_ARCHIVE, "build the graph of visible BSP tree" ); + r_showtree = Cvar_Get( "r_showtree", "0", FCVAR_ARCHIVE, "build the graph of visible BSP tree" ); r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", FCVAR_ARCHIVE, "allow to get lighting from world and bmodels" ); r_lighting_modulate = Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "lightstyles modulate scale" ); r_lighting_ambient = Cvar_Get( "r_lighting_ambient", "0.3", FCVAR_ARCHIVE, "map ambient lighting scale" ); @@ -473,8 +473,8 @@ void GL_InitCommands( void ) gl_clear = Cvar_Get( "gl_clear", "0", FCVAR_ARCHIVE, "clearing screen after each frame" ); gl_test = Cvar_Get( "gl_test", "0", 0, "engine developer cvar for quick testing new features" ); gl_wireframe = Cvar_Get( "gl_wireframe", "0", FCVAR_ARCHIVE|FCVAR_SPONLY, "show wireframe overlay" ); - gl_wgl_msaa_samples = Cvar_Get( "gl_wgl_msaa_samples", "4", FCVAR_GLCONFIG, "enable multisample anti-aliasing" ); - gl_msaa = Cvar_Get( "gl_msaa", "2", FCVAR_ARCHIVE, "enable multi sample anti-aliasing" ); + gl_wgl_msaa_samples = Cvar_Get( "gl_wgl_msaa_samples", "0", FCVAR_GLCONFIG, "samples number for multisample anti-aliasing" ); + gl_msaa = Cvar_Get( "gl_msaa", "1", FCVAR_ARCHIVE, "enable or disable multisample anti-aliasing" ); gl_stencilbits = Cvar_Get( "gl_stencilbits", "8", FCVAR_GLCONFIG, "pixelformat stencil bits (0 - auto)" ); gl_round_down = Cvar_Get( "gl_round_down", "2", FCVAR_RENDERINFO, "round texture sizes to nearest POT value" ); // these cvar not used by engine but some mods requires this diff --git a/engine/common/cvar.c b/engine/common/cvar.c index bb1df89f..39c63de9 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -710,6 +710,26 @@ void Cvar_SetCheatState( void ) } } +/* +============ +Cvar_SetGL + +As Cvar_Set, but also flags it as glconfig +============ +*/ +static void Cvar_SetGL( const char *name, const char *value ) +{ + convar_t *var = Cvar_FindVar( name ); + + if( var && !FBitSet( var->flags, FCVAR_GLCONFIG )) + { + Con_Reportf( S_ERROR "Can't set non-GL cvar %s to %s\n", name, value ); + return; + } + + Cvar_FullSet( name, value, FCVAR_GLCONFIG ); +} + /* ============ Cvar_Command @@ -722,7 +742,7 @@ qboolean Cvar_Command( convar_t *v ) // special case for setup opengl configuration if( host.apply_opengl_config ) { - Cvar_FullSet( Cmd_Argv( 0 ), Cmd_Argv( 1 ), FCVAR_GLCONFIG ); + Cvar_SetGL( Cmd_Argv( 0 ), Cmd_Argv( 1 ) ); return true; } @@ -811,13 +831,15 @@ As Cvar_Set, but also flags it as glconfig */ void Cvar_SetGL_f( void ) { + convar_t *var; + if( Cmd_Argc() != 3 ) { Con_Printf( S_USAGE "setgl \n" ); return; } - Cvar_FullSet( Cmd_Argv( 1 ), Cmd_Argv( 2 ), FCVAR_GLCONFIG ); + Cvar_SetGL( Cmd_Argv( 1 ), Cmd_Argv( 2 ) ); } /* @@ -910,7 +932,7 @@ void Cvar_Init( void ) cmd_scripting = Cvar_Get( "cmd_scripting", "0", FCVAR_ARCHIVE, "enable simple condition checking and variable operations" ); Cvar_RegisterVariable (&host_developer); // early registering for dev - Cmd_AddCommand( "setgl", Cvar_SetGL_f, "create or change the value of a opengl variable" ); // OBSOLETE + Cmd_AddCommand( "setgl", Cvar_SetGL_f, "change the value of a opengl variable" ); // OBSOLETE Cmd_AddCommand( "toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" ); Cmd_AddCommand( "reset", Cvar_Reset_f, "reset any type variable to initial value" ); Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" ); From 860d3be42d9d37aaa3e29feaef58c0aca65b9850 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Jan 2019 02:42:28 +0300 Subject: [PATCH 197/205] demo: draw recording message a bit higher --- engine/client/cl_demo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 5c069fef..f3e9e69a 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -497,10 +497,10 @@ void CL_DrawDemoRecording( void ) pos = FS_Tell( cls.demofile ); Q_snprintf( string, sizeof( string ), "^1RECORDING:^7 %s: %s time: %02d:%02d", cls.demoname, - Q_memprint( pos ), (int)(cls.demotime / 60.0f ), (int)fmod( cls.demotime, 60.0f )); + Q_memprint( pos ), (int)(cls.demotime / 60.0f ), (int)fmod( cls.demotime, 60.0f )); Con_DrawStringLen( string, &len, NULL ); - Con_DrawString(( glState.width - len ) >> 1, glState.height >> 2, string, color ); + Con_DrawString(( glState.width - len ) >> 1, glState.height >> 4, string, color ); } /* From 6ba406be7d7bf2654eed8b5ad9468b7fc7b62caf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Jan 2019 03:02:16 +0300 Subject: [PATCH 198/205] legacymode: some codestyle fixes --- engine/client/cl_events.c | 2 +- engine/client/cl_frame.c | 20 +++++++++++++++++--- engine/client/cl_parse.c | 28 +++++++++++++++++++++++----- engine/client/cl_tent.c | 6 +++++- engine/common/net_encode.c | 2 +- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index 75c0516b..3dff5921 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -393,7 +393,7 @@ void CL_ParseEvent( sizebuf_t *msg ) event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS ); if( MSG_ReadOneBit( msg )) - packet_index = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_ENTITY_BITS:MAX_ENTITY_BITS ); + packet_index = MSG_ReadUBitLong( msg, cls.legacymode ? MAX_LEGACY_ENTITY_BITS : MAX_ENTITY_BITS ); else packet_index = -1; if( MSG_ReadOneBit( msg )) diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 2d2f4e70..bf964fc6 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -721,7 +721,10 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) CL_WriteDemoJumpTime(); // sentinel count. save it for debug checking - count = cls.legacymode?MSG_ReadWord( msg ) : ( MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1 ); + if( cls.legacymode ) + count = MSG_ReadWord( msg ); + else count = MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1; + newframe = &cl.frames[cl.parsecountmod]; // allocate parse entities @@ -795,8 +798,19 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) while( 1 ) { - newnum = cls.legacymode ? MSG_ReadWord( msg ) : MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); - if( newnum == (cls.legacymode?0:LAST_EDICT) ) break; // end of packet entities + int lastedict; + if( cls.legacymode ) + { + newnum = MSG_ReadWord( msg ); + lastedict = 0; + } + else + { + newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + lastedict = LAST_EDICT; + } + + if( newnum == lastedict ) break; // end of packet entities if( MSG_CheckOverflow( msg )) Host_Error( "CL_ParsePacketEntities: overflow\n" ); player = CL_IsPlayerIndex( newnum ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index d4f2e8c4..f864ac16 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1128,7 +1128,7 @@ void CL_ParseClientData( sizebuf_t *msg ) if( !MSG_ReadOneBit( msg )) break; // read the weapon idx - idx = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_WEAPON_BITS:MAX_WEAPON_BITS ); + idx = MSG_ReadUBitLong( msg, cls.legacymode ? MAX_LEGACY_WEAPON_BITS : MAX_WEAPON_BITS ); MSG_ReadWeaponData( msg, &from_wd[idx], &to_wd[idx], cl.mtime[0] ); } @@ -1295,14 +1295,26 @@ register new user message or update existing void CL_RegisterUserMessage( sizebuf_t *msg ) { char *pszName; - int svc_num, size; + int svc_num, size, bits; svc_num = MSG_ReadByte( msg ); - size = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); + + if( cls.legacymode ) + { + size = MSG_ReadByte( msg ); + bits = 8; + } + else + { + size = MSG_ReadWord( msg ); + bits = 16; + } + pszName = MSG_ReadString( msg ); // important stuff - if( size == (cls.legacymode?0xFF:0xFFFF) ) size = -1; + if( size == ( BIT( bits ) - 1 ) ) + size = -1; svc_num = bound( 0, svc_num, 255 ); CL_LinkUserMessage( pszName, svc_num, size ); @@ -1958,7 +1970,13 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num ) iSize = clgame.msg[i].size; // message with variable sizes receive an actual size as first byte - if( iSize == -1 ) iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); + if( iSize == -1 ) + { + if( cls.legacymode ) + iSize = MSG_ReadByte( msg ); + else iSize = MSG_ReadWord( msg ); + } + if( iSize >= MAX_USERMSG_LENGTH ) { Msg("WTF??? %d %d\n", i, svc_num ); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index d9314195..53529be9 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2024,7 +2024,7 @@ void CL_ParseTempEntity( sizebuf_t *msg ) { sizebuf_t buf; byte pbuf[256]; - int iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg ); + int iSize; int type, color, count, flags; int decalIndex, modelIndex, entityIndex; float scale, life, frameRate, vel, random; @@ -2035,6 +2035,10 @@ void CL_ParseTempEntity( sizebuf_t *msg ) cl_entity_t *pEnt; dlight_t *dl; + if( cls.legacymode ) + iSize = MSG_ReadByte( msg ); + else iSize = MSG_ReadWord( msg ); + decalIndex = modelIndex = entityIndex = 0; // parse user message into buffer diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 5cbedf89..5cdac1e6 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -1890,7 +1890,7 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state to->entityType = MSG_ReadUBitLong( msg, 2 ); to->number = number; - if( cls.legacymode?(to->entityType == ENTITY_BEAM):FBitSet(to->entityType, ENTITY_BEAM) ) + if( cls.legacymode ? ( to->entityType == ENTITY_BEAM ) : FBitSet( to->entityType, ENTITY_BEAM )) { dt = Delta_FindStruct( "custom_entity_state_t" ); } From ed049ea5390e9b78a26898ee6c8b5a3cc2f5281b Mon Sep 17 00:00:00 2001 From: mittorn Date: Mon, 28 Jan 2019 14:09:06 +0000 Subject: [PATCH 199/205] Implement --single-binary and --stdin-input options --- engine/wscript | 19 ++++++++++++++++++- wscript | 2 -- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/engine/wscript b/engine/wscript index db158206..213a88ef 100644 --- a/engine/wscript +++ b/engine/wscript @@ -13,13 +13,19 @@ def options(opt): opt.add_option( '--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, help = 'build engine with BSP2 map support(recommended for Quake, breaks compability!)') + opt.add_option( + '--single-binary', action = 'store_true', dest = 'SINGLE_BINARY', default = None, + help = 'build single "xash" binary instead of xash.dll/libxash.so (default for dedicated)') + opt.add_option( + '--stdin-input', action = 'store_true', dest = 'USE_SELECT', default = None, + help = 'enable console input from stdin (default for dedicated)') + def configure(conf): # check for dedicated server build if conf.options.DEDICATED: if(conf.env.DEST_OS == 'linux'): conf.check_cc( lib='rt' ) - conf.env.append_unique('DEFINES', 'SINGLE_BINARY') conf.env.append_unique('DEFINES', 'XASH_DEDICATED') elif conf.env.DEST_OS2 == 'android': # Android doesn't need SDL2 conf.check_cc(lib='log') @@ -29,6 +35,17 @@ def configure(conf): conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') conf.env.append_unique('DEFINES', 'XASH_SDL') + if conf.options.SINGLE_BINARY == None: + conf.options.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated + conf.env.SINGLE_BINARY = conf.options.SINGLE_BINARY + if conf.options.SINGLE_BINARY: + conf.env.append_unique('DEFINES', 'SINGLE_BINARY') + + if conf.options.USE_SELECT == None: + conf.options.USE_SELECT = conf.options.DEDICATED + if conf.options.USE_SELECT: + conf.env.append_unique('DEFINES', 'XASH_USE_SELECT') + if conf.options.SUPPORT_BSP2_FORMAT: conf.env.append_unique('DEFINES', 'SUPPORT_BSP2_FORMAT') diff --git a/wscript b/wscript index 78003a23..09070f1b 100644 --- a/wscript +++ b/wscript @@ -100,8 +100,6 @@ def configure(conf): linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) conf.env.DEDICATED = conf.options.DEDICATED - conf.env.SINGLE_BINARY = conf.options.DEDICATED # We don't need game launcher on dedicated - if conf.env.DEST_OS == 'linux': conf.check_cc( lib='dl' ) From bbd4c1315e6c8759fa362c6e7439800a7b311857 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 02:32:54 +0700 Subject: [PATCH 200/205] legacymode: implement clc_userinfo --- engine/client/cl_parse.c | 12 ++++++++++++ engine/common/common.h | 1 + engine/common/cvar.c | 1 + engine/common/protocol.h | 2 ++ 4 files changed, 16 insertions(+) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index f864ac16..737e9457 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -3095,3 +3095,15 @@ void CL_LegacyPrecache_f( void ) MSG_WriteString( &cls.netchan.message, va( "begin %i", spawncount )); cls.signon = SIGNONS; } + +void CL_LegacyUpdateInfo( void ) +{ + if( !cls.legacymode ) + return; + + if( cls.state != ca_active ) + return; + + MSG_BeginClientCmd( &cls.netchan.message, clc_legacy_userinfo ); + MSG_WriteString( &cls.netchan.message, cls.userinfo ); +} diff --git a/engine/common/common.h b/engine/common/common.h index 301b140a..c0a60d3b 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -975,6 +975,7 @@ qboolean CL_IsThirdPerson( void ); qboolean CL_IsIntermission( void ); qboolean CL_Initialized( void ); char *CL_Userinfo( void ); +void CL_LegacyUpdateInfo( void ); void CL_CharEvent( int key ); qboolean CL_DisableVisibility( void ); int CL_PointContents( const vec3_t point ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 39c63de9..ab0e4562 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -121,6 +121,7 @@ static qboolean Cvar_UpdateInfo( convar_t *var, const char *value, qboolean noti // time to update server copy of userinfo CL_ServerCommand( true, "setinfo \"%s\" \"%s\"\n", var->name, value ); + CL_LegacyUpdateInfo(); } #endif } diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 6564b571..d9c6486e 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -253,6 +253,8 @@ extern const char *clc_strings[clc_lastmsg+1]; #define svc_legacy_event 27 // playback event queue #define svc_legacy_changing 3 // changelevel by server request +#define clc_legacy_userinfo 6 // [[userinfo string] + #define SND_LEGACY_LARGE_INDEX (1<<2) // a send sound as short #define MAX_LEGACY_ENTITY_BITS 12 #define MAX_LEGACY_WEAPON_BITS 5 From 98bf05b948548db4e1e8dd782b7049ebb6bdc5c9 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 16:38:19 +0700 Subject: [PATCH 201/205] identification: fix network device detection --- engine/common/identification.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/identification.c b/engine/common/identification.c index 792a9786..0ee61750 100644 --- a/engine/common/identification.c +++ b/engine/common/identification.c @@ -206,8 +206,8 @@ qboolean ID_ValidateNetDevice( const char *dev ) int assignType; // These devices are fake, their mac address is generated each boot, while assign_type is 0 - if( Q_strnicmp( dev, "ccmni", sizeof( "ccmni" ) ) || - Q_strnicmp( dev, "ifb", sizeof( "ifb" ) ) ) + if( !Q_strnicmp( dev, "ccmni", sizeof( "ccmni" ) ) || + !Q_strnicmp( dev, "ifb", sizeof( "ifb" ) ) ) return false; pfile = FS_LoadDirectFile( va( "%s/%s/addr_assign_type", prefix, dev ), NULL ); From 52fca4ac0cf682e34ea9746034113ecf07039d1f Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 17:00:40 +0700 Subject: [PATCH 202/205] legacymode: send identification --- engine/client/cl_main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ae4b0147..29952b99 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1012,17 +1012,25 @@ void CL_SendConnectPacket( void ) key = ID_GetMD5(); memset( protinfo, 0, sizeof( protinfo )); - Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); - Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); if( cls.legacymode ) { - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", - PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo ); + /// TODO: add input devices list + //Info_SetValueForKey( protinfo, "d", va( "%d", input_devices ), sizeof( protinfo ) ); + Info_SetValueForKey( protinfo, "v", XASH_VERSION, sizeof( protinfo ) ); + Info_SetValueForKey( protinfo, "b", va( "%d", Q_buildnum() ), sizeof( protinfo ) ); + Info_SetValueForKey( protinfo, "o", Q_buildos(), sizeof( protinfo ) ); + Info_SetValueForKey( protinfo, "a", Q_buildarch(), sizeof( protinfo ) ); + Info_SetValueForKey( protinfo, "i", ID_GetMD5(), sizeof( protinfo ) ); + + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" 0 \"%s\"\n", + PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo, protinfo ); Con_Printf( "Trying to connect by legacy protocol\n" ); } else { + Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); + Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); Con_Printf( "Trying to connect by modern protocol\n" ); } From f3ae5159cb37a9d5822489c92f4a3e5da6e24c0e Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 17:27:36 +0700 Subject: [PATCH 203/205] Add endian conversion macros --- engine/common/common.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/engine/common/common.h b/engine/common/common.h index c0a60d3b..0ee4dfd2 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -120,6 +120,37 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc #define GAME_EXPORT #endif + +#ifdef XASH_BIG_ENDIAN +#define LittleLong(x) (((int)(((x)&255)<<24)) + ((int)((((x)>>8)&255)<<16)) + ((int)(((x)>>16)&255)<<8) + (((x) >> 24)&255)) +#define LittleLongSW(x) (x = LittleLong(x) ) +#define LittleShort(x) ((short)( (((short)(x) >> 8) & 255) + (((short)(x) & 255) << 8))) +#define LittleShortSW(x) (x = LittleShort(x) ) +_inline float LittleFloat( float f ) +{ + union + { + float f; + unsigned char b[4]; + } dat1, dat2; + + dat1.f = f; + dat2.b[0] = dat1.b[3]; + dat2.b[1] = dat1.b[2]; + dat2.b[2] = dat1.b[1]; + dat2.b[3] = dat1.b[0]; + + return dat2.f; +} +#else +#define LittleLong(x) (x) +#define LittleLongSW(x) +#define LittleShort(x) (x) +#define LittleShortSW(x) +#define LittleFloat(x) (x) +#endif + + typedef unsigned int dword; typedef unsigned int uint; typedef char string[MAX_STRING]; From f044a59984067f85c448b8ef3234a82c5bdbe4a9 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 17:28:39 +0700 Subject: [PATCH 204/205] Port old netsplit implementation --- engine/common/net_chan.c | 135 +++++++++++++++++++++++++++++++++++++++ engine/common/netchan.h | 45 +++++++++++++ 2 files changed, 180 insertions(+) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index a5c744c4..cabb0b1a 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -102,6 +102,141 @@ const char *ns_strings[NS_COUNT] = "Server", }; + +/* +================================= + +NETWORK PACKET SPLIT + +================================= +*/ + +/* +====================== +NetSplit_GetLong + +Collect fragmrnts with signature 0xFFFFFFFE to single packet +return true when got full packet +====================== +*/ +qboolean NetSplit_GetLong( netsplit_t *ns, netadr_t *from, byte *data, size_t *length ) +{ + netsplit_packet_t *packet = (netsplit_packet_t*)data; + netsplit_chain_packet_t * p; + + //ASSERT( *length > NETSPLIT_HEADER_SIZE ); + if( *length <= NETSPLIT_HEADER_SIZE ) return false; + + LittleLongSW(packet->id); + LittleLongSW(packet->length); + LittleLongSW(packet->part); + + p = &ns->packets[packet->id & NETSPLIT_BACKUP_MASK]; + // Con_Reportf( S_NOTE "NetSplit_GetLong: packet from %s, id %d, index %d length %d\n", NET_AdrToString( *from ), (int)packet->id, (int)packet->index, (int)*length ); + + // no packets with this id received + if( packet->id != p->id ) + { + // warn if previous packet not received + if( p->received < p->count ) + { + //CL_WarnLostSplitPacket(); + Con_Reportf( S_WARN "NetSplit_GetLong: lost packet %d\n", p->id ); + } + + p->id = packet->id; + p->count = packet->count; + p->received = 0; + memset( p->recieved_v, 0, 32 ); + } + + // use bool vector to detect dup packets + if( p->recieved_v[packet->index >> 5 ] & ( 1 << ( packet->index & 31 ) ) ) + { + Con_Reportf( S_WARN "NetSplit_GetLong: dup packet from %s\n", NET_AdrToString( *from ) ); + return false; + } + + p->received++; + + // mark as received + p->recieved_v[packet->index >> 5] |= 1 << ( packet->index & 31 ); + + // prevent overflow + if( packet->part * packet->index > NET_MAX_PAYLOAD ) + { + Con_Reportf( S_WARN "NetSplit_GetLong: packet out fo bounds from %s (part %d index %d)\n", NET_AdrToString( *from ), packet->part, packet->index ); + return false; + } + + if( packet->length > NET_MAX_PAYLOAD ) + { + Con_Reportf( S_WARN "NetSplit_GetLong: packet out fo bounds from %s (length %d)\n", NET_AdrToString( *from ), packet->length ); + return false; + } + + memcpy( p->data + packet->part * packet->index, packet->data, *length - 18 ); + + // rewrite results of NET_GetPacket + if( p->received == packet->count ) + { + //ASSERT( packet->length % packet->part == (*length - NETSPLIT_HEADER_SIZE) % packet->part ); + size_t len = packet->length; + + ns->total_received += len; + + ns->total_received_uncompressed += len; + *length = len; + + // Con_Reportf( S_NOTE "NetSplit_GetLong: packet from %s, id %d received %d length %d\n", NET_AdrToString( *from ), (int)packet->id, (int)p->received, (int)packet->length ); + memcpy( data, p->data, len ); + return true; + } + else + *length = NETSPLIT_HEADER_SIZE + packet->part; + + + return false; +} + +/* +====================== +NetSplit_SendLong + +Send parts that are less or equal maxpacket +====================== +*/ +void NetSplit_SendLong( netsrc_t sock, size_t length, void *data, netadr_t to, unsigned int maxpacket, unsigned int id) +{ + netsplit_packet_t packet = {0}; + unsigned int part = maxpacket - NETSPLIT_HEADER_SIZE; + + packet.signature = LittleLong(0xFFFFFFFE); + packet.id = LittleLong(id); + packet.length = LittleLong(length); + packet.part = LittleLong(part); + packet.count = ( length - 1 ) / part + 1; + + //Con_Reportf( S_NOTE "NetSplit_SendLong: packet to %s, count %d, length %d\n", NET_AdrToString( to ), (int)packet.count, (int)packet.length ); + + while( packet.index < packet.count ) + { + unsigned int size = part; + + if( size > length ) + size = length; + + length -= size; + + memcpy( packet.data, (const byte*)data + packet.index * part, size ); + //Con_Reportf( S_NOTE "NetSplit_SendLong: packet to %s, id %d, index %d\n", NET_AdrToString( to ), (int)packet.id, (int)packet.index ); + + NET_SendPacket( sock, size + NETSPLIT_HEADER_SIZE, &packet, to ); + packet.index++; + } + +} + /* =============== Netchan_Init diff --git a/engine/common/netchan.h b/engine/common/netchan.h index d1d60435..9d588faa 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -84,6 +84,47 @@ GNU General Public License for more details. #define NUM_PACKET_ENTITIES 256 // 170 Mb for multiplayer with 32 players #define MAX_CUSTOM_BASELINES 64 +#define NET_EXT_SPLIT (1U<<1) +#define NETSPLIT_BACKUP 8 +#define NETSPLIT_BACKUP_MASK (NETSPLIT_BACKUP - 1) +#define NETSPLIT_HEADER_SIZE 18 + +typedef struct netsplit_chain_packet_s +{ + // bool vector + unsigned int recieved_v[8]; + // serial number + unsigned int id; + byte data[NET_MAX_PAYLOAD]; + byte received; + byte count; +} netsplit_chain_packet_t; + +// raw packet format +typedef struct netsplit_packet_s +{ + unsigned int signature; // 0xFFFFFFFE + unsigned int length; + unsigned int part; + unsigned int id; + // max 256 parts + byte count; + byte index; + byte data[NET_MAX_PAYLOAD - NETSPLIT_HEADER_SIZE]; +} netsplit_packet_t; + + +typedef struct netsplit_s +{ + netsplit_chain_packet_t packets[NETSPLIT_BACKUP]; + integer64 total_received; + integer64 total_received_uncompressed; +} netsplit_t; + +// packet splitting +qboolean NetSplit_GetLong( netsplit_t *ns, netadr_t *from, byte *data, size_t *length ); + + /* ============================================================== @@ -203,6 +244,10 @@ typedef struct netchan_s // added for net_speeds size_t total_sended; size_t total_received; + qboolean split; + unsigned int maxpacket; + unsigned int splitid; + netsplit_t netsplit; } netchan_t; extern netadr_t net_from; From 480ef0a468b3d7e80f610e657ce7014a51553aca Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 29 Jan 2019 19:01:21 +0700 Subject: [PATCH 205/205] legacymode: add netsplit support (incoming only) --- engine/client/cl_main.c | 34 +++++++++++++++++++++++++++++++++- engine/client/cl_parse.c | 5 +++++ engine/common/net_chan.c | 2 +- engine/common/net_ws.c | 5 ++++- engine/common/net_ws.h | 4 ++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 29952b99..e1d86ee8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1015,6 +1015,13 @@ void CL_SendConnectPacket( void ) if( cls.legacymode ) { + // set related userinfo keys + if( cl_dlmax->value >= 40000 || cl_dlmax->value < 100 ) + Cvar_FullSet( "cl_maxpacket", "1400", FCVAR_USERINFO ); + else + Cvar_FullSet( "cl_maxpacket", cl_dlmax->string, FCVAR_USERINFO ); + Cvar_FullSet( "cl_maxpayload", "1000", FCVAR_USERINFO ); + /// TODO: add input devices list //Info_SetValueForKey( protinfo, "d", va( "%d", input_devices ), sizeof( protinfo ) ); Info_SetValueForKey( protinfo, "v", XASH_VERSION, sizeof( protinfo ) ); @@ -1023,12 +1030,15 @@ void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "a", Q_buildarch(), sizeof( protinfo ) ); Info_SetValueForKey( protinfo, "i", ID_GetMD5(), sizeof( protinfo ) ); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" 0 \"%s\"\n", + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" 2 \"%s\"\n", PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo, protinfo ); Con_Printf( "Trying to connect by legacy protocol\n" ); } else { + // remove useless userinfo keys + Cvar_FullSet( "cl_maxpacket", "0", 0 ); + Cvar_FullSet( "cl_maxpayload", "1000", 0 ); Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); @@ -1365,6 +1375,19 @@ void CL_Reconnect( qboolean setup_netchan ) if( setup_netchan ) { Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, CL_GetFragmentSize ); + + if( cls.legacymode ) + { + unsigned int extensions = Q_atoi( Cmd_Argv( 1 ) ); + + if( extensions & NET_EXT_SPLIT ) + { + // only enable incoming split for legacy mode + cls.netchan.split = true; + Con_Reportf( "^2NET_EXT_SPLIT enabled^7 (packet sizes is %d/%d)\n", (int)cl_dlmax->value, 65536 ); + } + } + } else { @@ -2036,6 +2059,11 @@ void CL_ReadNetMessage( void ) while( CL_GetMessage( net_message_buffer, &curSize )) { + if( cls.legacymode && *((int *)&net_message_buffer) == 0xFFFFFFFE ) + // Will rewrite existing packet by merged + if( !NetSplit_GetLong( &cls.netchan.netsplit, &net_from, net_message_buffer, &curSize ) ) + continue; + MSG_Init( &net_message, "ServerData", net_message_buffer, curSize ); // check for connectionless packet (0xffffffff) first @@ -2653,6 +2681,10 @@ void CL_InitLocal( void ) Cvar_Get( "team", "", FCVAR_USERINFO, "player team" ); Cvar_Get( "skin", "", FCVAR_USERINFO, "player skin" ); + // legacy mode cvars (need this to add it to userinfo) + Cvar_Get( "cl_maxpacket", "0", 0, "legacy server compatibility" ); + Cvar_Get( "cl_maxpayload", "1000", 0, "legacy server compatibility" ); + cl_showfps = Cvar_Get( "cl_showfps", "1", FCVAR_ARCHIVE, "show client fps" ); cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", FCVAR_ARCHIVE, "disable smooth up stair climbing and interpolate position in multiplayer" ); cl_smoothtime = Cvar_Get( "cl_smoothtime", "0", FCVAR_ARCHIVE, "time to smooth up" ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 737e9457..db23748d 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -3107,3 +3107,8 @@ void CL_LegacyUpdateInfo( void ) MSG_BeginClientCmd( &cls.netchan.message, clc_legacy_userinfo ); MSG_WriteString( &cls.netchan.message, cls.userinfo ); } + +qboolean CL_LegacyMode( void ) +{ + return cls.legacymode; +} diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index cabb0b1a..b2db3dbf 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -189,7 +189,7 @@ qboolean NetSplit_GetLong( netsplit_t *ns, netadr_t *from, byte *data, size_t *l *length = len; // Con_Reportf( S_NOTE "NetSplit_GetLong: packet from %s, id %d received %d length %d\n", NET_AdrToString( *from ), (int)packet->id, (int)p->received, (int)packet->length ); - memcpy( data, p->data, len ); + memcpy( data, p->data, len ); return true; } else diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index beeaa59e..08e03261 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1218,7 +1218,10 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len // Transfer data memcpy( data, buf, ret ); *length = ret; - +#ifndef XASH_DEDICATED + if( CL_LegacyMode() ) + return NET_LagPacket( true, sock, from, length, data ); +#endif // check for split message if( *(int *)data == NET_HEADER_SPLITPACKET ) { diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index c946dd3f..c8f45d6f 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -63,4 +63,8 @@ qboolean NET_BufferToBufferDecompress( char *dest, uint *destLen, char *source, void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ); void NET_ClearLagData( qboolean bClient, qboolean bServer ); +#ifndef XASH_DEDICATED +qboolean CL_LegacyMode( void ); +#endif + #endif//NET_WS_H