diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f033e13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,319 @@ +# Binaries +*.o +*.so +*.a +*.framework + +# Other +*.save +# Qt Creator for some reason creates *.user.$version files, so exclude it too +*.user* +*~ + +### Xcode ### +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.xcuserstate + + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Waf ### +*.lock* +*.waf* +*.pyc + +### CMake ### +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +install_manifest.txt +# makedepend +Makefile.dep +*.bak +ALL_BUILD.* +INSTALL.* +ZERO_CHECK.* + +# Visual Studio +*.obj +*.dll +*.exp +*.lib +*.suo +*.sdf +Debug/ +Release/ +ipch/ +*.opensdf +# Use CMake for generating projects +*.vcxproj.filters +*.vcxproj +*.sln + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# PVS Studio for Linux output +*.cl.cfg + +# Kate +*.kate-swp +*.swp + +# QtCreator +build-* + +# Android +*.apk + +# Visual Studio CMake support +CMakeSettings.json + +# rpmbuild +RPMS/ diff --git a/cl_dll/StudioModelRenderer.cpp b/cl_dll/StudioModelRenderer.cpp index b272a50..5e5886f 100644 --- a/cl_dll/StudioModelRenderer.cpp +++ b/cl_dll/StudioModelRenderer.cpp @@ -28,6 +28,7 @@ #include "r_studioint.h" #include "StudioModelRenderer.h" +#include "exportdef.h" // Global engine <-> studio model rendering code interface engine_studio_api_t IEngineStudio; @@ -1872,7 +1873,6 @@ HUD_GetStudioModelInterface Export this function for the engine to use the studio renderer class to render objects. ==================== */ -#define DLLEXPORT __declspec( dllexport ) extern "C" int DLLEXPORT HUD_GetStudioModelInterface( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio ) { if ( version != STUDIO_INTERFACE_VERSION ) @@ -1889,4 +1889,4 @@ extern "C" int DLLEXPORT HUD_GetStudioModelInterface( int version, struct r_stud // Success return 1; -} \ No newline at end of file +} diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 3069215..c849d4c 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -29,16 +29,14 @@ extern "C" } #include -#include - -#define DLLEXPORT __declspec( dllexport ) - +// #include +#include "exportdef.h" cl_enginefunc_t gEngfuncs; render_api_t gRenderfuncs; CHud gHUD; int g_iXashEngine = FALSE; -BOOL g_fRenderInitialized = FALSE; +qboolean g_fRenderInitialized = FALSE; void InitInput (void); void EV_HookEvents( void ); void IN_Commands( void ); @@ -80,7 +78,8 @@ HUD_GetRect Vgui stub ================================ */ -int *HUD_GetRect( void ) +// a1ba: not supposed to run under GoldSrc, so why we should care about VGui_ViewportPaintBackground? +/*int *HUD_GetRect( void ) { RECT wrect; static int extent[4]; @@ -103,7 +102,7 @@ int *HUD_GetRect( void ) } } return extent; -} +}*/ /* ================================ @@ -296,7 +295,8 @@ Called by engine every frame that client .dll is loaded void DLLEXPORT HUD_Frame( double time ) { - gEngfuncs.VGui_ViewportPaintBackground( HUD_GetRect( )); + // a1ba: not supposed to run under GoldSrc, so why we should care about VGui_ViewportPaintBackground? + // gEngfuncs.VGui_ViewportPaintBackground( HUD_GetRect( )); } /* @@ -383,4 +383,4 @@ int DLLEXPORT HUD_GetRenderInterface( int version, render_api_t *renderfuncs, re g_fRenderInitialized = TRUE; return TRUE; -} \ No newline at end of file +} diff --git a/cl_dll/cl_dll.dsp b/cl_dll/cl_dll.dsp index 3a8d8cc..be6e9d4 100644 --- a/cl_dll/cl_dll.dsp +++ b/cl_dll/cl_dll.dsp @@ -79,7 +79,7 @@ SOURCE="$(InputPath)" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /G5 /MDd /W3 /Gm /GR /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\pm_shared" /I "..\engine" /I "..\utils\vgui\include" /I "..\game_shared" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_WINDOWS" /D "CLIENT_DLL" /D "CLIENT_WEAPONS" /FAs /FR /YX /FD /c +# ADD CPP /nologo /G5 /MDd /W3 /Gm /GR /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\pm_shared" /I "..\engine" /I "..\utils\vgui\include" /I "..\game_shared" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_WINDOWS" /D "CLIENT_DLL" /D "CLIENT_WEAPONS" /D "GOLDSOURCE_SUPPORT" /FAs /FR /YX /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" diff --git a/cl_dll/cl_dll.h b/cl_dll/cl_dll.h index e1b16c1..1ea3f54 100644 --- a/cl_dll/cl_dll.h +++ b/cl_dll/cl_dll.h @@ -31,7 +31,11 @@ typedef float vec_t; typedef int (*pfnUserMsgHook)(const char *pszName, int iSize, void *pbuf); #include "util_vector.h" -#define EXPORT _declspec( dllexport ) +#include "exportdef.h" + +#ifndef _WIN32 +#define _cdecl +#endif // _WIN32 #include "../engine/cdll_int.h" #include "../dlls/quakedef.h" @@ -39,4 +43,4 @@ typedef int (*pfnUserMsgHook)(const char *pszName, int iSize, void *pbuf); extern cl_enginefunc_t gEngfuncs; extern render_api_t gRenderfuncs; -extern struct ref_params_s *gpViewParams; \ No newline at end of file +extern struct ref_params_s *gpViewParams; diff --git a/cl_dll/cl_util.h b/cl_dll/cl_util.h index 0f9e9ea..540e54a 100644 --- a/cl_dll/cl_util.h +++ b/cl_dll/cl_util.h @@ -32,14 +32,14 @@ #define DECLARE_MESSAGE(y, x) int __MsgFunc_##x(const char *pszName, int iSize, void *pbuf) \ { \ - return gHUD.##y.MsgFunc_##x(pszName, iSize, pbuf ); \ + return gHUD.y.MsgFunc_##x(pszName, iSize, pbuf ); \ } #define HOOK_COMMAND(x, y) gEngfuncs.pfnAddCommand( x, __CmdFunc_##y ); #define DECLARE_COMMAND(y, x) void __CmdFunc_##x( void ) \ { \ - gHUD.##y.UserCmd_##x( ); \ + gHUD.y.UserCmd_##x( ); \ } inline float CVAR_GET_FLOAT( const char *x ) { return gEngfuncs.pfnGetCvarFloat( (char*)x ); } @@ -180,4 +180,4 @@ void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ); void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ); void AngleQuaternion( float *angles, vec4_t quaternion ); -struct mleaf_s *Mod_PointInLeaf( Vector p, struct mnode_s *node ); \ No newline at end of file +struct mleaf_s *Mod_PointInLeaf( Vector p, struct mnode_s *node ); diff --git a/cl_dll/entity.cpp b/cl_dll/entity.cpp index 17f8b1f..0ff7dd6 100644 --- a/cl_dll/entity.cpp +++ b/cl_dll/entity.cpp @@ -22,8 +22,7 @@ #include "pm_shared.h" #include "r_studioint.h" #include "com_model.h" - -#define DLLEXPORT __declspec( dllexport ) +#include "exportdef.h" void VectorAngles( const float *forward, float *angles ); @@ -773,4 +772,4 @@ Indices must start at 1, not zero. cl_entity_t DLLEXPORT *HUD_GetUserEntity( int index ) { return NULL; -} \ No newline at end of file +} diff --git a/cl_dll/hud_redraw.cpp b/cl_dll/hud_redraw.cpp index 890c229..cf319f0 100644 --- a/cl_dll/hud_redraw.cpp +++ b/cl_dll/hud_redraw.cpp @@ -119,7 +119,8 @@ int CHud :: DrawHudNumberString( int xpos, int ypos, int iMinX, int iNumber, int int CHud :: DrawHudStringReverse( int xpos, int ypos, int iMinX, char *szString, int r, int g, int b ) { // find the end of the string - for ( char *szIt = szString; *szIt != 0; szIt++ ) + char *szIt; + for ( szIt = szString; *szIt != 0; szIt++ ) { // we should count the length? } @@ -135,4 +136,4 @@ int CHud :: DrawHudStringReverse( int xpos, int ypos, int iMinX, char *szString, } return xpos; -} \ No newline at end of file +} diff --git a/cl_dll/in_camera.cpp b/cl_dll/in_camera.cpp index b79aee6..03e5523 100644 --- a/cl_dll/in_camera.cpp +++ b/cl_dll/in_camera.cpp @@ -15,7 +15,13 @@ #include "camera.h" #include "in_defs.h" #include "cl_entity.h" +#ifdef _WIN32 #include "windows.h" +#else // _WIN32 +// unused functionality, so it's safe to just throw it out here +#define GetCursorPos( ppt ) +#define SetCursorPos( x, y ) +#endif // _WIN32 float CL_KeyState (kbutton_t *key); @@ -75,6 +81,12 @@ int iMouseInUse=0; int cam_distancemove; extern int mouse_x, mouse_y; //used to determine what the current x and y values are int cam_old_mouse_x, cam_old_mouse_y; //holds the last ticks mouse movement +#ifndef _WIN32 +struct POINT +{ + int x, y; +}; +#endif POINT cam_mouse; //-------------------------------------------------- Local Variables @@ -618,4 +630,4 @@ int DLLEXPORT CL_IsThirdPerson( void ) void DLLEXPORT CL_CameraOffset( float *ofs ) { VectorCopy( cam_ofs, ofs ); -} \ No newline at end of file +} diff --git a/cl_dll/in_defs.h b/cl_dll/in_defs.h index 0763c1c..bbb4de6 100644 --- a/cl_dll/in_defs.h +++ b/cl_dll/in_defs.h @@ -16,6 +16,6 @@ // fall over #define ROLL 2 -#define DLLEXPORT __declspec( dllexport ) +#include "exportdef.h" -#endif \ No newline at end of file +#endif diff --git a/cl_dll/input.cpp b/cl_dll/input.cpp index aa50ec5..62354f7 100644 --- a/cl_dll/input.cpp +++ b/cl_dll/input.cpp @@ -29,6 +29,7 @@ extern "C" #include "r_studioint.h" #include "com_model.h" #include "cl_entity.h" +#include "exportdef.h" extern "C" { diff --git a/cl_dll/input_goldsource.cpp b/cl_dll/input_goldsource.cpp new file mode 100644 index 0000000..c45841c --- /dev/null +++ b/cl_dll/input_goldsource.cpp @@ -0,0 +1,1607 @@ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +// in_win.c -- windows 95 mouse and joystick code +// 02/21/97 JCB Added extended DirectInput code to support external controllers. + +#include "input_mouse.h" + +#ifdef SUPPORT_GOLDSOURCE_INPUT + +#include "hud.h" +#include "cl_util.h" +#include "camera.h" +#include "kbutton.h" +#include "cvardef.h" +#include "const.h" +#include "camera.h" +#include "in_defs.h" +#include "keydefs.h" +#include "view.h" + +#ifndef _WIN32 +#define USE_SDL2 +#endif + +#ifdef USE_SDL2 +#define ARRAYSIZE(p) ( sizeof(p) /sizeof(p[0]) ) +#include +#include +#include +int (*pfnSDL_SetRelativeMouseMode)(SDL_bool); +Uint32 (*pfnSDL_GetRelativeMouseState)(int* x, int* y); +int (*pfnSDL_NumJoysticks)(void); +SDL_bool (*pfnSDL_IsGameController)(int); +SDL_GameController* (*pfnSDL_GameControllerOpen)(int); +Sint16 (*pfnSDL_GameControllerGetAxis)(SDL_GameController*, SDL_GameControllerAxis); +Uint8 (*pfnSDL_GameControllerGetButton)(SDL_GameController*, SDL_GameControllerButton); +void (*pfnSDL_JoystickUpdate)(void); +const char* (*pfnSDL_GameControllerName)(SDL_GameController*); + +int safe_pfnSDL_SetRelativeMouseMode(SDL_bool mode) +{ + if (pfnSDL_SetRelativeMouseMode) + return pfnSDL_SetRelativeMouseMode(mode); + return -1; +} +Uint32 safe_pfnSDL_GetRelativeMouseState(int* x, int* y) +{ + if (pfnSDL_GetRelativeMouseState) + return pfnSDL_GetRelativeMouseState(x, y); + return 0; +} +int safe_pfnSDL_NumJoysticks() +{ + if (pfnSDL_NumJoysticks) + return pfnSDL_NumJoysticks(); + return -1; +} +SDL_bool safe_pfnSDL_IsGameController(int joystick_index) +{ + if (pfnSDL_IsGameController) + return pfnSDL_IsGameController(joystick_index); + return SDL_FALSE; +} +SDL_GameController* safe_pfnSDL_GameControllerOpen(int joystick_index) +{ + if (pfnSDL_GameControllerOpen) + return pfnSDL_GameControllerOpen(joystick_index); + return NULL; +} +Sint16 safe_pfnSDL_GameControllerGetAxis(SDL_GameController* gamecontroller, SDL_GameControllerAxis axis) +{ + if (pfnSDL_GameControllerGetAxis) + return pfnSDL_GameControllerGetAxis(gamecontroller, axis); + return 0; +} +Uint8 safe_pfnSDL_GameControllerGetButton(SDL_GameController* gamecontroller, SDL_GameControllerButton button) +{ + if (pfnSDL_GameControllerGetButton) + return pfnSDL_GameControllerGetButton(gamecontroller, button); + return 0; +} +void safe_pfnSDL_JoystickUpdate() +{ + if (pfnSDL_JoystickUpdate) + pfnSDL_JoystickUpdate(); +} +const char* safe_pfnSDL_GameControllerName(SDL_GameController* gamecontroller) +{ + if (pfnSDL_GameControllerName) + return pfnSDL_GameControllerName(gamecontroller); + return NULL; +} + +struct SDLFunction +{ + void** ppfnFunc; + const char* name; +}; +static SDLFunction sdlFunctions[] = { + {(void**)&pfnSDL_SetRelativeMouseMode, "SDL_SetRelativeMouseMode"}, + {(void**)&pfnSDL_GetRelativeMouseState, "SDL_GetRelativeMouseState"}, + {(void**)&pfnSDL_NumJoysticks, "SDL_NumJoysticks"}, + {(void**)&pfnSDL_IsGameController, "SDL_IsGameController"}, + {(void**)&pfnSDL_GameControllerOpen, "SDL_GameControllerOpen"}, + {(void**)&pfnSDL_GameControllerGetAxis, "SDL_GameControllerGetAxis"}, + {(void**)&pfnSDL_GameControllerGetButton, "SDL_GameControllerGetButton"}, + {(void**)&pfnSDL_JoystickUpdate, "SDL_JoystickUpdate"}, + {(void**)&pfnSDL_GameControllerName, "SDL_GameControllerName"} +}; +#endif + +#ifdef _WIN32 +#include +#else +typedef unsigned int DWORD; +#endif + +#define MOUSE_BUTTON_COUNT 5 + +// use IN_SetVisibleMouse to set: +int iVisibleMouse = 0; + +extern cl_enginefunc_t gEngfuncs; + +extern int iMouseInUse; + +extern kbutton_t in_strafe; +extern kbutton_t in_mlook; +extern kbutton_t in_speed; +extern kbutton_t in_jlook; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; + +extern cvar_t *lookstrafe; +extern cvar_t *lookspring; +extern cvar_t *cl_pitchdown; +extern cvar_t *cl_pitchup; +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_sidespeed; +extern cvar_t *cl_forwardspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_movespeedkey; + +#ifdef _WIN32 +static double s_flRawInputUpdateTime = 0.0f; +static bool m_bRawInput = false; +static bool m_bMouseThread = false; +bool isMouseRelative = false; +#endif + +#ifdef _WIN32 +#include "progdefs.h" +extern globalvars_t *gpGlobals; +#endif + +extern Vector dead_viewangles; + +void V_StopPitchDrift( void ) +{ + +} + +// mouse variables +cvar_t *m_filter; +extern cvar_t *sensitivity; + +// Custom mouse acceleration (0 disable, 1 to enable, 2 enable with separate yaw/pitch rescale) +static cvar_t *m_customaccel; +//Formula: mousesensitivity = ( rawmousedelta^m_customaccel_exponent ) * m_customaccel_scale + sensitivity +// If mode is 2, then x and y sensitivity are scaled by m_pitch and m_yaw respectively. +// Custom mouse acceleration value. +static cvar_t *m_customaccel_scale; +//Max mouse move scale factor, 0 for no limit +static cvar_t *m_customaccel_max; +//Mouse move is raised to this power before being scaled by scale factor +static cvar_t *m_customaccel_exponent; + +#ifdef _WIN32 +// if threaded mouse is enabled then the time to sleep between polls +static cvar_t *m_mousethread_sleep; +#endif + +float mouse_x, mouse_y; + +static int restore_spi; +static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; +static int mouseactive = 0; +static int mouseparmsvalid; +static int mouseshowtoggle = 1; + +// joystick defines and variables +// where should defines be moved? +#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick +#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball +#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V +#define JOY_AXIS_X 0 +#define JOY_AXIS_Y 1 +#define JOY_AXIS_Z 2 +#define JOY_AXIS_R 3 +#define JOY_AXIS_U 4 +#define JOY_AXIS_V 5 + +enum _ControlList +{ + AxisNada = 0, + AxisForward, + AxisLook, + AxisSide, + AxisTurn +}; + +#if !defined(USE_SDL2) && defined(_WIN32) +DWORD dwAxisFlags[JOY_MAX_AXES] = +{ + JOY_RETURNX, + JOY_RETURNY, + JOY_RETURNZ, + JOY_RETURNR, + JOY_RETURNU, + JOY_RETURNV +}; +#endif + +DWORD dwAxisMap[ JOY_MAX_AXES ]; +DWORD dwControlMap[ JOY_MAX_AXES ]; +#if defined(USE_SDL2) +int pdwRawValue[ JOY_MAX_AXES ]; +#elif defined(_WIN32) +PDWORD pdwRawValue[ JOY_MAX_AXES ]; +#endif +DWORD joy_oldbuttonstate, joy_oldpovstate; + +int joy_id; +DWORD joy_numbuttons; + +#ifdef USE_SDL2 +SDL_GameController *s_pJoystick = NULL; +#elif defined(_WIN32) +DWORD joy_flags; +static JOYINFOEX ji; +#endif + +// none of these cvars are saved over a session +// this means that advanced controller configuration needs to be executed +// each time. this avoids any problems with getting back to a default usage +// or when changing from one controller to another. this way at least something +// works. +extern cvar_t *in_joystick; +cvar_t *joy_name; +cvar_t *joy_advanced; +cvar_t *joy_advaxisx; +cvar_t *joy_advaxisy; +cvar_t *joy_advaxisz; +cvar_t *joy_advaxisr; +cvar_t *joy_advaxisu; +cvar_t *joy_advaxisv; +cvar_t *joy_forwardthreshold; +cvar_t *joy_sidethreshold; +cvar_t *joy_pitchthreshold; +cvar_t *joy_yawthreshold; +cvar_t *joy_forwardsensitivity; +cvar_t *joy_sidesensitivity; +cvar_t *joy_pitchsensitivity; +cvar_t *joy_yawsensitivity; +cvar_t *joy_wwhack1; +cvar_t *joy_wwhack2; + +int joy_avail, joy_advancedinit, joy_haspov; + +#ifdef _WIN32 +unsigned int s_hMouseThreadId = 0; +HANDLE s_hMouseThread = 0; +HANDLE s_hMouseQuitEvent = 0; +HANDLE s_hMouseThreadActiveLock = 0; +#endif + +/* +=========== +Force_CenterView_f +=========== +*/ +void Force_CenterView_f (void) +{ + vec3_t viewangles; + + if (!iMouseInUse) + { + gEngfuncs.GetViewAngles( (float *)viewangles ); + viewangles[PITCH] = 0; + gEngfuncs.SetViewAngles( (float *)viewangles ); + } +} + +#ifdef _WIN32 + +LONG mouseThreadActive = 0; +LONG mouseThreadCenterX = 0; +LONG mouseThreadCenterY = 0; +LONG mouseThreadDeltaX = 0; +LONG mouseThreadDeltaY = 0; +LONG mouseThreadSleep = 0; + +bool MouseThread_ActiveLock_Enter( void ) +{ + if(!m_bMouseThread) + return true; + + return WAIT_OBJECT_0 == WaitForSingleObject( s_hMouseThreadActiveLock, INFINITE); +} + +void MouseThread_ActiveLock_Exit( void ) +{ + if(!m_bMouseThread) + return; + + SetEvent( s_hMouseThreadActiveLock ); +} + +unsigned __stdcall MouseThread_Function( void * pArg ) +{ + while ( true ) + { + DWORD sleepVal = (DWORD)InterlockedExchangeAdd(&mouseThreadSleep, 0); + if(0 > sleepVal) sleepVal = 0; + else if(1000 < sleepVal) sleepVal = 1000; + if(WAIT_OBJECT_0 == WaitForSingleObject( s_hMouseQuitEvent, sleepVal)) + { + break; + } + + if( MouseThread_ActiveLock_Enter() ) + { + if ( InterlockedExchangeAdd(&mouseThreadActive, 0) ) + { + POINT mouse_pos; + POINT center_pos; + + center_pos.x = InterlockedExchangeAdd(&mouseThreadCenterX, 0); + center_pos.y = InterlockedExchangeAdd(&mouseThreadCenterY, 0); + GetCursorPos(&mouse_pos); + + mouse_pos.x -= center_pos.x; + mouse_pos.y -= center_pos.y; + + if(mouse_pos.x || mouse_pos.y) SetCursorPos( center_pos.x, center_pos.y ); + + InterlockedExchangeAdd(&mouseThreadDeltaX, mouse_pos.x); + InterlockedExchangeAdd(&mouseThreadDeltaY, mouse_pos.y); + } + + MouseThread_ActiveLock_Exit(); + } + } + + return 0; +} + +/// Updates mouseThreadActive using the global variables mouseactive, iVisibleMouse and m_bRawInput. Should be called after any of these is changed. +/// Has to be interlocked manually by programmer! Use MouseThread_ActiveLock_Enter and MouseThread_ActiveLock_Exit. +void UpdateMouseThreadActive(void) +{ + InterlockedExchange(&mouseThreadActive, mouseactive && !iVisibleMouse && !m_bRawInput); +} + +#endif + +void IN_SetMouseMode(bool enable) +{ + static bool currentMouseMode = false; + + if(enable == currentMouseMode) + return; + + if(enable) + { +#ifdef _WIN32 + if (mouseparmsvalid) + restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); + + m_bRawInput = CVAR_GET_FLOAT( "m_rawinput" ) != 0; + if(m_bRawInput) + { +#ifdef USE_SDL2 + safe_pfnSDL_SetRelativeMouseMode(SDL_TRUE); +#endif + isMouseRelative = true; + } +#else + safe_pfnSDL_SetRelativeMouseMode(SDL_TRUE); +#endif + + currentMouseMode = true; + } + else + { +#ifdef _WIN32 + if(isMouseRelative) + { +#ifdef USE_SDL2 + safe_pfnSDL_SetRelativeMouseMode(SDL_FALSE); +#endif + isMouseRelative = false; + } + + if (restore_spi) + SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); +#else + safe_pfnSDL_SetRelativeMouseMode(SDL_FALSE); +#endif + + currentMouseMode = false; + } +} + +void IN_SetVisibleMouse(bool visible) +{ +#ifdef _WIN32 + bool lockEntered = MouseThread_ActiveLock_Enter(); +#endif + + iVisibleMouse = visible; + + IN_SetMouseMode(!visible); + +#ifdef _WIN32 + UpdateMouseThreadActive(); + if(lockEntered) MouseThread_ActiveLock_Exit(); +#endif +} + +void IN_ResetMouse( void ); + +/* +=========== +IN_ActivateMouse +=========== +*/ +void GoldSourceInput::IN_ActivateMouse (void) +{ + if (mouseinitialized) + { +#ifdef _WIN32 + bool lockEntered = MouseThread_ActiveLock_Enter(); +#endif + + IN_SetMouseMode(true); + + mouseactive = 1; + +#ifdef _WIN32 + UpdateMouseThreadActive(); + if(lockEntered) MouseThread_ActiveLock_Exit(); +#endif + + // now is a good time to reset mouse positon: + IN_ResetMouse(); + } +} + + +/* +=========== +IN_DeactivateMouse +=========== +*/ +void GoldSourceInput::IN_DeactivateMouse (void) +{ + if (mouseinitialized) + { +#ifdef _WIN32 + bool lockEntered = MouseThread_ActiveLock_Enter(); +#endif + + IN_SetMouseMode(false); + + mouseactive = 0; + +#ifdef _WIN32 + UpdateMouseThreadActive(); + if(lockEntered) MouseThread_ActiveLock_Exit(); +#endif + } +} + +/* +=========== +IN_StartupMouse +=========== +*/ +void GoldSourceInput::IN_StartupMouse (void) +{ + if ( gEngfuncs.CheckParm ("-nomouse", NULL ) ) + return; + + mouseinitialized = 1; +#ifdef _WIN32 + mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); + + if (mouseparmsvalid) + { + if ( gEngfuncs.CheckParm ("-noforcemspd", NULL ) ) + newmouseparms[2] = originalmouseparms[2]; + + if ( gEngfuncs.CheckParm ("-noforcemaccel", NULL ) ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + } + + if ( gEngfuncs.CheckParm ("-noforcemparms", NULL ) ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + newmouseparms[2] = originalmouseparms[2]; + } + } +#endif + + mouse_buttons = MOUSE_BUTTON_COUNT; +} + +/* +=========== +IN_Shutdown +=========== +*/ +void GoldSourceInput::IN_Shutdown (void) +{ + IN_DeactivateMouse (); + +#ifdef _WIN32 + if ( s_hMouseQuitEvent ) + { + SetEvent( s_hMouseQuitEvent ); + } + + if ( s_hMouseThread ) + { + if(WAIT_OBJECT_0 != WaitForSingleObject( s_hMouseThread, 5000 )) + { + TerminateThread( s_hMouseThread, 0 ); + } + CloseHandle( s_hMouseThread ); + s_hMouseThread = (HANDLE)0; + } + + if ( s_hMouseQuitEvent ) + { + CloseHandle( s_hMouseQuitEvent ); + s_hMouseQuitEvent = (HANDLE)0; + } + + if( s_hMouseThreadActiveLock ) + { + CloseHandle( s_hMouseThreadActiveLock ); + s_hMouseThreadActiveLock = (HANDLE)0; + } +#endif + +#ifdef USE_SDL2 + for (int j=0; jvalue; + + // Using special accleration values + if ( m_customaccel->value != 0 ) + { + float raw_mouse_movement_distance = sqrt( mx * mx + my * my ); + float acceleration_scale = m_customaccel_scale->value; + float accelerated_sensitivity_max = m_customaccel_max->value; + float accelerated_sensitivity_exponent = m_customaccel_exponent->value; + float accelerated_sensitivity = ( (float)pow( raw_mouse_movement_distance, accelerated_sensitivity_exponent ) * acceleration_scale + mouse_senstivity ); + + if ( accelerated_sensitivity_max > 0.0001f && + accelerated_sensitivity > accelerated_sensitivity_max ) + { + accelerated_sensitivity = accelerated_sensitivity_max; + } + + *x *= accelerated_sensitivity; + *y *= accelerated_sensitivity; + + // Further re-scale by yaw and pitch magnitude if user requests alternate mode 2 + // This means that they will need to up their value for m_customaccel_scale greatly (>40x) since m_pitch/yaw default + // to 0.022 + if ( m_customaccel->value == 2 ) + { + *x *= m_yaw->value; + *y *= m_pitch->value; + } + } + else + { + // Just apply the default + *x *= mouse_senstivity; + *y *= mouse_senstivity; + } +} + +void GoldSourceInput::IN_GetMouseDelta( int *pOutX, int *pOutY) +{ + bool active = mouseactive && !iVisibleMouse; + int mx, my; + + if(active) + { + int deltaX, deltaY; +#ifdef _WIN32 + if ( !m_bRawInput ) + { + if ( m_bMouseThread ) + { + // update mouseThreadSleep: + InterlockedExchange(&mouseThreadSleep, (LONG)m_mousethread_sleep->value); + + bool lockEntered = MouseThread_ActiveLock_Enter(); + + current_pos.x = InterlockedExchange( &mouseThreadDeltaX, 0 ); + current_pos.y = InterlockedExchange( &mouseThreadDeltaY, 0 ); + + if(lockEntered) MouseThread_ActiveLock_Exit(); + } + else + { + GetCursorPos (¤t_pos); + } + } + else +#endif + { +#ifdef USE_SDL2 + safe_pfnSDL_GetRelativeMouseState( &deltaX, &deltaY ); + current_pos.x = deltaX; + current_pos.y = deltaY; +#else + GetCursorPos (¤t_pos); + deltaX = current_pos.x - gEngfuncs.GetWindowCenterX(); + deltaY = current_pos.y - gEngfuncs.GetWindowCenterY(); +#endif + } + +#ifdef _WIN32 + if ( !m_bRawInput ) + { + if ( m_bMouseThread ) + { + mx = current_pos.x; + my = current_pos.y; + } + else + { + mx = current_pos.x - gEngfuncs.GetWindowCenterX() + mx_accum; + my = current_pos.y - gEngfuncs.GetWindowCenterY() + my_accum; + } + } + else +#endif + { + mx = deltaX + mx_accum; + my = deltaY + my_accum; + } + + mx_accum = 0; + my_accum = 0; + + // reset mouse position if required, so there is room to move: +#ifdef _WIN32 + // do not reset if mousethread would do it: + if ( m_bRawInput || !m_bMouseThread ) +#else + if(true) +#endif + IN_ResetMouse(); + +#ifdef _WIN32 + // update m_bRawInput occasionally: + if ( gpGlobals && gpGlobals->time - s_flRawInputUpdateTime > 1.0f ) + { + s_flRawInputUpdateTime = gpGlobals->time; + + bool lockEntered = MouseThread_ActiveLock_Enter(); + + m_bRawInput = CVAR_GET_FLOAT( "m_rawinput" ) != 0; + + if(m_bRawInput && !isMouseRelative) + { +#ifdef USE_SDL2 + safe_pfnSDL_SetRelativeMouseMode(SDL_TRUE); +#endif + isMouseRelative = true; + } + else if(!m_bRawInput && isMouseRelative) + { +#ifdef USE_SDL2 + safe_pfnSDL_SetRelativeMouseMode(SDL_FALSE); +#endif + isMouseRelative = false; + } + + UpdateMouseThreadActive(); + if(lockEntered) MouseThread_ActiveLock_Exit(); + } +#endif + } + else + { + mx = my = 0; + } + + if(pOutX) *pOutX = mx; + if(pOutY) *pOutY = my; +} + +/* +=========== +IN_MouseMove +=========== +*/ +void GoldSourceInput::IN_MouseMove ( float frametime, usercmd_t *cmd) +{ + int mx, my; + vec3_t viewangles; + + gEngfuncs.GetViewAngles( (float *)viewangles ); + + if ( in_mlook.state & 1) + { + V_StopPitchDrift (); + } + + //jjb - this disbles normal mouse control if the user is trying to + // move the camera, or if the mouse cursor is visible or if we're in intermission + if ( !iMouseInUse && !gHUD.m_iIntermission && !iVisibleMouse ) + { + IN_GetMouseDelta( &mx, &my ); + + if (m_filter && m_filter->value) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + + old_mouse_x = mx; + old_mouse_y = my; + + // Apply custom mouse scaling/acceleration + IN_ScaleMouse( &mouse_x, &mouse_y ); + + // add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe->value && (in_mlook.state & 1) )) + cmd->sidemove += m_side->value * mouse_x; + else + viewangles[YAW] -= m_yaw->value * mouse_x; + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + viewangles[PITCH] += m_pitch->value * mouse_y; + if (viewangles[PITCH] > cl_pitchdown->value) + viewangles[PITCH] = cl_pitchdown->value; + if (viewangles[PITCH] < -cl_pitchup->value) + viewangles[PITCH] = -cl_pitchup->value; + } + else + { + if ((in_strafe.state & 1) && gEngfuncs.IsNoClipping() ) + { + cmd->upmove -= m_forward->value * mouse_y; + } + else + { + cmd->forwardmove -= m_forward->value * mouse_y; + } + } + } + + gEngfuncs.SetViewAngles( (float *)viewangles ); + +/* +//#define TRACE_TEST +#if defined( TRACE_TEST ) + { + int mx, my; + void V_Move( int mx, int my ); + IN_GetMousePos( &mx, &my ); + V_Move( mx, my ); + } +#endif +*/ +} + +/* +=========== +IN_Accumulate +=========== +*/ +void GoldSourceInput::IN_Accumulate (void) +{ + //only accumulate mouse if we are not moving the camera with the mouse + if ( !iMouseInUse && !iVisibleMouse) + { + if (mouseactive) + { +#ifdef _WIN32 + if ( !m_bRawInput ) + { + if ( !m_bMouseThread ) + { + GetCursorPos (¤t_pos); + + mx_accum += current_pos.x - gEngfuncs.GetWindowCenterX(); + my_accum += current_pos.y - gEngfuncs.GetWindowCenterY(); + } + } + else +#endif + { +#ifdef USE_SDL2 + int deltaX, deltaY; + safe_pfnSDL_GetRelativeMouseState( &deltaX, &deltaY ); + mx_accum += deltaX; + my_accum += deltaY; +#else + GetCursorPos (¤t_pos); + + mx_accum += current_pos.x - gEngfuncs.GetWindowCenterX(); + my_accum += current_pos.y - gEngfuncs.GetWindowCenterY(); +#endif + } + + // force the mouse to the center, so there's room to move +#ifdef _WIN32 + // do not reset if mousethread would do it: + if ( m_bRawInput || !m_bMouseThread ) +#else + if(true) +#endif + IN_ResetMouse(); + + } + } + +} + +/* +=================== +IN_ClearStates +=================== +*/ +void GoldSourceInput::IN_ClearStates (void) +{ + if ( !mouseactive ) + return; + + mx_accum = 0; + my_accum = 0; + mouse_oldbuttonstate = 0; +} + +/* +=============== +IN_StartupJoystick +=============== +*/ +void IN_StartupJoystick (void) +{ + // abort startup if user requests no joystick + if ( gEngfuncs.CheckParm ("-nojoy", NULL ) ) + return; + + // assume no joystick + joy_avail = 0; +#ifdef USE_SDL2 + int nJoysticks = safe_pfnSDL_NumJoysticks(); + if ( nJoysticks > 0 ) + { + for ( int i = 0; i < nJoysticks; i++ ) + { + if ( safe_pfnSDL_IsGameController( i ) ) + { + s_pJoystick = safe_pfnSDL_GameControllerOpen( i ); + if ( s_pJoystick ) + { + //save the joystick's number of buttons and POV status + joy_numbuttons = SDL_CONTROLLER_BUTTON_MAX; + joy_haspov = 0; + + // old button and POV states default to no buttons pressed + joy_oldbuttonstate = joy_oldpovstate = 0; + + // mark the joystick as available and advanced initialization not completed + // this is needed as cvars are not available during initialization + gEngfuncs.Con_Printf ("joystick found\n\n", safe_pfnSDL_GameControllerName(s_pJoystick)); + joy_avail = 1; + joy_advancedinit = 0; + break; + } + } + } + } + else + { + gEngfuncs.Con_DPrintf ("joystick not found -- driver not present\n\n"); + } +#elif defined(_WIN32) + int numdevs; + JOYCAPS jc; + MMRESULT mmr; + // verify joystick driver is present + if ((numdevs = joyGetNumDevs ()) == 0) + { + gEngfuncs.Con_DPrintf ("joystick not found -- driver not present\n\n"); + return; + } + + // cycle through the joystick ids for the first valid one + for (joy_id=0 ; joy_idvalue == 0.0) + { + // default joystick initialization + // 2 axes only with joystick control + dwAxisMap[JOY_AXIS_X] = AxisTurn; + // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; + dwAxisMap[JOY_AXIS_Y] = AxisForward; + // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; + } + else + { + if ( strcmp ( joy_name->string, "joystick") != 0 ) + { + // notify user of advanced controller + gEngfuncs.Con_Printf ("\n%s configured\n\n", joy_name->string); + } + + // advanced initialization here + // data supplied by user via joy_axisn cvars + dwTemp = (DWORD) joy_advaxisx->value; + dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = (DWORD) joy_advaxisy->value; + dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = (DWORD) joy_advaxisz->value; + dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = (DWORD) joy_advaxisr->value; + dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = (DWORD) joy_advaxisu->value; + dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = (DWORD) joy_advaxisv->value; + dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; + } + +#if !defined(USE_SDL2) && defined(_WIN32) + // compute the axes to collect from DirectInput + joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; + for (i = 0; i < JOY_MAX_AXES; i++) + { + if (dwAxisMap[i] != AxisNada) + { + joy_flags |= dwAxisFlags[i]; + } + } +#endif +} + + +/* +=========== +IN_Commands +=========== +*/ +void GoldSourceInput::IN_Commands (void) +{ + int i, key_index; + + if (!joy_avail) + { + return; + } + + DWORD buttonstate, povstate; + + // loop through the joystick buttons + // key a joystick event or auxillary event for higher number buttons for each state change +#ifdef USE_SDL2 + buttonstate = 0; + for ( i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++ ) + { + if ( safe_pfnSDL_GameControllerGetButton( s_pJoystick, (SDL_GameControllerButton)i ) ) + { + buttonstate |= 1<value != 0.0) + { + ji.dwUpos += 100; + } + return 1; + } + else + { + // read error occurred + // turning off the joystick seems too harsh for 1 read error,\ + // but what should be done? + // Con_Printf ("IN_ReadJoystick: no response\n"); + // joy_avail = 0; + return 0; + } +#else + return 0; +#endif +} + + +/* +=========== +IN_JoyMove +=========== +*/ +void IN_JoyMove ( float frametime, usercmd_t *cmd ) +{ + float speed, aspeed; + float fAxisValue, fTemp; + int i; + vec3_t viewangles; + + gEngfuncs.GetViewAngles( (float *)viewangles ); + + + // complete initialization if first time in + // this is needed as cvars are not available at initialization time + if( joy_advancedinit != 1 ) + { + Joy_AdvancedUpdate_f(); + joy_advancedinit = 1; + } + + // verify joystick is available and that the user wants to use it + if (!joy_avail || !in_joystick->value) + { + return; + } + + // collect the joystick data, if possible + if (IN_ReadJoystick () != 1) + { + return; + } + + if (in_speed.state & 1) + speed = cl_movespeedkey->value; + else + speed = 1; + + aspeed = speed * frametime; + + // loop through the axes + for (i = 0; i < JOY_MAX_AXES; i++) + { + // get the floating point zero-centered, potentially-inverted data for the current axis +#ifdef USE_SDL2 + fAxisValue = (float)pdwRawValue[i]; +#elif defined(_WIN32) + fAxisValue = (float) *pdwRawValue[i]; + fAxisValue -= 32768.0; +#endif + + if (joy_wwhack2->value != 0.0) + { + if (dwAxisMap[i] == AxisTurn) + { + // this is a special formula for the Logitech WingMan Warrior + // y=ax^b; where a = 300 and b = 1.3 + // also x values are in increments of 800 (so this is factored out) + // then bounds check result to level out excessively high spin rates + fTemp = 300.0 * pow(fabs(fAxisValue) / 800.0, 1.3); + if (fTemp > 14000.0) + fTemp = 14000.0; + // restore direction information + fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; + } + } + + // convert range from -32768..32767 to -1..1 + fAxisValue /= 32768.0; + + switch (dwAxisMap[i]) + { + case AxisForward: + if ((joy_advanced->value == 0.0) && (in_jlook.state & 1)) + { + // user wants forward control to become look control + if (fabs(fAxisValue) > joy_pitchthreshold->value) + { + // if mouse invert is on, invert the joystick pitch value + // only absolute control support here (joy_advanced is 0) + if (m_pitch->value < 0.0) + { + viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + else + { + viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if(lookspring->value == 0.0) + { + V_StopPitchDrift(); + } + } + } + else + { + // user wants forward control to be forward control + if (fabs(fAxisValue) > joy_forwardthreshold->value) + { + cmd->forwardmove += (fAxisValue * joy_forwardsensitivity->value) * speed * cl_forwardspeed->value; + } + } + break; + + case AxisSide: + if (fabs(fAxisValue) > joy_sidethreshold->value) + { + cmd->sidemove += (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; + } + break; + + case AxisTurn: + if ((in_strafe.state & 1) || (lookstrafe->value && (in_jlook.state & 1))) + { + // user wants turn control to become side control + if (fabs(fAxisValue) > joy_sidethreshold->value) + { + cmd->sidemove -= (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; + } + } + else + { + // user wants turn control to be turn control + if (fabs(fAxisValue) > joy_yawthreshold->value) + { + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * aspeed * cl_yawspeed->value; + } + else + { + viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * speed * 180.0; + } + + } + } + break; + + case AxisLook: + if (in_jlook.state & 1) + { + if (fabs(fAxisValue) > joy_pitchthreshold->value) + { + // pitch movement detected and pitch movement desired by user + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + else + { + viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * speed * 180.0; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if( lookspring->value == 0.0 ) + { + V_StopPitchDrift(); + } + } + } + break; + + default: + break; + } + } + + // bounds check pitch + if (viewangles[PITCH] > cl_pitchdown->value) + viewangles[PITCH] = cl_pitchdown->value; + if (viewangles[PITCH] < -cl_pitchup->value) + viewangles[PITCH] = -cl_pitchup->value; + + gEngfuncs.SetViewAngles( (float *)viewangles ); +} + +/* +=========== +IN_Move +=========== +*/ +void GoldSourceInput::IN_Move ( float frametime, usercmd_t *cmd) +{ + if ( !iMouseInUse && mouseactive ) + { + IN_MouseMove ( frametime, cmd); + } + + IN_JoyMove ( frametime, cmd); +} + +/* +=========== +IN_Init +=========== +*/ +void GoldSourceInput::IN_Init (void) +{ + m_filter = gEngfuncs.pfnRegisterVariable ( "m_filter","0", FCVAR_ARCHIVE ); + sensitivity = gEngfuncs.pfnRegisterVariable ( "sensitivity","3", FCVAR_ARCHIVE ); // user mouse sensitivity setting. + + in_joystick = gEngfuncs.pfnRegisterVariable ( "joystick","0", FCVAR_ARCHIVE ); + joy_name = gEngfuncs.pfnRegisterVariable ( "joyname", "joystick", 0 ); + joy_advanced = gEngfuncs.pfnRegisterVariable ( "joyadvanced", "0", 0 ); + joy_advaxisx = gEngfuncs.pfnRegisterVariable ( "joyadvaxisx", "0", 0 ); + joy_advaxisy = gEngfuncs.pfnRegisterVariable ( "joyadvaxisy", "0", 0 ); + joy_advaxisz = gEngfuncs.pfnRegisterVariable ( "joyadvaxisz", "0", 0 ); + joy_advaxisr = gEngfuncs.pfnRegisterVariable ( "joyadvaxisr", "0", 0 ); + joy_advaxisu = gEngfuncs.pfnRegisterVariable ( "joyadvaxisu", "0", 0 ); + joy_advaxisv = gEngfuncs.pfnRegisterVariable ( "joyadvaxisv", "0", 0 ); + joy_forwardthreshold = gEngfuncs.pfnRegisterVariable ( "joyforwardthreshold", "0.15", 0 ); + joy_sidethreshold = gEngfuncs.pfnRegisterVariable ( "joysidethreshold", "0.15", 0 ); + joy_pitchthreshold = gEngfuncs.pfnRegisterVariable ( "joypitchthreshold", "0.15", 0 ); + joy_yawthreshold = gEngfuncs.pfnRegisterVariable ( "joyyawthreshold", "0.15", 0 ); + joy_forwardsensitivity = gEngfuncs.pfnRegisterVariable ( "joyforwardsensitivity", "-1.0", 0 ); + joy_sidesensitivity = gEngfuncs.pfnRegisterVariable ( "joysidesensitivity", "-1.0", 0 ); + joy_pitchsensitivity = gEngfuncs.pfnRegisterVariable ( "joypitchsensitivity", "1.0", 0 ); + joy_yawsensitivity = gEngfuncs.pfnRegisterVariable ( "joyyawsensitivity", "-1.0", 0 ); + joy_wwhack1 = gEngfuncs.pfnRegisterVariable ( "joywwhack1", "0.0", 0 ); + joy_wwhack2 = gEngfuncs.pfnRegisterVariable ( "joywwhack2", "0.0", 0 ); + + m_customaccel = gEngfuncs.pfnRegisterVariable ( "m_customaccel", "0", FCVAR_ARCHIVE ); + m_customaccel_scale = gEngfuncs.pfnRegisterVariable ( "m_customaccel_scale", "0.04", FCVAR_ARCHIVE ); + m_customaccel_max = gEngfuncs.pfnRegisterVariable ( "m_customaccel_max", "0", FCVAR_ARCHIVE ); + m_customaccel_exponent = gEngfuncs.pfnRegisterVariable ( "m_customaccel_exponent", "1", FCVAR_ARCHIVE ); + +#ifdef _WIN32 + m_bRawInput = CVAR_GET_FLOAT( "m_rawinput" ) != 0; + m_bMouseThread = gEngfuncs.CheckParm ("-mousethread", NULL ) != NULL; + m_mousethread_sleep = gEngfuncs.pfnRegisterVariable ( "m_mousethread_sleep", "1", FCVAR_ARCHIVE ); // default to less than 1000 Hz + + m_bMouseThread = m_bMouseThread && NULL != m_mousethread_sleep; + + if (m_bMouseThread) + { + // init mouseThreadSleep: +#if 0 // _beginthreadex is not defined on VS 6? + InterlockedExchange(&mouseThreadSleep, (LONG)m_mousethread_sleep->value); + + s_hMouseQuitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + s_hMouseThreadActiveLock = CreateEvent( NULL, FALSE, TRUE, NULL ); + if ( s_hMouseQuitEvent && s_hMouseThreadActiveLock) + { + s_hMouseThread = (HANDLE)_beginthreadex( NULL, 0, MouseThread_Function, NULL, 0, &s_hMouseThreadId ); + } + + m_bMouseThread = NULL != s_hMouseThread; +#else + m_bMouseThread = 0; +#endif + + // at this early stage this won't print anything: + // gEngfuncs.Con_DPrintf ("Mouse thread %s.\n", m_bMouseThread ? "initalized" : "failed to initalize"); + } +#endif + +#ifdef USE_SDL2 +#ifdef __APPLE__ +#define SDL2_FULL_LIBNAME "libsdl2-2.0.0.dylib" +#else +#define SDL2_FULL_LIBNAME "libSDL2-2.0.so.0" +#endif + sdl2Lib = dlopen(SDL2_FULL_LIBNAME, RTLD_NOW|RTLD_LOCAL); + if (sdl2Lib) { + for (int j=0; jIN_ClientMoveEvent(forwardmove, sidemove); +} + +extern "C" void DLLEXPORT IN_ClientLookEvent( float relyaw, float relpitch ) +{ + currentInput->IN_ClientLookEvent(relyaw, relpitch); +} + +void IN_Move( float frametime, usercmd_t *cmd ) +{ + currentInput->IN_Move(frametime, cmd); +} + +extern "C" void DLLEXPORT IN_MouseEvent( int mstate ) +{ + currentInput->IN_MouseEvent(mstate); +} + +extern "C" void DLLEXPORT IN_ClearStates( void ) +{ + currentInput->IN_ClearStates(); +} + +extern "C" void DLLEXPORT IN_ActivateMouse( void ) +{ + currentInput->IN_ActivateMouse(); +} + +extern "C" void DLLEXPORT IN_DeactivateMouse( void ) +{ + currentInput->IN_DeactivateMouse(); +} + +extern "C" void DLLEXPORT IN_Accumulate( void ) +{ + currentInput->IN_Accumulate(); +} + +void IN_Commands( void ) +{ + currentInput->IN_Commands(); +} + +void IN_Shutdown( void ) +{ + currentInput->IN_Shutdown(); +} + +void IN_Init( void ) +{ +#ifdef SUPPORT_GOLDSOURCE_INPUT + if (IsXashFWGS()) { + gEngfuncs.Con_Printf( "FWGS Xash3D input is in use\n" ); + currentInput = &fwgsInput; + } else { + gEngfuncs.Con_Printf( "GoldSource input is in use\n" ); + currentInput = &goldSourceInput; + } +#else + currentInput = &fwgsInput; +#endif + currentInput->IN_Init(); +} diff --git a/cl_dll/input_mouse.h b/cl_dll/input_mouse.h new file mode 100644 index 0000000..c13b7fa --- /dev/null +++ b/cl_dll/input_mouse.h @@ -0,0 +1,79 @@ +#pragma once +#ifndef INPUT_MOUSE_H +#define INPUT_MOUSE_H +#include "cl_dll.h" +#include "usercmd.h" +#include "in_defs.h" + +class AbstractInput +{ +public: + virtual void IN_ClientMoveEvent( float forwardmove, float sidemove ) = 0; + virtual void IN_ClientLookEvent( float relyaw, float relpitch ) = 0; + virtual void IN_Move( float frametime, usercmd_t *cmd ) = 0; + virtual void IN_MouseEvent( int mstate ) = 0; + virtual void IN_ClearStates( void ) = 0; + virtual void IN_ActivateMouse( void ) = 0; + virtual void IN_DeactivateMouse( void ) = 0; + virtual void IN_Accumulate( void ) = 0; + virtual void IN_Commands( void ) = 0; + virtual void IN_Shutdown( void ) = 0; + virtual void IN_Init( void ) = 0; +}; + +class FWGSInput : public AbstractInput +{ +public: + virtual void IN_ClientMoveEvent( float forwardmove, float sidemove ); + virtual void IN_ClientLookEvent( float relyaw, float relpitch ); + virtual void IN_Move( float frametime, usercmd_t *cmd ); + virtual void IN_MouseEvent( int mstate ); + virtual void IN_ClearStates( void ); + virtual void IN_ActivateMouse( void ); + virtual void IN_DeactivateMouse( void ); + virtual void IN_Accumulate( void ); + virtual void IN_Commands( void ); + virtual void IN_Shutdown( void ); + virtual void IN_Init( void ); + +protected: + float ac_forwardmove; + float ac_sidemove; + int ac_movecount; + float rel_yaw; + float rel_pitch; +}; + +// No need for goldsource input support on the platforms that are not supported by GoldSource. +#if defined(GOLDSOURCE_SUPPORT) && (defined(_WIN32) || defined(__linux__) || defined(__APPLE__)) && (defined(__i386) || defined(_M_IX86)) +#define SUPPORT_GOLDSOURCE_INPUT +class GoldSourceInput : public AbstractInput +{ +public: + virtual void IN_ClientMoveEvent( float forwardmove, float sidemove ) {} + virtual void IN_ClientLookEvent( float relyaw, float relpitch ) {} + virtual void IN_Move( float frametime, usercmd_t *cmd ); + virtual void IN_MouseEvent( int mstate ); + virtual void IN_ClearStates( void ); + virtual void IN_ActivateMouse( void ); + virtual void IN_DeactivateMouse( void ); + virtual void IN_Accumulate( void ); + virtual void IN_Commands( void ); + virtual void IN_Shutdown( void ); + virtual void IN_Init( void ); + +protected: + void IN_GetMouseDelta( int *pOutX, int *pOutY); + void IN_MouseMove ( float frametime, usercmd_t *cmd); + void IN_StartupMouse (void); + + int mouse_buttons; + int mouse_oldbuttonstate; + POINT current_pos; + int old_mouse_x, old_mouse_y, mx_accum, my_accum; + int mouseinitialized; + void* sdl2Lib; +}; +#endif + +#endif diff --git a/cl_dll/input_xash3d.cpp b/cl_dll/input_xash3d.cpp new file mode 100644 index 0000000..ecb642c --- /dev/null +++ b/cl_dll/input_xash3d.cpp @@ -0,0 +1,283 @@ +#include "hud.h" +#include "usercmd.h" +#include "cvardef.h" +#include "kbutton.h" +#include "keydefs.h" +#include "input_mouse.h" +#include "cl_entity.h" +extern cvar_t *sensitivity; +extern cvar_t *in_joystick; + +extern kbutton_t in_strafe; +extern kbutton_t in_mlook; +extern kbutton_t in_speed; +extern kbutton_t in_jlook; +extern kbutton_t in_forward; +extern kbutton_t in_back; +extern kbutton_t in_moveleft; +extern kbutton_t in_moveright; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *lookstrafe; +extern cvar_t *lookspring; +extern cvar_t *cl_pitchdown; +extern cvar_t *cl_pitchup; +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_sidespeed; +extern cvar_t *cl_forwardspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_movespeedkey; +cvar_t *cl_laddermode; + + +#define F 1U<<0 // Forward +#define B 1U<<1 // Back +#define L 1U<<2 // Left +#define R 1U<<3 // Right +#define T 1U<<4 // Forward stop +#define S 1U<<5 // Side stop + +#define BUTTON_DOWN 1 +#define IMPULSE_DOWN 2 +#define IMPULSE_UP 4 + +int CL_IsDead( void ); +extern Vector dead_viewangles; + +void IN_ToggleButtons( float forwardmove, float sidemove ) +{ + static unsigned int moveflags = T | S; + + if( forwardmove ) + moveflags &= ~T; + else + { + //if( in_forward.state || in_back.state ) gEngfuncs.Con_Printf("Buttons pressed f%d b%d\n", in_forward.state, in_back.state); + if( !( moveflags & T ) ) + { + //IN_ForwardUp(); + //IN_BackUp(); + //gEngfuncs.Con_Printf("Reset forwardmove state f%d b%d\n", in_forward.state, in_back.state); + in_forward.state &= ~BUTTON_DOWN; + in_back.state &= ~BUTTON_DOWN; + moveflags |= T; + } + } + if( sidemove ) + moveflags &= ~S; + else + { + //gEngfuncs.Con_Printf("l%d r%d\n", in_moveleft.state, in_moveright.state); + //if( in_moveleft.state || in_moveright.state ) gEngfuncs.Con_Printf("Buttons pressed l%d r%d\n", in_moveleft.state, in_moveright.state); + if( !( moveflags & S ) ) + { + //IN_MoverightUp(); + //IN_MoveleftUp(); + //gEngfuncs.Con_Printf("Reset sidemove state f%d b%d\n", in_moveleft.state, in_moveright.state); + in_moveleft.state &= ~BUTTON_DOWN; + in_moveright.state &= ~BUTTON_DOWN; + moveflags |= S; + } + } + + if( forwardmove > 0.7 && !( moveflags & F ) ) + { + moveflags |= F; + in_forward.state |= BUTTON_DOWN; + } + if( forwardmove < 0.7 && ( moveflags & F ) ) + { + moveflags &= ~F; + in_forward.state &= ~BUTTON_DOWN; + } + if( forwardmove < -0.7 && !( moveflags & B ) ) + { + moveflags |= B; + in_back.state |= BUTTON_DOWN; + } + if( forwardmove > -0.7 && ( moveflags & B ) ) + { + moveflags &= ~B; + in_back.state &= ~BUTTON_DOWN; + } + if( sidemove > 0.9 && !( moveflags & R ) ) + { + moveflags |= R; + in_moveright.state |= BUTTON_DOWN; + } + if( sidemove < 0.9 && ( moveflags & R ) ) + { + moveflags &= ~R; + in_moveright.state &= ~BUTTON_DOWN; + } + if( sidemove < -0.9 && !( moveflags & L ) ) + { + moveflags |= L; + in_moveleft.state |= BUTTON_DOWN; + } + if( sidemove > -0.9 && ( moveflags & L ) ) + { + moveflags &= ~L; + in_moveleft.state &= ~BUTTON_DOWN; + } +} + +void FWGSInput::IN_ClientMoveEvent( float forwardmove, float sidemove ) +{ + //gEngfuncs.Con_Printf("IN_MoveEvent\n"); + + ac_forwardmove += forwardmove; + ac_sidemove += sidemove; + ac_movecount++; +} + +void FWGSInput::IN_ClientLookEvent( float relyaw, float relpitch ) +{ + rel_yaw += relyaw; + rel_pitch += relpitch; +} + +// Rotate camera and add move values to usercmd +void FWGSInput::IN_Move( float frametime, usercmd_t *cmd ) +{ + Vector viewangles; + bool fLadder = false; + + if( gHUD.m_iIntermission ) + return; // we can't move during intermission + + if( cl_laddermode->value != 2 ) + { + cl_entity_t *pplayer = gEngfuncs.GetLocalPlayer(); + if( pplayer ) + fLadder = pplayer->curstate.movetype == MOVETYPE_FLY; + } + //if(ac_forwardmove || ac_sidemove) + //gEngfuncs.Con_Printf("Move: %f %f %f %f\n", ac_forwardmove, ac_sidemove, rel_pitch, rel_yaw); + if( in_mlook.state & 1 ) + { + void V_StopPitchDrift( void ); + V_StopPitchDrift(); + } + + if( CL_IsDead() ) + { + viewangles = dead_viewangles; // HACKHACK: see below + } + else + { + gEngfuncs.GetViewAngles( viewangles ); + } + if( gHUD.GetSensitivity() != 0 ) + { + rel_yaw *= gHUD.GetSensitivity(); + rel_pitch *= gHUD.GetSensitivity(); + } + else + { + rel_yaw *= sensitivity->value; + rel_pitch *= sensitivity->value; + } + viewangles[YAW] += rel_yaw; + if( fLadder ) + { + if( cl_laddermode->value == 1 ) + viewangles[YAW] -= ac_sidemove * 5; + ac_sidemove = 0; + } + viewangles[PITCH] += rel_pitch; + + if( viewangles[PITCH] > cl_pitchdown->value ) + viewangles[PITCH] = cl_pitchdown->value; + if( viewangles[PITCH] < -cl_pitchup->value ) + viewangles[PITCH] = -cl_pitchup->value; + + // HACKHACK: change viewangles directly in viewcode, + // so viewangles when player is dead will not be changed on server + if( !CL_IsDead() ) + { + gEngfuncs.SetViewAngles( viewangles ); + } + + dead_viewangles = viewangles; // keep them actual + if( ac_movecount ) + { + IN_ToggleButtons( ac_forwardmove / ac_movecount, ac_sidemove / ac_movecount ); + + if( ac_forwardmove ) + cmd->forwardmove = ac_forwardmove * cl_forwardspeed->value / ac_movecount; + if( ac_sidemove ) + cmd->sidemove = ac_sidemove * cl_sidespeed->value / ac_movecount; + if( ( in_speed.state & 1 ) && ( ac_sidemove || ac_forwardmove ) ) + { + cmd->forwardmove *= cl_movespeedkey->value; + cmd->sidemove *= cl_movespeedkey->value; + } + } + + ac_sidemove = ac_forwardmove = rel_pitch = rel_yaw = 0; + ac_movecount = 0; +} + +void FWGSInput::IN_MouseEvent( int mstate ) +{ + static int mouse_oldbuttonstate; + // perform button actions + for( int i = 0; i < 5; i++ ) + { + if( ( mstate & ( 1 << i ) ) && !( mouse_oldbuttonstate & ( 1 << i ) ) ) + { + gEngfuncs.Key_Event( K_MOUSE1 + i, 1 ); + } + + if( !( mstate & ( 1 << i ) ) && ( mouse_oldbuttonstate & ( 1 << i ) ) ) + { + gEngfuncs.Key_Event( K_MOUSE1 + i, 0 ); + } + } + + mouse_oldbuttonstate = mstate; +} + +// Stubs + +void FWGSInput::IN_ClearStates( void ) +{ + //gEngfuncs.Con_Printf( "IN_ClearStates\n" ); +} + +void FWGSInput::IN_ActivateMouse( void ) +{ + //gEngfuncs.Con_Printf( "IN_ActivateMouse\n" ); +} + +void FWGSInput::IN_DeactivateMouse( void ) +{ + //gEngfuncs.Con_Printf( "IN_DeactivateMouse\n" ); +} + +void FWGSInput::IN_Accumulate( void ) +{ + //gEngfuncs.Con_Printf( "IN_Accumulate\n" ); +} + +void FWGSInput::IN_Commands( void ) +{ + //gEngfuncs.Con_Printf( "IN_Commands\n" ); +} + +void FWGSInput::IN_Shutdown( void ) +{ +} + +// Register cvars and reset data +void FWGSInput::IN_Init( void ) +{ + sensitivity = gEngfuncs.pfnRegisterVariable( "sensitivity", "3", FCVAR_ARCHIVE ); + in_joystick = gEngfuncs.pfnRegisterVariable( "joystick", "0", FCVAR_ARCHIVE ); + cl_laddermode = gEngfuncs.pfnRegisterVariable( "cl_laddermode", "2", FCVAR_ARCHIVE ); + ac_forwardmove = ac_sidemove = rel_yaw = rel_pitch = 0; +} diff --git a/cl_dll/inputw32.cpp b/cl_dll/inputw32.cpp deleted file mode 100644 index c278a8f..0000000 --- a/cl_dll/inputw32.cpp +++ /dev/null @@ -1,935 +0,0 @@ -//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -// in_win.c -- windows 95 mouse and joystick code -// 02/21/97 JCB Added extended DirectInput code to support external controllers. - -#include "hud.h" -#include "cl_util.h" -#include "camera.h" -#include "kbutton.h" -#include "cvardef.h" -#include "usercmd.h" -#include "const.h" -#include "camera.h" -#include "in_defs.h" -#include "../engine/keydefs.h" -#include "view.h" -#include "windows.h" - -#define MOUSE_BUTTON_COUNT 5 - -// Set this to 1 to show mouse cursor. Experimental -int g_iVisibleMouse = 0; - -extern "C" -{ - void DLLEXPORT IN_ActivateMouse( void ); - void DLLEXPORT IN_DeactivateMouse( void ); - void DLLEXPORT IN_MouseEvent (int mstate); - void DLLEXPORT IN_Accumulate (void); - void DLLEXPORT IN_ClearStates (void); -} - -extern cl_enginefunc_t gEngfuncs; - -extern int iMouseInUse; - -extern kbutton_t in_strafe; -extern kbutton_t in_mlook; -extern kbutton_t in_speed; -extern kbutton_t in_jlook; - -extern cvar_t *m_pitch; -extern cvar_t *m_yaw; -extern cvar_t *m_forward; -extern cvar_t *m_side; - -extern cvar_t *lookstrafe; -extern cvar_t *lookspring; -extern cvar_t *cl_pitchdown; -extern cvar_t *cl_pitchup; -extern cvar_t *cl_yawspeed; -extern cvar_t *cl_sidespeed; -extern cvar_t *cl_forwardspeed; -extern cvar_t *cl_pitchspeed; -extern cvar_t *cl_movespeedkey; - -// mouse variables -cvar_t *m_filter; -cvar_t *sensitivity; - -int mouse_buttons; -int mouse_oldbuttonstate; -POINT current_pos; -int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; - -static int restore_spi; -static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; -static int mouseactive; -int mouseinitialized; -static int mouseparmsvalid; -static int mouseshowtoggle = 1; - -// joystick defines and variables -// where should defines be moved? -#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick -#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball -#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V -#define JOY_AXIS_X 0 -#define JOY_AXIS_Y 1 -#define JOY_AXIS_Z 2 -#define JOY_AXIS_R 3 -#define JOY_AXIS_U 4 -#define JOY_AXIS_V 5 - -enum _ControlList -{ - AxisNada = 0, - AxisForward, - AxisLook, - AxisSide, - AxisTurn -}; - -DWORD dwAxisFlags[JOY_MAX_AXES] = -{ - JOY_RETURNX, - JOY_RETURNY, - JOY_RETURNZ, - JOY_RETURNR, - JOY_RETURNU, - JOY_RETURNV -}; - -DWORD dwAxisMap[ JOY_MAX_AXES ]; -DWORD dwControlMap[ JOY_MAX_AXES ]; -PDWORD pdwRawValue[ JOY_MAX_AXES ]; - -// none of these cvars are saved over a session -// this means that advanced controller configuration needs to be executed -// each time. this avoids any problems with getting back to a default usage -// or when changing from one controller to another. this way at least something -// works. -cvar_t *in_joystick; -cvar_t *joy_name; -cvar_t *joy_advanced; -cvar_t *joy_advaxisx; -cvar_t *joy_advaxisy; -cvar_t *joy_advaxisz; -cvar_t *joy_advaxisr; -cvar_t *joy_advaxisu; -cvar_t *joy_advaxisv; -cvar_t *joy_forwardthreshold; -cvar_t *joy_sidethreshold; -cvar_t *joy_pitchthreshold; -cvar_t *joy_yawthreshold; -cvar_t *joy_forwardsensitivity; -cvar_t *joy_sidesensitivity; -cvar_t *joy_pitchsensitivity; -cvar_t *joy_yawsensitivity; -cvar_t *joy_wwhack1; -cvar_t *joy_wwhack2; - -int joy_avail, joy_advancedinit, joy_haspov; -DWORD joy_oldbuttonstate, joy_oldpovstate; - -int joy_id; -DWORD joy_flags; -DWORD joy_numbuttons; - -static JOYINFOEX ji; - -/* -=========== -Force_CenterView_f -=========== -*/ -void Force_CenterView_f (void) -{ - vec3_t viewangles; - - if (!iMouseInUse) - { - gEngfuncs.GetViewAngles( (float *)viewangles ); - viewangles[PITCH] = 0; - gEngfuncs.SetViewAngles( (float *)viewangles ); - } -} - -/* -=========== -IN_ActivateMouse -=========== -*/ -void DLLEXPORT IN_ActivateMouse (void) -{ - if (mouseinitialized) - { - if (mouseparmsvalid) - restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); - mouseactive = 1; - } -} - -/* -=========== -IN_DeactivateMouse -=========== -*/ -void DLLEXPORT IN_DeactivateMouse (void) -{ - if (mouseinitialized) - { - if (restore_spi) - SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); - - mouseactive = 0; - } -} - -/* -=========== -IN_StartupMouse -=========== -*/ -void IN_StartupMouse (void) -{ - if ( gEngfuncs.CheckParm ("-nomouse", NULL ) ) - return; - - mouseinitialized = 1; - mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); - - if (mouseparmsvalid) - { - if ( gEngfuncs.CheckParm ("-noforcemspd", NULL ) ) - newmouseparms[2] = originalmouseparms[2]; - - if ( gEngfuncs.CheckParm ("-noforcemaccel", NULL ) ) - { - newmouseparms[0] = originalmouseparms[0]; - newmouseparms[1] = originalmouseparms[1]; - } - - if ( gEngfuncs.CheckParm ("-noforcemparms", NULL ) ) - { - newmouseparms[0] = originalmouseparms[0]; - newmouseparms[1] = originalmouseparms[1]; - newmouseparms[2] = originalmouseparms[2]; - } - } - - mouse_buttons = MOUSE_BUTTON_COUNT; -} - -/* -=========== -IN_Shutdown -=========== -*/ -void IN_Shutdown (void) -{ - IN_DeactivateMouse (); -} - -/* -=========== -IN_GetMousePos - -Ask for mouse position from engine -=========== -*/ -void IN_GetMousePos( int *mx, int *my ) -{ - gEngfuncs.GetMousePosition( mx, my ); -} - -/* -=========== -IN_ResetMouse - -FIXME: Call through to engine? -=========== -*/ -void IN_ResetMouse( void ) -{ - SetCursorPos ( gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY() ); -} - -/* -=========== -IN_MouseEvent -=========== -*/ -void DLLEXPORT IN_MouseEvent (int mstate) -{ - int i; - - if ( iMouseInUse || g_iVisibleMouse ) - return; - - // perform button actions - for (i=0 ; ivalue) - { - mouse_x = (mx + old_mouse_x) * 0.5; - mouse_y = (my + old_mouse_y) * 0.5; - } - else - { - mouse_x = mx; - mouse_y = my; - } - - old_mouse_x = mx; - old_mouse_y = my; - - if ( gHUD.GetSensitivity() != 0 ) - { - mouse_x *= gHUD.GetSensitivity(); - mouse_y *= gHUD.GetSensitivity(); - } - else - { - mouse_x *= sensitivity->value; - mouse_y *= sensitivity->value; - } - - // add mouse X/Y movement to cmd - if ( (in_strafe.state & 1) || (lookstrafe->value && (in_mlook.state & 1) )) - cmd->sidemove += m_side->value * mouse_x; - else - viewangles[YAW] -= m_yaw->value * mouse_x; - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - viewangles[PITCH] += m_pitch->value * mouse_y; - if (viewangles[PITCH] > cl_pitchdown->value) - viewangles[PITCH] = cl_pitchdown->value; - if (viewangles[PITCH] < -cl_pitchup->value) - viewangles[PITCH] = -cl_pitchup->value; - } - else - { - if ((in_strafe.state & 1) && gEngfuncs.IsNoClipping() ) - { - cmd->upmove -= m_forward->value * mouse_y; - } - else - { - cmd->forwardmove -= m_forward->value * mouse_y; - } - } - - // if the mouse has moved, force it to the center, so there's room to move - if ( mx || my ) - { - IN_ResetMouse(); - } - } - - gEngfuncs.SetViewAngles( (float *)viewangles ); -} - -/* -=========== -IN_Accumulate -=========== -*/ -void DLLEXPORT IN_Accumulate (void) -{ - //only accumulate mouse if we are not moving the camera with the mouse - if ( !iMouseInUse && !g_iVisibleMouse ) - { - if (mouseactive) - { - GetCursorPos (¤t_pos); - - mx_accum += current_pos.x - gEngfuncs.GetWindowCenterX(); - my_accum += current_pos.y - gEngfuncs.GetWindowCenterY(); - - // force the mouse to the center, so there's room to move - IN_ResetMouse(); - } - } - -} - -/* -=================== -IN_ClearStates -=================== -*/ -void DLLEXPORT IN_ClearStates (void) -{ - if ( !mouseactive ) - return; - - mx_accum = 0; - my_accum = 0; - mouse_oldbuttonstate = 0; -} - -/* -=============== -IN_StartupJoystick -=============== -*/ -void IN_StartupJoystick (void) -{ - int numdevs; - JOYCAPS jc; - MMRESULT mmr; - - // assume no joystick - joy_avail = 0; - - // abort startup if user requests no joystick - if ( gEngfuncs.CheckParm ("-nojoy", NULL ) ) - return; - - // verify joystick driver is present - if ((numdevs = joyGetNumDevs ()) == 0) - { - gEngfuncs.Con_DPrintf ("joystick not found -- driver not present\n\n"); - return; - } - - // cycle through the joystick ids for the first valid one - for (joy_id=0 ; joy_idvalue == 0.0) - { - // default joystick initialization - // 2 axes only with joystick control - dwAxisMap[JOY_AXIS_X] = AxisTurn; - // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; - dwAxisMap[JOY_AXIS_Y] = AxisForward; - // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; - } - else - { - if ( strcmp ( joy_name->string, "joystick") != 0 ) - { - // notify user of advanced controller - gEngfuncs.Con_Printf ("\n%s configured\n\n", joy_name->string); - } - - // advanced initialization here - // data supplied by user via joy_axisn cvars - dwTemp = (DWORD) joy_advaxisx->value; - dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisy->value; - dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisz->value; - dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisr->value; - dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisu->value; - dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisv->value; - dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; - } - - // compute the axes to collect from DirectInput - joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; - for (i = 0; i < JOY_MAX_AXES; i++) - { - if (dwAxisMap[i] != AxisNada) - { - joy_flags |= dwAxisFlags[i]; - } - } -} - - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ - int i, key_index; - DWORD buttonstate, povstate; - - if (!joy_avail) - { - return; - } - - - // loop through the joystick buttons - // key a joystick event or auxillary event for higher number buttons for each state change - buttonstate = ji.dwButtons; - for (i=0 ; i < (int)joy_numbuttons ; i++) - { - if ( (buttonstate & (1<value != 0.0) - { - ji.dwUpos += 100; - } - return 1; - } - else - { - // read error occurred - // turning off the joystick seems too harsh for 1 read error,\ - // but what should be done? - // Con_Printf ("IN_ReadJoystick: no response\n"); - // joy_avail = 0; - return 0; - } -} - - -/* -=========== -IN_JoyMove -=========== -*/ -void IN_JoyMove ( float frametime, usercmd_t *cmd ) -{ - float speed, aspeed; - float fAxisValue, fTemp; - int i; - vec3_t viewangles; - - gEngfuncs.GetViewAngles( (float *)viewangles ); - - - // complete initialization if first time in - // this is needed as cvars are not available at initialization time - if( joy_advancedinit != 1 ) - { - Joy_AdvancedUpdate_f(); - joy_advancedinit = 1; - } - - // verify joystick is available and that the user wants to use it - if (!joy_avail || !in_joystick->value) - { - return; - } - - // collect the joystick data, if possible - if (IN_ReadJoystick () != 1) - { - return; - } - - if (in_speed.state & 1) - speed = cl_movespeedkey->value; - else - speed = 1; - - aspeed = speed * frametime; - - // loop through the axes - for (i = 0; i < JOY_MAX_AXES; i++) - { - // get the floating point zero-centered, potentially-inverted data for the current axis - fAxisValue = (float) *pdwRawValue[i]; - // move centerpoint to zero - fAxisValue -= 32768.0; - - if (joy_wwhack2->value != 0.0) - { - if (dwAxisMap[i] == AxisTurn) - { - // this is a special formula for the Logitech WingMan Warrior - // y=ax^b; where a = 300 and b = 1.3 - // also x values are in increments of 800 (so this is factored out) - // then bounds check result to level out excessively high spin rates - fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); - if (fTemp > 14000.0) - fTemp = 14000.0; - // restore direction information - fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; - } - } - - // convert range from -32768..32767 to -1..1 - fAxisValue /= 32768.0; - - switch (dwAxisMap[i]) - { - case AxisForward: - if ((joy_advanced->value == 0.0) && (in_jlook.state & 1)) - { - // user wants forward control to become look control - if (fabs(fAxisValue) > joy_pitchthreshold->value) - { - // if mouse invert is on, invert the joystick pitch value - // only absolute control support here (joy_advanced is 0) - if (m_pitch->value < 0.0) - { - viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; - } - else - { - viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; - } - V_StopPitchDrift(); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if(lookspring->value == 0.0) - { - V_StopPitchDrift(); - } - } - } - else - { - // user wants forward control to be forward control - if (fabs(fAxisValue) > joy_forwardthreshold->value) - { - cmd->forwardmove += (fAxisValue * joy_forwardsensitivity->value) * speed * cl_forwardspeed->value; - } - } - break; - - case AxisSide: - if (fabs(fAxisValue) > joy_sidethreshold->value) - { - cmd->sidemove += (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; - } - break; - - case AxisTurn: - if ((in_strafe.state & 1) || (lookstrafe->value && (in_jlook.state & 1))) - { - // user wants turn control to become side control - if (fabs(fAxisValue) > joy_sidethreshold->value) - { - cmd->sidemove -= (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; - } - } - else - { - // user wants turn control to be turn control - if (fabs(fAxisValue) > joy_yawthreshold->value) - { - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * aspeed * cl_yawspeed->value; - } - else - { - viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * speed * 180.0; - } - - } - } - break; - - case AxisLook: - if (in_jlook.state & 1) - { - if (fabs(fAxisValue) > joy_pitchthreshold->value) - { - // pitch movement detected and pitch movement desired by user - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; - } - else - { - viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * speed * 180.0; - } - V_StopPitchDrift(); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if( lookspring->value == 0.0 ) - { - V_StopPitchDrift(); - } - } - } - break; - - default: - break; - } - } - - // bounds check pitch - if (viewangles[PITCH] > cl_pitchdown->value) - viewangles[PITCH] = cl_pitchdown->value; - if (viewangles[PITCH] < -cl_pitchup->value) - viewangles[PITCH] = -cl_pitchup->value; - - gEngfuncs.SetViewAngles( (float *)viewangles ); - -} - -/* -=========== -IN_Move -=========== -*/ -void IN_Move ( float frametime, usercmd_t *cmd) -{ - if ( !iMouseInUse && mouseactive ) - { - IN_MouseMove ( frametime, cmd); - } - - IN_JoyMove ( frametime, cmd); -} - -/* -=========== -IN_Init -=========== -*/ -void IN_Init (void) -{ - m_filter = gEngfuncs.pfnRegisterVariable ( "m_filter","0", FCVAR_ARCHIVE ); - sensitivity = gEngfuncs.pfnRegisterVariable ( "sensitivity","3", FCVAR_ARCHIVE ); // user mouse sensitivity setting. - - in_joystick = gEngfuncs.pfnRegisterVariable ( "joystick","0", FCVAR_ARCHIVE ); - joy_name = gEngfuncs.pfnRegisterVariable ( "joyname", "joystick", 0 ); - joy_advanced = gEngfuncs.pfnRegisterVariable ( "joyadvanced", "0", 0 ); - joy_advaxisx = gEngfuncs.pfnRegisterVariable ( "joyadvaxisx", "0", 0 ); - joy_advaxisy = gEngfuncs.pfnRegisterVariable ( "joyadvaxisy", "0", 0 ); - joy_advaxisz = gEngfuncs.pfnRegisterVariable ( "joyadvaxisz", "0", 0 ); - joy_advaxisr = gEngfuncs.pfnRegisterVariable ( "joyadvaxisr", "0", 0 ); - joy_advaxisu = gEngfuncs.pfnRegisterVariable ( "joyadvaxisu", "0", 0 ); - joy_advaxisv = gEngfuncs.pfnRegisterVariable ( "joyadvaxisv", "0", 0 ); - joy_forwardthreshold = gEngfuncs.pfnRegisterVariable ( "joyforwardthreshold", "0.15", 0 ); - joy_sidethreshold = gEngfuncs.pfnRegisterVariable ( "joysidethreshold", "0.15", 0 ); - joy_pitchthreshold = gEngfuncs.pfnRegisterVariable ( "joypitchthreshold", "0.15", 0 ); - joy_yawthreshold = gEngfuncs.pfnRegisterVariable ( "joyyawthreshold", "0.15", 0 ); - joy_forwardsensitivity = gEngfuncs.pfnRegisterVariable ( "joyforwardsensitivity", "-1.0", 0 ); - joy_sidesensitivity = gEngfuncs.pfnRegisterVariable ( "joysidesensitivity", "-1.0", 0 ); - joy_pitchsensitivity = gEngfuncs.pfnRegisterVariable ( "joypitchsensitivity", "1.0", 0 ); - joy_yawsensitivity = gEngfuncs.pfnRegisterVariable ( "joyyawsensitivity", "-1.0", 0 ); - joy_wwhack1 = gEngfuncs.pfnRegisterVariable ( "joywwhack1", "0.0", 0 ); - joy_wwhack2 = gEngfuncs.pfnRegisterVariable ( "joywwhack2", "0.0", 0 ); - - gEngfuncs.pfnAddCommand ("force_centerview", Force_CenterView_f); - gEngfuncs.pfnAddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); - - IN_StartupMouse (); - IN_StartupJoystick (); -} \ No newline at end of file diff --git a/cl_dll/saytext.cpp b/cl_dll/saytext.cpp index cce0f90..acf5721 100644 --- a/cl_dll/saytext.cpp +++ b/cl_dll/saytext.cpp @@ -143,7 +143,8 @@ int CHudSayText :: MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ) void CHudSayText :: SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex ) { // find an empty string slot - for ( int i = 0; i < MAX_LINES; i++ ) + int i; + for ( i = 0; i < MAX_LINES; i++ ) { if ( ! *g_szLineBuffer[i] ) break; @@ -285,4 +286,4 @@ void CHudSayText :: EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ) } } } -} \ No newline at end of file +} diff --git a/cl_dll/scoreboard.cpp b/cl_dll/scoreboard.cpp index dbef510..ef340eb 100644 --- a/cl_dll/scoreboard.cpp +++ b/cl_dll/scoreboard.cpp @@ -18,9 +18,9 @@ // implementation of CHudScoreboard class // -#include "STDIO.H" -#include "STDLIB.H" -#include "MATH.H" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" #include "hud.h" #include "cl_util.h" @@ -134,7 +134,8 @@ int CHudScoreboard :: Draw( float fTime ) } // clear out team scores - for ( int i = 1; i <= m_iNumTeams; i++ ) + int i; + for ( i = 1; i <= m_iNumTeams; i++ ) { if ( !g_TeamInfo[i].scores_overriden ) g_TeamInfo[i].frags = g_TeamInfo[i].deaths = 0; @@ -151,7 +152,8 @@ int CHudScoreboard :: Draw( float fTime ) continue; // skip over players who are not in a team // find what team this player is in - for ( int j = 1; j <= m_iNumTeams; j++ ) + int j; + for ( j = 1; j <= m_iNumTeams; j++ ) { if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) break; @@ -281,8 +283,8 @@ int CHudScoreboard :: DrawPlayers( int xpos_rel, float list_slot, int nameoffset // Find the top ranking player int highest_frags = -99999; int lowest_deaths = 99999; int best_player = 0; - - for ( int i = 1; i < MAX_PLAYERS; i++ ) + int i; + for ( i = 1; i < MAX_PLAYERS; i++ ) { if ( g_PlayerInfoList[i].name && g_PlayerExtraInfo[i].frags >= highest_frags ) { @@ -375,7 +377,8 @@ int CHudScoreboard :: DrawPlayers( int xpos_rel, float list_slot, int nameoffset void CHudScoreboard :: GetAllPlayersInfo( void ) { - for ( int i = 1; i < MAX_PLAYERS; i++ ) + int i; + for ( i = 1; i < MAX_PLAYERS; i++ ) { GetPlayerInfo( i, &g_PlayerInfoList[i] ); @@ -427,7 +430,8 @@ int CHudScoreboard :: MsgFunc_TeamInfo( const char *pszName, int iSize, void *pb // rebuild the list of teams // clear out player counts from teams - for ( int i = 1; i <= m_iNumTeams; i++ ) + int i; + for ( i = 1; i <= m_iNumTeams; i++ ) { g_TeamInfo[i].players = 0; } @@ -444,7 +448,8 @@ int CHudScoreboard :: MsgFunc_TeamInfo( const char *pszName, int iSize, void *pb continue; // skip over players who are not in a team // is this player in an existing team? - for ( int j = 1; j <= m_iNumTeams; j++ ) + int j; + for ( j = 1; j <= m_iNumTeams; j++ ) { if ( g_TeamInfo[j].name[0] == '\0' ) break; @@ -456,7 +461,8 @@ int CHudScoreboard :: MsgFunc_TeamInfo( const char *pszName, int iSize, void *pb if ( j > m_iNumTeams ) { // they aren't in a listed team, so make a new one // search through for an empty team slot - for ( int j = 1; j <= m_iNumTeams; j++ ) + int j; + for ( j = 1; j <= m_iNumTeams; j++ ) { if ( g_TeamInfo[j].name[0] == '\0' ) break; @@ -492,7 +498,8 @@ int CHudScoreboard :: MsgFunc_TeamScore( const char *pszName, int iSize, void *p char *TeamName = READ_STRING(); // find the team matching the name - for ( int i = 1; i <= m_iNumTeams; i++ ) + int i; + for ( i = 1; i <= m_iNumTeams; i++ ) { if ( !stricmp( TeamName, g_TeamInfo[i].name ) ) break; @@ -519,4 +526,4 @@ void CHudScoreboard :: DeathMsg( int killer, int victim ) if ( killer == m_iPlayerNum ) m_iLastKilledBy = m_iPlayerNum; } -} \ No newline at end of file +} diff --git a/cl_dll/util.cpp b/cl_dll/util.cpp index 54b1f0c..343c654 100644 --- a/cl_dll/util.cpp +++ b/cl_dll/util.cpp @@ -18,9 +18,9 @@ // implementation of class-less helper functions // -#include "STDIO.H" -#include "STDLIB.H" -#include "MATH.H" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" #include "hud.h" #include "cl_util.h" @@ -29,7 +29,7 @@ #include "in_defs.h" #include "com_model.h" -vec3_t vec3_origin( 0, 0, 0 ); +// vec3_t vec3_origin( 0, 0, 0 ); double sqrt(double x); @@ -431,4 +431,4 @@ mleaf_t *Mod_PointInLeaf( Vector p, mnode_t *node ) // never reached return NULL; -} \ No newline at end of file +} diff --git a/cl_dll/util_vector.h b/cl_dll/util_vector.h index 2255b33..697fac7 100644 --- a/cl_dll/util_vector.h +++ b/cl_dll/util_vector.h @@ -15,11 +15,14 @@ // Vector.h // A subset of the extdll.h in the project HL Entity DLL // +#pragma once +#ifndef UTIL_VECTOR_H +#define UTIL_VECTOR_H // Misc C-runtime library headers -#include "STDIO.H" -#include "STDLIB.H" -#include "MATH.H" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" // Header file containing definition of globalvars_t and entvars_t typedef int func_t; // @@ -121,3 +124,4 @@ inline Vector CrossProduct(const Vector& a, const Vector& b) { return Vector( a. #define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist) #define vec3_t Vector +#endif // UTIL_VECTOR_H diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index be6a115..ab53831 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -7,12 +7,12 @@ // view/refresh setup functions +#include #include "hud.h" #include "cl_util.h" #include "cvardef.h" #include "usercmd.h" #include "const.h" -#include #include "entity_state.h" #include "cl_entity.h" #include "ref_params.h" diff --git a/cl_dll/wscript b/cl_dll/wscript new file mode 100644 index 0000000..a407a76 --- /dev/null +++ b/cl_dll/wscript @@ -0,0 +1,51 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from waflib import Utils +import os + +def options(opt): + # stub + return + +def configure(conf): + # stub + return + +def build(bld): + source = bld.path.parent.ant_glob([ + 'pm_shared/*.c' + ]) + + source += [ + 'cdll_int.cpp', '../game_shared/common.cpp', 'entity.cpp', 'hud.cpp', + 'hud_draw.cpp', 'hud_msg.cpp', 'hud_redraw.cpp', 'hud_sbar.cpp', + 'hud_update.cpp', 'in_camera.cpp', 'input.cpp', 'input_xash3d.cpp', 'input_mouse.cpp', + 'message.cpp', 'parsemsg.cpp', 'saytext.cpp', 'scoreboard.cpp', + '../game_shared/stringlib.cpp', 'StudioModelRenderer.cpp', 'text_message.cpp', + 'util.cpp', 'view.cpp' + ] + + includes = Utils.to_list('. ../common ../engine ../game_shared ../dlls ../pm_shared ') + + libs = [] + + defines = ['CLIENT_DLL'] + + if bld.env.DEST_OS2 not in ['android']: + install_path = os.path.join(bld.env.GAMEDIR, bld.env.CLIENT_DIR) + else: + install_path = bld.env.PREFIX + + bld.shlib( + source = source, + target = 'client', + features = 'c cxx', + includes = includes, + defines = defines, + use = libs, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = 1 + ) diff --git a/common/features.h b/common/enginefeatures.h similarity index 100% rename from common/features.h rename to common/enginefeatures.h diff --git a/contrib/tyabus/Android.mk b/contrib/tyabus/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/contrib/tyabus/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/contrib/tyabus/cl_dll/Android.mk b/contrib/tyabus/cl_dll/Android.mk new file mode 100644 index 0000000..44bd265 --- /dev/null +++ b/contrib/tyabus/cl_dll/Android.mk @@ -0,0 +1,58 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := client +#ifeq ($(XASH_SDL),1) +#APP_PLATFORM := android-12 +#LOCAL_SHARED_LIBRARIES += SDL2 +#LOCAL_CFLAGS += -DXASH_SDL +#else +APP_PLATFORM := android-8 +#endif + +include $(XASH3D_CONFIG) + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a-hard) +LOCAL_MODULE_FILENAME = libclient_hardfp +endif + +INCLUDES = -I../common -I. -I../game_shared -I../pm_shared -I../engine -I../dlls -I../utils/false_vgui/include +DEFINES = -fsigned-char -Wno-write-strings -DLINUX -D_LINUX -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DCLIENT_WEAPONS -DCLIENT_DLL -w -D_snprintf=snprintf + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/. \ + $(LOCAL_PATH)/../common \ + $(LOCAL_PATH)/../engine \ + $(LOCAL_PATH)/../game_shared \ + $(LOCAL_PATH)/../dlls \ + $(LOCAL_PATH)/../pm_shared + +LOCAL_CFLAGS += $(DEFINES) $(INCLUDES) + +LOCAL_SRC_FILES := cdll_int.cpp \ + ../game_shared/common.cpp \ + entity.cpp \ + hud.cpp \ + hud_draw.cpp \ + hud_msg.cpp \ + hud_redraw.cpp \ + hud_sbar.cpp \ + hud_update.cpp \ + in_camera.cpp \ + input.cpp \ + input_xash3d.cpp \ + input_mouse.cpp \ + message.cpp \ + parsemsg.cpp \ + ../pm_shared/pm_debug.c \ + ../pm_shared/pm_math.c \ + ../pm_shared/pm_shared.c \ + saytext.cpp \ + scoreboard.cpp \ + ../game_shared/stringlib.cpp \ + StudioModelRenderer.cpp \ + text_message.cpp \ + util.cpp \ + view.cpp \ + +include $(BUILD_SHARED_LIBRARY) diff --git a/contrib/tyabus/dlls/Android.mk b/contrib/tyabus/dlls/Android.mk new file mode 100644 index 0000000..dac4bd8 --- /dev/null +++ b/contrib/tyabus/dlls/Android.mk @@ -0,0 +1,49 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := server + +include $(XASH3D_CONFIG) + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a-hard) +LOCAL_MODULE_FILENAME = libserver_hardfp +endif + +LOCAL_CFLAGS += -D_LINUX -DCLIENT_WEAPONS -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf \ + -fno-exceptions -fsigned-char -w + +LOCAL_CPPFLAGS := $(LOCAL_CFLAGS) + +LOCAL_C_INCLUDES := $(SDL_PATH)/include \ + $(LOCAL_PATH)/. \ + $(LOCAL_PATH)/../common \ + $(LOCAL_PATH)/../engine \ + $(LOCAL_PATH)/../pm_shared \ + $(LOCAL_PATH)/../game_shared + +LOCAL_SRC_FILES := client.cpp \ + ../game_shared/common.cpp \ + crc.cpp \ + dll_int.cpp \ + game.cpp \ + globals.cpp \ + physics.cpp \ + pr_cmds.cpp \ + pr_edict.cpp \ + pr_exec.cpp \ + pr_message.cpp \ + pr_move.cpp \ + pr_phys.cpp \ + pr_save.cpp \ + pr_world.cpp \ + saverestore.cpp \ + ../game_shared/stringlib.cpp \ + util.cpp \ + ../pm_shared/pm_debug.c \ + ../pm_shared/pm_math.c \ + ../pm_shared/pm_shared.c + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/dlls/client.cpp b/dlls/client.cpp index ef45bdc..b686b04 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -536,7 +536,7 @@ void UpdateClientData( edict_t *pEntity ) if (pev->health != pcache->health) { - int iHealth = max( pev->health, 0 ); // make sure that no negative health values are sent + int iHealth = Q_max( pev->health, 0 ); // make sure that no negative health values are sent MESSAGE_BEGIN( MSG_ONE, gmsgStats, pEntity ); WRITE_BYTE( STAT_HEALTH ); @@ -1625,4 +1625,4 @@ int ShouldCollide( edict_t *pentTouched, edict_t *pentOther ) // ED_UpdateEdictFields( pentOther ); return 1; -} \ No newline at end of file +} diff --git a/dlls/dll_int.cpp b/dlls/dll_int.cpp index 1fd0ceb..0cf719e 100644 --- a/dlls/dll_int.cpp +++ b/dlls/dll_int.cpp @@ -191,12 +191,14 @@ void OnFreeEntPrivateData( edict_s *pEdict ) } // Required DLL entry point +#ifdef _WIN32 BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { return TRUE; } +#endif // _WIN32 -void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) +extern "C" void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) { memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); gpGlobals = pGlobals; @@ -204,4 +206,4 @@ void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t g_iXashEngineBuildNumber = CVAR_GET_FLOAT( "build" ); // 0 for old builds or GoldSrc if( g_iXashEngineBuildNumber <= 0 ) g_iXashEngineBuildNumber = (int)CVAR_GET_FLOAT( "buildnum" ); -} \ No newline at end of file +} diff --git a/dlls/exportdef.h b/dlls/exportdef.h new file mode 100644 index 0000000..363d8d1 --- /dev/null +++ b/dlls/exportdef.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef EXPORTDEF_H +#define EXPORTDEF_H +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define EXPORT __attribute__ ((dllexport)) + #else + #define EXPORT __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. + #endif +#else + #if __GNUC__ >= 4 + #define EXPORT __attribute__ ((visibility ("default"))) + #else + #define EXPORT + #endif +#endif +#define DLLEXPORT EXPORT +#define _DLLEXPORT EXPORT +#endif // EXPORTDEF_H diff --git a/dlls/extdll.h b/dlls/extdll.h index 9138cdf..633ed1e 100644 --- a/dlls/extdll.h +++ b/dlls/extdll.h @@ -54,8 +54,10 @@ typedef int BOOL; #endif #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) +#ifndef _vsnprintf #define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) #endif +#endif #endif //_WIN32 #ifdef _WIN32 diff --git a/dlls/physics.cpp b/dlls/physics.cpp index 110f864..9ba4676 100644 --- a/dlls/physics.cpp +++ b/dlls/physics.cpp @@ -25,9 +25,8 @@ #include "client.h" #include "physcallback.h" #include "com_model.h" -#include "features.h" +#include "enginefeatures.h" #include "pm_defs.h" -#include "features.h" extern DLL_GLOBAL BOOL g_fXashEngine; @@ -236,4 +235,4 @@ int Server_GetPhysicsInterface( int iVersion, server_physics_api_t *pfuncsFromEn memcpy( pFunctionTable, &gPhysicsInterface, iImportSize ); return TRUE; -} \ No newline at end of file +} diff --git a/dlls/pr_edict.cpp b/dlls/pr_edict.cpp index 3b98acd..2478d22 100644 --- a/dlls/pr_edict.cpp +++ b/dlls/pr_edict.cpp @@ -521,7 +521,8 @@ void ED_Print( edict_t *ed ) // if the value is still all 0, skip the field int type = d->type & ~DEF_SAVEGLOBAL; - for( int j = 0; j < gProgSizes[type]; j++ ) + int j; + for( j = 0; j < gProgSizes[type]; j++ ) if( v[j] ) break; if( j == gProgSizes[type] ) @@ -1100,4 +1101,4 @@ void PR_UnloadProgs( void ) free( pr.source_globals ); free( pr.temp_entvars ); memset( &pr, 0, sizeof( prog_state_t )); -} \ No newline at end of file +} diff --git a/dlls/pr_message.cpp b/dlls/pr_message.cpp index 3d04a34..3a21b10 100644 --- a/dlls/pr_message.cpp +++ b/dlls/pr_message.cpp @@ -228,7 +228,8 @@ void PR_SendMessage( void ) return; } - for( int msg_num = 0; msg_num < pr.num_messages; ) + int msg_num; + for( msg_num = 0; msg_num < pr.num_messages; ) { pr_message_t *msg = &pr.messages[msg_num]; @@ -478,4 +479,4 @@ void PR_SendMessage( void ) // clear the queue pr.num_messages = 0; -} \ No newline at end of file +} diff --git a/dlls/pr_world.cpp b/dlls/pr_world.cpp index e376009..ad68dfa 100644 --- a/dlls/pr_world.cpp +++ b/dlls/pr_world.cpp @@ -18,7 +18,7 @@ GNU General Public License for more details. #include "client.h" #include "physcallback.h" #include "com_model.h" -#include "features.h" +#include "enginefeatures.h" /* ================== @@ -200,4 +200,4 @@ void SV_LinkEdict( edict_t *pent, BOOL touch_triggers ) SV_TouchLinks( pent, GET_AREANODE( )); g_fTouchSemaphore = FALSE; } -} \ No newline at end of file +} diff --git a/dlls/wscript b/dlls/wscript new file mode 100644 index 0000000..41e4908 --- /dev/null +++ b/dlls/wscript @@ -0,0 +1,47 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from waflib import Utils +import os + +def options(opt): + # stub + return + +def configure(conf): + # stub + return + +def build(bld): + defines = [] + source = bld.path.parent.ant_glob([ + 'pm_shared/*.c', + ]) + + source += [ + 'client.cpp', '../game_shared/common.cpp', 'crc.cpp', 'dll_int.cpp', 'game.cpp', 'globals.cpp', 'physics.cpp', + 'pr_cmds.cpp', 'pr_edict.cpp', 'pr_exec.cpp', 'pr_message.cpp', 'pr_move.cpp', + 'pr_phys.cpp', 'pr_save.cpp', 'pr_world.cpp', 'saverestore.cpp', + '../game_shared/stringlib.cpp', 'util.cpp'] + + includes = Utils.to_list('. ../common ../engine ../pm_shared ../game_shared') + + libs = [] + + if bld.env.DEST_OS2 not in ['android']: + install_path = os.path.join(bld.env.GAMEDIR, bld.env.SERVER_DIR) + else: + install_path = bld.env.PREFIX + + bld.shlib( + source = source, + target = 'server', + features = 'c cxx', + includes = includes, + defines = defines, + use = libs, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = 2 + ) diff --git a/game_shared/common.cpp b/game_shared/common.cpp index 804c477..4c7c9c0 100644 --- a/game_shared/common.cpp +++ b/game_shared/common.cpp @@ -14,7 +14,9 @@ GNU General Public License for more details. */ #define NOMINMAX +#ifdef _WIN32 #include +#endif #include #include @@ -164,7 +166,7 @@ char *COM_ParseFileExt( char *data, char *token, long token_size, bool allowNewL // skip whitespace skipwhite: - while(( c = ((byte)*data)) <= ' ' ) + while(( c = ((unsigned char)*data)) <= ' ' ) { if( c == 0 ) return NULL; // end of file; @@ -192,7 +194,7 @@ char *COM_ParseFileExt( char *data, char *token, long token_size, bool allowNewL data++; while( 1 ) { - c = (byte)*data++; + c = (unsigned char)*data++; if( c == '\"' || !c ) { if( len < token_size ) @@ -227,7 +229,7 @@ char *COM_ParseFileExt( char *data, char *token, long token_size, bool allowNewL token[len] = c; data++; len++; - c = ((byte)*data); + c = ((unsigned char)*data); if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',' || c == '|' ) break; @@ -312,4 +314,4 @@ unsigned int COM_HashKey( const char *string, unsigned int hashSize ) hashKey = (hashKey + i) * 37 + Q_tolower( string[i] ); return (hashKey % hashSize); -} \ No newline at end of file +} diff --git a/game_shared/stringlib.cpp b/game_shared/stringlib.cpp index 9724739..3ede2cf 100644 --- a/game_shared/stringlib.cpp +++ b/game_shared/stringlib.cpp @@ -4,9 +4,13 @@ //======================================================================= #define NOMINMAX +#ifdef _WIN32 #include +#endif #include #include +#include +#include #include #pragma warning(disable : 4244) // MIPS @@ -450,7 +454,7 @@ int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list ar { size_t result; - result = _vsnprintf( buffer, buffersize, format, args ); + result = vsnprintf( buffer, buffersize, format, args ); if( result < 0 || result >= buffersize ) { @@ -578,4 +582,4 @@ char *Q_pretifymem( float value, int digitsafterdecimal ) *o = 0; // terminate return out; -} \ No newline at end of file +} diff --git a/mainui/menu.cpp b/mainui/menu.cpp index ae0e878..b9bf834 100644 --- a/mainui/menu.cpp +++ b/mainui/menu.cpp @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "Menu.h" +#include "menu.h" enum m_state_e { @@ -136,15 +136,15 @@ void COM_FileBase ( const char *in, char *out ) int len, start, end; len = strlen( in ); - + // scan backward for '.' end = len - 1; while ( end && in[end] != '.' && in[end] != '/' && in[end] != '\\' ) end--; - + if ( in[end] != '.' ) // no '.', copy to end end = len-1; - else + else end--; // Found ',', copy to left of '.' @@ -155,7 +155,7 @@ void COM_FileBase ( const char *in, char *out ) if ( in[start] != '/' && in[start] != '\\' ) start = 0; - else + else start++; // Length of new sting @@ -1996,7 +1996,8 @@ void M_Quit_Draw (void) for( int i = 0; i < 4; i++ ) { - M_PrintWhite( 16, 68 + i*8, quitMessage[msgNumber*4 + i] ); + // center message + M_PrintWhite( 16 + 8 * 5, 68 + i*8, quitMessage[msgNumber*4 + i] ); } /*M_Print (16, 52, " \n"); M_Print (16, 60, " \n"); diff --git a/mainui/udll_int.cpp b/mainui/udll_int.cpp index ad455d0..ecfc932 100644 --- a/mainui/udll_int.cpp +++ b/mainui/udll_int.cpp @@ -14,8 +14,7 @@ GNU General Public License for more details. */ #include "menu.h" - -#define EXPORT __declspec(dllexport) +#include "exportdef.h" ui_enginefuncs_t engfuncs; ui_globalvars_t *gpGlobals; diff --git a/scripts/waflib/deps.py b/scripts/waflib/deps.py new file mode 100644 index 0000000..f216515 --- /dev/null +++ b/scripts/waflib/deps.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# Michel Mooij, michel.mooij7@gmail.com + +from waflib import Utils +from waflib import Errors + + +def get_deps(bld, target): + '''Returns a list of (nested) targets on which this target depends. + + :param bld: a *waf* build instance from the top level *wscript* + :type bld: waflib.Build.BuildContext + :param target: task name for which the dependencies should be returned + :type target: str + :returns: a list of task names on which the given target depends + ''' + try: + tgen = bld.get_tgen_by_name(target) + except Errors.WafError: + return [] + else: + uses = Utils.to_list(getattr(tgen, 'use', [])) + deps = uses[:] + for use in uses: + deps += get_deps(bld, use) + return list(set(deps)) + + +def get_tgens(bld, names): + '''Returns a list of task generators based on the given list of task + generator names. + + :param bld: a *waf* build instance from the top level *wscript* + :type bld: waflib.Build.BuildContext + :param names: list of task generator names + :type names: list of str + :returns: list of task generators + ''' + tgens=[] + for name in names: + try: + tgen = bld.get_tgen_by_name(name) + except Errors.WafError: + pass + else: + tgens.append(tgen) + return list(set(tgens)) + + +def get_targets(bld): + '''Returns a list of user specified build targets or None if no specific + build targets has been selected using the *--targets=* command line option. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + :returns: a list of user specified target names (using --targets=x,y,z) or None + ''' + if bld.targets == '': + return None + targets = bld.targets.split(',') + for target in targets: + targets += get_deps(bld, target) + return targets + diff --git a/scripts/waflib/force_32bit.py b/scripts/waflib/force_32bit.py new file mode 100644 index 0000000..efec6b1 --- /dev/null +++ b/scripts/waflib/force_32bit.py @@ -0,0 +1,56 @@ +# 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 + else: + flags = ['-m32'] + # Think different. + if(conf.env.DEST_OS == 'darwin'): + flags = ['-arch', 'i386'] + env_stash = conf.env + 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!') diff --git a/scripts/waflib/force_32bit.pyc b/scripts/waflib/force_32bit.pyc new file mode 100644 index 0000000..456992a Binary files /dev/null and b/scripts/waflib/force_32bit.pyc differ diff --git a/scripts/waflib/fwgslib.py b/scripts/waflib/fwgslib.py new file mode 100644 index 0000000..e7cdee0 --- /dev/null +++ b/scripts/waflib/fwgslib.py @@ -0,0 +1,30 @@ +# 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_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 diff --git a/scripts/waflib/fwgslib.pyc b/scripts/waflib/fwgslib.pyc new file mode 100644 index 0000000..08921cb Binary files /dev/null and b/scripts/waflib/fwgslib.pyc differ diff --git a/scripts/waflib/msdev.py b/scripts/waflib/msdev.py new file mode 100644 index 0000000..df6b2ca --- /dev/null +++ b/scripts/waflib/msdev.py @@ -0,0 +1,774 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# Michel Mooij, michel.mooij7@gmail.com +# modified: Alibek Omarov, a1ba.omarov@gmail.com + +''' +Summary +------- +Exports and converts *waf* project data, for C/C++ programs, static- and shared +libraries, into **Microsoft Visual Studio**, also known as **msdev**, +project files (.vcproj) and solution (.sln) files. + +**Microsoft Visual Studio** is a mature and stable integrated development +environment for, amongst others, the C and C++ programming language. A free +version of this IDE, known as the *express* version can be obtained from Microsoft +at http://wwww.visualstudio.com. + +Description +----------- +When exporting *waf* project data, a single **Visual Studio** solution will be +exported in the top level directory of your *WAF* build environment. This +solution file will contain references to all exported **Visual Studio** +projects and will include dependencies between those projects and will have the +same name as APPNAME variable from the top level *wscript* file. + +For each single task generator (*waflib.TaskGenerator*), for instance a +*bld.program(...)* which has been defined within a *wscript* file somewhere in +the build environment, a single **Visual Studio** project file will be generated +in the same directory as where the task generator has been defined. +The name of this task generator will be used as name for the exported **Visual +Studio** project file. If for instance the name of the task generator is +*hello*, then a **Visual Studio** project file named *hello.vcproj* will be +exported. + +Example below presents an overview of an environment in which **Visual Studio** +files already have been exported:: + + . + ├── components + │ └── clib + │ ├── program + │ │ ├── cprogram.vcproj + │ │ └── wscript + │ ├── shared + │ │ ├── cshlib.vcproj + │ │ └── wscript + │ └── static + │ ├── cstlib.vcproj + │ └── wscript + │ + ├── waf.vcproj + ├── appname.sln + └── wscript + + +Projects will be exported such that they will use the same settings and +structure as has been defined for that build task within the *waf* build +environment as much as possible. Note that since cross compilation is not +really supported in this IDE, only the first environment encountered that +is targeted for **MS Windows** will be exported; i.e. an environment in +which:: + + bld.env.DEST_OS == 'win32' + +is true. + + +Please note that in contrast to a *normal* IDE setup the exported projects +will contain either a *debug* **or** a *release* build target but not both at +the same time. By doing so exported projects will always use the same settings +(e.g. compiler options, installation paths) as when building the same task in +the *waf* build environment from command line. + + +Usage +----- +**Visual Studio** project and workspace files can be exported using the *msdev* +command, as shown in the example below:: + + $ waf msdev + +When needed, exported **Visual Studio** project- and solution files can be +removed using the *clean* command, as shown in the example below:: + + $ waf msdev --clean + +Once exported simply open the *appname.sln* using **Visual Studio** +this will automatically open all exported projects as well. + +Tasks generators to be excluded can be marked with the *skipme* option +as shown below:: + + def build(bld): + bld.program(name='foo', src='foobar.c', msdev_skip=True) + +''' + +import os +import sys +import copy +import uuid +import shutil +import xml.etree.ElementTree as ElementTree +from xml.dom import minidom +from waflib import Utils, Logs, Errors, Context +from waflib.Build import BuildContext +# import waftools +# from waftools import deps +from deps import get_targets +from subproject import get_subproject_env + + +def options(opt): + '''Adds command line options to the *waf* build environment + + :param opt: Options context from the *waf* build environment. + :type opt: waflib.Options.OptionsContext + ''' + opt.add_option('--msdev', dest='msdev', default=False, action='store_true', help='select msdev for export/import actions') + opt.add_option('--clean', dest='clean', default=False, action='store_true', help='delete exported files') + + +def configure(conf): + '''Method that will be invoked by *waf* when configuring the build + environment. + + :param conf: Configuration context from the *waf* build environment. + :type conf: waflib.Configure.ConfigurationContext + ''' + pass + + +class MsDevContext(BuildContext): + '''export C/C++ tasks to MS Visual Studio projects and solutions.''' + cmd = 'msdev' + + def execute(self): + '''Will be invoked when issuing the *msdev* command.''' + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + self.pre_build() + + for group in self.groups: + for tgen in group: + try: + f = tgen.post + except AttributeError: + pass + else: + f() + try: + self.get_tgen_by_name('') + except Exception: + pass + + self.msdev = True + if self.options.clean: + cleanup(self) + else: + export(self) + self.timer = Utils.Timer() + +def export(bld): + '''Exports all C and C++ task generators as **Visual Studio** projects + and creates a **Visual Studio** solution containing references to + those project. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + ''' + if not bld.options.msdev and not hasattr(bld, 'msdev'): + return + + Logs.pprint('RED', '''This tool is intended only to ease development for Windows-fags. +Don't use it for release builds, as it doesn't enables WinXP compatibility for now!''') + + solution = MsDevSolution(bld) + targets = get_targets(bld) + + saveenv = bld.env # root env + for tgen in bld.task_gen_cache_names.values(): + if targets and tgen.get_name() not in targets: + continue + if getattr(tgen, 'msdev_skipme', False): + continue + try: + bld.env = get_subproject_env(bld, tgen.path, True) + except IndexError: + bld.env = saveenv + if set(('c', 'cxx')) & set(getattr(tgen, 'features', [])): + project = MsDevProject(bld, tgen) + project.export() + + (name, fname, deps, pid) = project.get_metadata() + solution.add_project(name, fname, deps, pid) + + solution.export() + + +def cleanup(bld): + '''Removes all **Visual Studio** projects and workspaces from the + *waf* build environment. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + ''' + if not bld.options.msdev and not hasattr(bld, 'msdev'): + return + + targets = get_targets(bld) + saveenv = bld.env + + for tgen in bld.task_gen_cache_names.values(): + if targets and tgen.get_name() not in targets: + continue + if getattr(tgen, 'msdev_skipme', False): + continue + try: + bld.env = get_subproject_env(bld, tgen.path) + except IndexError: + bld.env = saveenv + if set(('c', 'cxx')) & set(getattr(tgen, 'features', [])): + project = MsDevProject(bld, tgen) + project.cleanup() + + solution = MsDevSolution(bld) + solution.cleanup() + + +class MsDev(object): + '''Abstract base class used for exporting *waf* project data to + **Visual Studio** projects and solutions. + + REMARK: + bld.objects() taks generators are treated as static libraries. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + ''' + + PROGRAM = '1' + '''Identifier for projects containing an executable''' + + SHLIB = '2' + '''Identifier for projects containing a shared library''' + + STLIB = '4' + '''Identifier for projects containing a static library''' + + C = 'c' + '''Identifier for projects using C language''' + + CXX = 'cxx' + '''Identifier for projects using C++ language''' + + def __init__(self, bld): + self.bld = bld + + def export(self): + '''Exports a **Visual Studio** solution or project.''' + content = self.get_content() + if not content: + return + if self.xml_clean: + content = self.xml_clean(content) + + node = self.make_node() + if not node: + return + node.write(content) + Logs.pprint('YELLOW', 'exported: %s' % node.abspath()) + + def cleanup(self): + '''Deletes a **Visual Studio** solution or project file including + associated files (e.g. *.ncb*). + ''' + cwd = self.get_cwd() + for node in cwd.ant_glob('*.user'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.ncb'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.suo'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.sln'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + node = self.find_node() + if node: + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + + def get_cwd(self): + cwd = os.path.dirname(self.get_fname()) + if cwd == "": + cwd = "." + return self.bld.srcnode.find_node(cwd) + + def find_node(self): + name = self.get_fname() + if not name: + return None + return self.bld.srcnode.find_node(name) + + def make_node(self): + name = self.get_fname() + if not name: + return None + return self.bld.srcnode.make_node(name.lower()) + + def get_fname(self): + ''' Returns file name.''' + return None + + def get_content(self): + ''' Returns file content.''' + return None + + def xml_clean(self, content): + s = minidom.parseString(content).toprettyxml(indent="\t") + lines = [l for l in s.splitlines() if not l.isspace() and len(l)] + lines[0] = '' + return '\n'.join(lines) + + +class MsDevSolution(MsDev): + '''Class used for exporting *waf* project data to a **Visual Studio** + solution located in the lop level directory of the *waf* build + environment. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + ''' + + def __init__(self, bld): + super(MsDevSolution, self).__init__(bld) + self.projects = {} + self.xml_clean = None + + def get_fname(self): + '''Returns the workspace's file name.''' + return '%s.sln' % getattr(Context.g_module, Context.APPNAME) + + def export(self): + '''Exports a **Visual Studio** solution.''' + dst = self.get_fname() + + s = MSDEV_SOLUTION + + with open(dst, 'w') as f: + for line in s[0:3]: + f.write(line) + for name, (fname, deps, pid) in self.projects.items(): + sid = str(uuid.uuid4()).upper() + f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' % (sid, name, fname, pid)) + if len(deps): + f.write('\tProjectSection(ProjectDependencies) = postProject\n') + for d in deps: + try: + (_, _, pid) = self.projects[d] + except KeyError: + pass + else: + f.write('\t\t{%s} = {%s}\n' % (pid, pid)) + f.write('\tEndProjectSection\n') + f.write('EndProject\n') + for line in s[3:8]: + f.write(line) + for _, (_, _, pid) in self.projects.items(): + f.write('\t\t{%s}.Debug|Win32.ActiveCfg = Debug|Win32\n' % (pid)) + f.write('\t\t{%s}.Debug|Win32.Build.0 = Debug|Win32\n' % (pid)) + for line in s[8:]: + f.write(line) + Logs.pprint('YELLOW', 'exported: %s' % os.path.abspath(dst)) + + def add_project(self, name, fname, deps, pid): + '''Adds a project to the workspace. + + :param name: Name of the project. + :type name: str + :param fname: Complete path to the project file + :type fname: str + :param deps: List of names on which this project depends + :type deps: list of str + ''' + self.projects[name] = (fname, deps, pid) + + +class MsDevProject(MsDev): + '''Class used for exporting *waf* project data to **Visual Studio** + projects. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + + :param gen: Task generator that contains all information of the task to be + converted and exported to the **Visual Studio** project. + :type gen: waflib.Task.TaskGen + ''' + + def __init__(self, bld, gen): + super(MsDevProject, self).__init__(bld) + self.gen = gen + self.id = str(uuid.uuid4()).upper() + self.type = self.get_type(gen) + self.language = self.get_language(gen) + self.buildpath = self.get_buildpath(bld, gen) + + def get_type(self, gen): + if set(('cprogram', 'cxxprogram')) & set(gen.features): + return MsDev.PROGRAM + elif set(('cshlib', 'cxxshlib')) & set(gen.features): + return MsDev.SHLIB + else: + return MsDev.STLIB + + def get_language(self, gen): + return MsDev.CXX if 'cxx' in gen.features else MsDev.C + + def get_buildpath(self, bld, gen): + pth = '%s/%s' % (bld.path.get_bld().path_from(gen.path), gen.path.relpath()) + return pth.replace('/', '\\') + + def get_fname(self): + '''Returns the project's file name.''' + return '%s/%s.vcproj' % (self.gen.path.relpath().replace('\\', '/'), self.gen.get_name()) + + def get_root(self): + '''Returns a document root, either from an existing file, or from template.''' + fname = self.get_fname() + if os.path.exists(fname): + tree = ElementTree.parse(fname) + root = tree.getroot() + else: + root = ElementTree.fromstring(MSDEV_PROJECT) + return root + + def get_content(self): + '''Returns the content of a project file.''' + root = self.get_root() + root.set('Name', self.gen.get_name()) + root.set('ProjectGUID', '{%s}' % self.id) + configurations = root.find('Configurations') + for configuration in configurations.iter('Configuration'): + configuration.set('ConfigurationType', '%s' % self.type) + configuration.set('OutputDirectory', '%s\\msdev' % self.buildpath) + configuration.set('IntermediateDirectory', '%s\\msdev' % self.buildpath) + for tool in configuration.iter('Tool'): + name = tool.get('Name') + if name == 'VCCLCompilerTool': + tool.set('PreprocessorDefinitions', '%s' % self.get_compiler_defines(self.gen)) + includes = [] + for include in self.get_compiler_includes(self.bld, self.gen): + includes.append('%s' % include) + tool.set('AdditionalIncludeDirectories', ';'.join(includes)) + if name == 'VCLinkerTool': + if self.type == MsDev.PROGRAM: + # Force Windows Subsystem + # TODO: this isn't enables Windows XP compatibility! + tool.set('SubSystem', '2') + self.update_link_deps(tool) + self.update_link_paths(tool) + files = root.find('Files') + self.update_includes(files) + self.update_sources(files) + return ElementTree.tostring(root) + + def update_includes(self, files): + '''Add include files.''' + includes = [] + for filtr in files.iter('Filter'): + if filtr.get('Name') == 'Header Files': + for include in filtr.iter('File'): + includes.append(include.get('RelativePath')) + break + if len(includes) == 0: + filtr = ElementTree.SubElement(files, 'Filter', attrib={'Name':'Header Files', 'Filter':'h;hpp;hxx;hm;inl;inc;xsd'}) + filtr.set('UniqueIdentifier', '{%s}' % str(uuid.uuid4()).upper()) + for include in self.get_include_files(self.bld, self.gen): + if include not in includes: + ElementTree.SubElement(filtr, 'File', attrib={'RelativePath':'%s' % include}) + + def update_sources(self, files): + '''Add source files.''' + sources = [] + for filtr in files.iter('Filter'): + if filtr.get('Name') == 'Source Files': + for source in filtr.iter('File'): + sources.append(source.get('RelativePath')) + break + if len(sources) == 0: + filtr = ElementTree.SubElement(files, 'Filter', attrib={'Name':'Source Files', 'Filter':'cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx'}) + filtr.set('UniqueIdentifier', '{%s}' % str(uuid.uuid4()).upper()) + for source in self.get_genlist(self.gen, 'source'): + if source not in sources: + ElementTree.SubElement(filtr, 'File', attrib={'RelativePath':'%s' % source}) + + def update_link_deps(self, tool): + '''Add libraries on which this project depends.''' + deps = tool.get('AdditionalDependencies') + + deps = [] # clean out deps everytime + + libs = self.get_link_libs(self.bld, self.gen) + for lib in libs: + dep = '%s.lib' % lib + if dep not in deps: + deps.append(dep) + if len(deps): + add_deps = " ".join(deps) # work around when converting to vcxproj by inserting spaces + tool.set('AdditionalDependencies', add_deps) + + def update_link_paths(self, tool): + deps = tool.get('AdditionalLibraryDirectories', '') + deps = [] + dirs = self.get_link_paths(self.bld, self.gen) + for dep in dirs: + if dep not in deps: + deps.append(dep) + if len(deps): + tool.set('AdditionalLibraryDirectories', ';'.join(deps)) + + def get_metadata(self): + '''Returns a tuple containing project information (name, file name and + dependencies). + ''' + name = self.gen.get_name() + fname = self.get_fname().replace('/', '\\') + deps = Utils.to_list(getattr(self.gen, 'use', [])) + return (name, fname, deps, self.id) + + def get_genlist(self, gen, name): + lst = Utils.to_list(getattr(gen, name, [])) + lst = [str(l.path_from(gen.path)) if hasattr(l, 'path_from') else l for l in lst] + return [l.replace('/', '\\') for l in lst] + + def get_compiler_options(self, bld, gen): + if self.language == MsDev.CXX: + flags = getattr(gen, 'cxxflags', []) + bld.env.CXXFLAGS + else: + flags = getattr(gen, 'cflags', []) + bld.env.CFLAGS + if self.type == MsDev.SHLIB: + if self.language == MsDev.CXX: + flags.extend(bld.env.CXXFLAGS_cxxshlib) + else: + flags.extend(bld.env.CFLAGS_cshlib) + return list(set(flags)) + + def get_compiler_includes(self, bld, gen): + includes = self.get_genlist(gen, 'includes') + for include in bld.env['INCLUDES']: + root = bld.path.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, include]) + if pref == root: + node = bld.root.find_dir(include) + if node: + includes.append(node.path_from(gen.path).replace('/', '\\')) + + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + uselib_incs = bld.env['INCLUDES_%s' % dep] + for uselib_inc in uselib_incs: + root = bld.root.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, uselib_inc]) + if pref == root: + node = bld.root.find_dir(uselib_inc) + if node: + inc = node.path_from(gen.path).replace('/', '\\') + includes.append(inc) + Logs.pprint('YELLOW', 'Added relative include: %s' % inc) + includes.append(uselib_inc) + return includes + + def get_compiler_defines(self, gen): + defines = self.get_genlist(gen, 'defines') + gen.bld.env.DEFINES + if 'win32' in sys.platform: + defines = [d.replace('"', '\\"') for d in defines] + defines = ';'.join(defines) + return defines + + def get_link_options(self, bld, gen): + flags = getattr(gen, 'linkflags', []) + bld.env.LINKFLAGS + if self.language == MsDev.CXX: + if self.type == MsDev.SHLIB: + flags.extend(bld.env.LINKFLAGS_cxxshlib) + else: + flags.extend(bld.env.LINKFLAGS_cshlib) + return list(set(flags)) + + def get_link_libs(self, bld, gen): + libs = Utils.to_list(getattr(gen, 'lib', [])) + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + try: + tgen = bld.get_tgen_by_name(dep) + except Errors.WafError: + uselib_libs = bld.env['LIB_%s' % dep] + for uselib_lib in uselib_libs: + libs.append(uselib_lib) + pass + else: + if self.type == MsDev.STLIB: + libs.append(dep) + return libs + + def get_link_paths(self, bld, gen): + dirs = [] + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + try: + tgen = bld.get_tgen_by_name(dep) + except Errors.WafError: + uselib_paths = bld.env['LIBPATH_%s' % dep] + for uselib_path in uselib_paths: + root = bld.root.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, uselib_path]) + if pref == root: + node = bld.root.find_dir(uselib_path) + if node: + libpath = node.path_from(gen.path).replace('/', '\\') + dirs.append(libpath) + Logs.pprint('YELLOW', 'Added relative library path: %s' % libpath) + dirs.append(uselib_path) + pass + else: + if self.type in (MsDev.STLIB, MsDev.SHLIB): + directory = '%s\\msdev' % tgen.path.get_bld().path_from(gen.path) + if directory not in dirs: + dirs.append(directory.replace('/', '\\')) + elif self.type in (MsDev.PROGRAM): + for directory in tgen.lib_paths: + if directory not in dirs: + dirs.append(directory.replace('/', '\\')) + return dirs + + def get_include_files(self, bld, gen): + includes = [] + for include in self.get_genlist(gen, 'includes'): + node = gen.path.find_dir(include) + if node: + for header in node.ant_glob('*.h*'): + includes.append(header.path_from(gen.path).replace('/', '\\')) + + for include in bld.env['INCLUDES']: + root = bld.path.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, include]) + if pref == root: + node = bld.root.find_dir(include) + if node: + for header in node.ant_glob('*.h*'): + includes.append(header.path_from(gen.path).replace('/', '\\')) + + return includes + + +MSDEV_PROJECT = \ +''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' + +MSDEV_SOLUTION = [ +'Microsoft Visual Studio Solution File, Format Version 8.00\n', +'# Visual Studio 2005\n', +'Global\n', + 'GlobalSection(SolutionConfigurationPlatforms) = preSolution\n', + 'Debug|Win32 = Debug|Win32\n', + 'EndGlobalSection\n', + 'GlobalSection(ProjectConfigurationPlatforms) = postSolution\n', + 'EndGlobalSection\n', + 'GlobalSection(SolutionProperties) = preSolution\n', + 'HideSolutionNode = FALSE\n', + 'EndGlobalSection\n', +'EndGlobal\n', +'\n'] diff --git a/scripts/waflib/reconfigure.py b/scripts/waflib/reconfigure.py new file mode 100644 index 0000000..333e64a --- /dev/null +++ b/scripts/waflib/reconfigure.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2019 mittorn + +''' +Reconfigure + +Store/load configuration user input + +Usage: + def options(opt): + opt.load('reconfigure') + + def configure(conf): + conf.load('reconfigure') + + ./waf configure --reconfigure +''' + +from waflib import Configure, Logs, Options, Utils, ConfigSet +import os + +import optparse +STORE_PATH = 'build/configuration.py' + +def options(opt): + opt.add_option('--rebuild-cache', dest='rebuild_cache', default=False, action='store_true', help='load previous configuration') + opt.add_option('--reconfigure', dest='reconfigure', default=False, action='store_true', help='load and update configuration') + +def configure(conf): + store_data = ConfigSet.ConfigSet() + options = vars(conf.options) + environ = conf.environ + if conf.options.reconfigure or conf.options.rebuild_cache: + store_data.load(STORE_PATH) + if conf.options.reconfigure: + for o in options: + if options[o]: store_data['OPTIONS'][o] = options[o] + store_data['ENVIRON'].update(environ) + store_data.store(STORE_PATH) + conf.environ = store_data['ENVIRON'] + conf.options = optparse.Values(store_data['OPTIONS']) + else: + store_data['OPTIONS'] = vars(conf.options) + store_data['ENVIRON'] = conf.environ + store_data.store(STORE_PATH) + \ No newline at end of file diff --git a/scripts/waflib/reconfigure.pyc b/scripts/waflib/reconfigure.pyc new file mode 100644 index 0000000..99974ec Binary files /dev/null and b/scripts/waflib/reconfigure.pyc differ diff --git a/scripts/waflib/xcompile.py b/scripts/waflib/xcompile.py new file mode 100644 index 0000000..c30ec2b --- /dev/null +++ b/scripts/waflib/xcompile.py @@ -0,0 +1,301 @@ +# 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 +from waflib import Logs +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' + +# This class does support ONLY r10e and r19c NDK +class Android: + ctx = None # waf context + arch = None + toolchain = None + api = None + toolchain_path = None + ndk_home = None + ndk_rev = 0 + is_hardfloat = False + clang = False + + def __init__(self, ctx, arch, toolchain, api): + self.ctx = ctx + for i in ['ANDROID_NDK_HOME', 'ANDROID_NDK']: + self.ndk_home = os.getenv(i) + if self.ndk_home != None: + break + + if not self.ndk_home: + conf.fatal('Set ANDROID_NDK_HOME environment variable pointing to the root of Android NDK!') + + # TODO: this were added at some point of NDK development + # but I don't know at which version + # r10e don't have it + source_prop = os.path.join(self.ndk_home, 'source.properties') + if os.path.exists(source_prop): + with open(source_prop) as ndk_props_file: + for line in ndk_props_file.readlines(): + tokens = line.split('=') + trimed_tokens = [token.strip() for token in tokens] + + if 'Pkg.Revision' in trimed_tokens: + self.ndk_rev = int(trimed_tokens[1].split('.')[0]) + else: + self.ndk_rev = 10 + + if self.ndk_rev not in [10, 19]: + ctx.fatal('Unknown NDK revision: {}'.format(self.ndk_rev)) + + self.arch = arch + if self.arch == 'armeabi-v7a-hard': + if self.ndk_rev <= 10: + self.arch = 'armeabi-v7a' # Only armeabi-v7a have hard float ABI + self.is_hardfloat = True + else: + raise Exception('NDK does not support hardfloat ABI') + + self.toolchain = toolchain + + if self.ndk_rev >= 19 or 'clang' in self.toolchain: + self.clang = True + + if self.is_arm64() or self.is_amd64() and self.api < 21: + Logs.warn('API level for 64-bit target automatically was set to 21') + self.api = 21 + elif self.ndk_rev >= 19 and self.api < 16: + Logs.warn('API level automatically was set to 16 due to NDK support') + self.api = 16 + else: self.api = api + self.toolchain_path = self.gen_toolchain_path() + + # 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): + ''' + Checks if selected architecture is **32-bit** ARM + ''' + return self.arch.startswith('armeabi') + + def is_x86(self): + ''' + Checks if selected architecture is **32-bit** or **64-bit** x86 + ''' + return self.arch == 'x86' + + def is_amd64(self): + ''' + Checks if selected architecture is **64-bit** x86 + ''' + return self.arch == 'x86_64' + + def is_arm64(self): + ''' + Checks if selected architecture is AArch64 + ''' + return self.arch == 'aarch64' + + def is_clang(self): + ''' + Checks if selected toolchain is Clang (TODO) + ''' + return self.clang + + def is_hardfp(self): + return self.is_hardfloat + + def gen_toolchain_path(self): + path = 'toolchains' + + if sys.platform.startswith('linux'): + toolchain_host = 'linux' + elif sys.platform.startswith('darwin'): + toolchain_host = 'darwin' + elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): + toolchain_host = 'windows' + else: raise Exception('Unsupported by NDK host platform') + + toolchain_host += '-' + + # Assuming we are building on x86 + if sys.maxsize > 2**32: + toolchain_host += 'x86_64' + else: toolchain_host += 'x86' + + if self.is_clang(): + if self.ndk_rev < 19: + raise Exception('Clang is not supported for this NDK') + + toolchain_folder = 'llvm' + + if self.is_x86(): + triplet = 'i686-linux-android{}-'.format(self.api) + elif self.is_arm(): + triplet = 'armv7a-linux-androideabi{}-'.format(self.api) + else: + triplet = self.arch + '-linux-android{}-'.format(self.api) + else: + if self.is_x86() or self.is_amd64(): + toolchain_folder = self.arch + '-' + self.toolchain + elif self.is_arm(): + toolchain_folder = 'arm-linux-androideabi-' + self.toolchain + else: + toolchain_folder = self.arch + '-linux-android-' + self.toolchain + + if self.is_x86(): + triplet = 'i686-linux-android-' + elif self.is_arm(): + triplet = 'arm-linux-androideabi-' + else: + triplet = self.arch + '-linux-android-' + + return os.path.join(path, toolchain_folder, 'prebuilt', toolchain_host, 'bin', triplet) + + def cc(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + ('clang' if self.is_clang() else 'gcc'))) + + def cxx(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + ('clang++' if self.is_clang() else 'g++'))) + + def system_stl(self): + # TODO: proper STL support + return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) + + def sysroot(self): + if self.ndk_rev >= 19: + return os.path.abspath(os.path.join(self.ndk_home, 'sysroot')) + else: + arch = self.arch + if self.is_arm(): + arch = 'arm' + elif self.is_arm64(): + arch = 'arm64' + path = 'platforms/android-{}/arch-{}'.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', '-D__ANDROID__'] + cflags += ['-I{0}'.format(self.system_stl())] + if self.is_arm(): + if self.arch == 'armeabi-v7a': + # ARMv7 support + cflags += ['-mthumb', '-mfpu=neon', '-mcpu=cortex-a9', '-DHAVE_EFFICIENT_UNALIGNED_ACCESS', '-DVECTORIZE_SINCOS'] + if not self.is_clang(): + cflags += [ '-mvectorize-with-neon-quad' ] + if self.is_hardfloat: + cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mhard-float', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK'] + else: + cflags += ['-mfloat-abi=softfp'] + else: + # ARMv5 support + cflags += ['-march=armv5te', '-mtune=xscale', '-msoft-float'] + elif self.is_x86(): + cflags += ['-mtune=atom', '-march=atom', '-mssse3', '-mfpmath=sse', '-DVECTORIZE_SINCOS', '-DHAVE_EFFICIENT_UNALIGNED_ACCESS'] + return cflags + + # they go before object list + def linkflags(self): + linkflags = ['--sysroot={0}'.format(self.sysroot())] + return linkflags + + def ldflags(self): + ldflags = ['-lgcc', '-no-canonical-prefixes'] + if self.is_arm(): + if self.arch == 'armeabi-v7a': + ldflags += ['-march=armv7-a', '-Wl,--fix-cortex-a8', '-mthumb'] + if self.is_hardfloat: + ldflags += ['-Wl,--no-warn-mismatch', '-lm_hard'] + else: + ldflags += ['-march=armv5te'] + return ldflags + +def options(opt): + android = opt.add_option_group('Android options') + android.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, + help='enable building for android, format: --android=,,, example: --android=armeabi-v7a-hard,4.9,9') + +def configure(conf): + if conf.options.ANDROID_OPTS: + values = conf.options.ANDROID_OPTS.split(',') + if len(values) != 3: + conf.fatal('Invalid --android paramater value!') + + valid_archs = ['x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'armeabi-v7a-hard', 'aarch64', 'mipsel', 'mips64el'] + + if values[0] not in valid_archs: + conf.fatal('Unknown arch: {}. Supported: {}'.format(values[0], ', '.join(valid_archs))) + + android = Android(conf, values[0], values[1], int(values[2])) + setattr(conf, 'android', android) + conf.environ['CC'] = android.cc() + conf.environ['CXX'] = android.cxx() + conf.env.CFLAGS += android.cflags() + conf.env.CXXFLAGS += android.cflags() + conf.env.LINKFLAGS += android.linkflags() + conf.env.LDFLAGS += android.ldflags() + + conf.env.HAVE_M = True + if android.is_hardfp(): + conf.env.LIB_M = ['m_hard'] + else: conf.env.LIB_M = ['m'] + + conf.env.PREFIX = '/lib/{}'.format(android.arch) + + conf.msg('Selected Android NDK', '{}, version: {}'.format(android.ndk_home, android.ndk_rev)) + # no need to print C/C++ compiler, as it would be printed by compiler_c/cxx + conf.msg('... C/C++ flags', ' '.join(android.cflags()).replace(android.ndk_home, '$NDK')) + conf.msg('... link flags', ' '.join(android.linkflags()).replace(android.ndk_home, '$NDK')) + conf.msg('... ld flags', ' '.join(android.ldflags()).replace(android.ndk_home, '$NDK')) + + # conf.env.ANDROID_OPTS = android + conf.env.DEST_OS2 = 'android' +# else: +# conf.load('compiler_c compiler_cxx') # Use host compiler :) + +def post_compiler_cxx_configure(conf): + if conf.options.ANDROID_OPTS: + if conf.android.ndk_rev >= 19: + conf.env.CXXFLAGS_cxxshlib += ['-static-libstdc++'] + conf.env.LDFLAGS_cxxshlib += ['-static-libstdc++'] + return + +def post_compiler_c_configure(conf): + return + +from waflib.Tools import compiler_cxx, compiler_c + +compiler_cxx_configure = getattr(compiler_cxx, 'configure') +compiler_c_configure = getattr(compiler_c, 'configure') + +def patch_compiler_cxx_configure(conf): + compiler_cxx_configure(conf) + post_compiler_cxx_configure(conf) + +def patch_compiler_c_configure(conf): + compiler_c_configure(conf) + post_compiler_c_configure(conf) + +setattr(compiler_cxx, 'configure', patch_compiler_cxx_configure) +setattr(compiler_c, 'configure', patch_compiler_c_configure) diff --git a/scripts/waflib/xcompile.pyc b/scripts/waflib/xcompile.pyc new file mode 100644 index 0000000..ffb3af9 Binary files /dev/null and b/scripts/waflib/xcompile.pyc differ diff --git a/waf b/waf new file mode 100755 index 0000000..7066d9f --- /dev/null +++ b/waf @@ -0,0 +1,170 @@ +#!/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.15" +REVISION="ff6573b86ad5ff5d449c8852ad58b8bc" +GIT="02c9f814da0d3f3cd4c1a4f9c5e0c7ed7129bb19" +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__': + + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SYRTu 000(bw#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$}:S}pr7b4CvӵԺ67RD׵]dXϫ wsv䡥%ނ-b1R|VO]EYxr{ wi=Uvi@F:}:#$t'`((m(:0#/*vEJQ$#$=@*^{=op%Ɍ{n)JuJmWshw}Z=ZqRvm4}fӓ;)Ͻv}U66ARB`zr{̺7w{]\$w9ĥZwyø4#$J*OCPO%4unI>wzn@dm.u61lQx#$^P;x3w{}u_2`>hQkOxjkGD^Zn{mz;&8٧[e>Eh/kx2L…=K=o)zΰOfmc4ٺ>r5VO^rw.սޯn/`\^w]}\%Rг0d6ldٻtz.36P81+ؼkfGB۶#$ jڠ#$.󲋎{e۽Μlwa9H΂y̻e+}nTfDAuv<۾|w{{ۮ2Zܰ5լ@'=w书ݽݼw;Hm{[ŧǹN7k砅j[[(z/wexr0V6Uq:^kfﶧ>}#$##,USNӻn\T2ӫ|Ͱvf/{}ϻsnlR({yzoZ:0,}}o @hhR{3p#$UǕy$l4#/Kݸۅݜ n%Um7vwPU0ػiɪN#/>oL^;=MvNBczޞzrw,u3BM^,2ٍ.e) IlK] cH .2,M#,0 k{,?nᯛۙ4Yt\ɯ&vPR(b>[;LVG؊)',9,Ɠl hlO5Rg?rcG G H!08q%F[1< HuMWhc ,AZdcnsES+tO;cb}w_)Ѣ,}ʯط"_"!v7K=?kiĦݸn#$(5=VFm#1pJ.kؼ޽^D Y*rƢT^ǭd#/FZT-Ől|Ϻ14AC x޺/)%EQ:ԖP̠#l՟IV~˳nɱ_Wu_ZI\$ZA`նKi(bCiZ‰GE2R]U"wլUjxiKe99eeQن81> (ݢ4VԥD_څi͖䁥Ҋ©T{8:n89lX ˽2qPgzrN5C0MT,3( bfF߳}}pRi#v/P}#DžǖtÊ~#,-6L֓:^KsY.F֏=QFvGczU2>[҄p"G>}n+jqJ]\t荎Gɧ֓DezQLP53J:tFuvC#)'qE;y&?,31O}aNz<'k9H#>)faRwlN@)WtWFnMB88hשۺ1gY mt4kyk֛1d$DX|yO^T=.bC1J4/5OTG/Ho߬eyiU(/.NP:cc%xr@#,w9V;kz&׮,cȶ_/4;íjvײϳ!*n[7Љ*U!VTօJ+]zez{#,~- UKTW&|_i{RWWKb"V#AET|'ۛJ V=,5ٕ[F(/rHͭ}poU5F,UMD#,q=3yyZֳKY12HiB|p데`75>YslD%/Scmdvp3$s=d.|d[Κwt5`r`QHL ߳7:)x1H0W~#,ZTҩьtuO~y$_抠s߾4?-:GBF/_A#,f#,X)?)q6NfAu]!P 2 TSe1987ðii5,hzڤx3[=;6䗶,/Z-%x^DES#/J m쪤<|uN+E3RJ{4}'k2C<"Tm쪦Hh5Vm g|K5x2#,ҡ@cFH@R-9rlfx#/h6DaKR7gFTaes^o#,b):2ɻ#T/\5~$oElC㉾=UD"gPo2*mg}薉Cwγk@RɬE['zw\w2;UإcO8>UpCؼD#/s >~x5 #,ILjW. VYPzqcm\p"Z[TpCNJAQ8dr*?WdB;b8[v>wNۭb6ǰk`a163z.9IO;#y#B U#${~ m#,qm|3duA}D]疹ͻK(csد*ʝOys.J1jf^E"+m33zYb&"l*v@l7XhkSq]sVTB8@uޯ-6h]d?&iu|Lh|FPFGy$˞L'Aie6GTTgJɒ[(S-.6-s0.0.( HHDyHM.^|nkەeȿ#,n|6Y9t!Le!tPvlt5/V%'D["4`yl_=4suV,anʐgOBu@n(#{7)ѹL x6,680[ש"L|Cbc?- +]Zc~~Pd@(GW+*ti?6s! 4Q:y7F$ݧNcnA0 hh;;xVpX#/} oGMc*9i}%|9٥K>?Ȍ\tTE$Sd<]y|"IӬD^hn9Q{:}D?#/!WK=:o_e{&GMD3y29&UXHNGnk`㋥z$6#,w@e\C1DD?$܍bc}w33!Do%$3\"ML\"F#$p<LhW@yPi.naE4P[k kJ?)Ѻbq L#/XD*^QD3A|˝3T - Y٠1ysʰXހ\#Ku{cJ2~yAN`L| oƛ5a3a{|/X: Pհ"EW@\#^E]5Hs9Ӭ}/!`mSAQٻAã?)Cf\ݲj8|w[}A߿pޝ$5AM".#$怳sܡȏyfQk_Ux8/+"` {G_jbPLI$#,#,c;B*ogS}2ՋV 8)?޼/@*Aݩ- `c,J!z:]WG~ϟ3/3i.\6 3dr>׵M7 )#)W4:,P]ۖ93O~6Fp|83#$bT(#,KIA0&JlP^ڲLMmhc?| f]>.>ѥKfxQ&'TQAٜؔt퀱ZJYOiĞi6,##,>]>z崎P@۩9X aah%zo4ܺƪ(NKoa\'ם\"#,3V?y%HcHM?miR:7{rk#,q!d>i#/k៦vxGfiX#$s ^'ߧC6S?47L@jacllfH-LwwZx"4stRA@X-kO6֫WrP F2E *0iDklq210b#$:N rHeޡbIM) 7OFNOI:,v;iv| VvOZ#,l  \m_mnnԼMQ!.ioǞh`PPE8U&B)"d`P #/'xy#,#/S  ;,)|/)eC&u䏇Hc[}$9ڨްkKf;O#$w t>>ac2fjcVf<l斍6Œ,h!xQK4&r\ff)hnilCZ6mԱ4 cQ#,D4:h4 0-Gd@#$奉c6P@ "l^~wS/1mtr-[sF*OW-_q^{ېcL1bh &Q4d "XTMPÄVǨꥍ 'XUd""ck:{9m pjg)~lDӡG524DSu#M;2@ 6Aҷ}llAD=8+_#/vIWLDz#$9;'JI%]-~V1N#,Ͽg/!kTAEHY-;$m%۱1,oط;0!ưuyQZ(b>6(=b-z@ysI|Ѻ"RDTȍT?erJFV蜑s[0J6Ȇ(q7"#,tjbaT0Y% 2h:6R(H y*I;MH+Jv#/)$դo.؅)ڊ"0ѥbQѸ&b̴:ۃC@F5j#&Ć!L .7{?O0BH6OKD#,c!14LϷ::V1w.#/W<ٞ#ɶnqXP6k_KERiDxrӑL4Dx2Oc;Gz ;Yu!`XἻ`>xs p#f z܈ע##,]Ckޣz)ac#$TyEc Xw$R@9aC{ŧBL O2HLّvB6l%?v~+g@9)J(m#$(#/g%9RqT邑"uQh4#/spcd\PZ1/Gؼe#$c!HaC?ɕi"1E#/̈A}xUo.va!{eQ:' ֧C#/7r߀TD$/,W?øUOzx8vXCӭoef'hK:(6bZJU1&t3XAdXb%2Mh鶚bZ,B02;Kd;*t;yHM(dfmj$jÍMa룧VS#/9Poo¸{*Ķ7 s ϑsoG<00RJ5th,KnߧGGZ 6r=ڏYݾA/<9oix:c>~jյecI#,AubGnaK$3?--[+)=u3g!'.lw{6"a*l hҺ%Ts*`Af tq}_;gC0iɍGsyU)uۯ%m b?g#$C0j5&&j$#/IGmЮ9>Txa'~ay)ȎOTXT(#=Nւ)RY4I RĢN3F~vݨX5RhT ITKg_umׇgC^<"޿>)@P,'Ph1(p.elE) )%*A#,#,=$]VGq_KQ#/<_>(T(` \oXdQPկ8d]@K`ߋ|2~oSczKBm·}M&s*JOh!0V()#$7hV)9thq{;_~'^!WԾnI=hc+`?MK[;:n6Xoin η,wk|~fHݠv#,5tH^1jIU@4};.H-Q;`uzJ_ۉVB56w_@/ E12vۉw>?} E=.6q>iwׅ!U`1"to"G#Qbr#>)3X 54(4}?&R<ϟ9#,5lo߀5'k,HZS)BES9;륾'Ѐyfn:aΔ&STTze0m$(pRqSi_ǵD{*#,y! 4W,W'aOaߡDű\NԝzE74P~Oׄ4M܏ y_'yԒb~i4 CcʼBUW%3)c< R`G_C;_ 0g皙7CUR(St C 7P HQUGN|Ooӯol~34mJPq0XPB5,ވsR\!HG"y}ydZ(!z]]W}fpUAt{]2ԛ>+Ǐ~X+n1~w.Zwe/fYWf߻Xݳ-Z"Z9|{|(f~CۢiMZ:ݫnџ/e͞8eE2wgתzy-E<NOg~Dr $~^ǚezo9)BZd9ǣCue;7Ŵcs'U',mW;٫]ç$?5UzNmt_A;&x#wF.wKWZ|chZ=7mkuۂ)HkKͧ6i5#$ۤѯOx?b.T1Tx'qqm^y;tAzJW_I١PàVN=+#,ii]:$Hq%b$h_BOa[9)>Qw e^G0gN#É)gdFcoQUW~6W}:_-a%oj%}Wd0?w wiGo_q@ehƘ}rpunC彽äG*}inQJN|W[O Yv'!,K?-:8BKMݘv45I/|8~ph? ^8(t٦K[Gѯa7LJ,]3\$'8*,>UB8aS@#0<us~nݣend)uBӮT|_N^vɥog,?p%{P5G|:^R[739 i_7d?/@(#$LRuw3_٫AzO_[-}>`۱;Qp"TNZVͪA B'/Խ;#,=v8#ϯa}>uFo(pmgZDAkSfzbg>~)nin`N7Gۛ٪=pqҟ8څ|(5|z9n~}jw'#$UhY_+SHlg!PDDz&iP$7Bqa4=]]Xn.=G3|d~坴M}78YBA?#,gaǤQ=<ۥln)W/#/]a'JNdəl7}\CdB>I'y'yGL4w+7x;RI۵>;F/ulu| ˣzN7h x#$#,8Mz*I+ϐi{*?U)Ϙȏ/TA쒞Q}ced#$?4v^ U3QG,?Z̹[r9G?7F50Ҩ6ةA EJ$ݱ!n,$Ud.?ͥY #/]ܦ3"{pcv)lv5Ec#,2J`B#,YCCt!IՊ"ca=j0XIFOaFf#$fUI1KBzZ8dueD&s΂PP}?~+S<% fZ9+r c F6,# Do?~F##E4D dҁBoPc ذJLAbm6-$HPYt~} o/͍2)M vˤ{#N4 -glLj]uu]t_ٰK=*~c:9'v_۝V) lw~I\ʶUdvV{#\3 hp ܂$:ˮg?%u{wKPp#,@&ߗӫ) 8o8/ "NWs=9 - <6aSZwz5;uwi"n?O|ŲY0/-!Gv<3knBmZ$V҆AX)%<#, cT8(2[IDdE+kaRCtTQj#,@RBdQZa++)YlF#UZڌZʅ؄`$Mx3#/F5ېMȠ`B#,F&9t)lcQ#,dL00TZCӸ貖3HZ7KkA#$ek.X#,<D:7YhlbbeCN"GDtd֖#/T"Pgj*®ʌp#R1lb%W<!U$IsJCCI4&A/^a/β߼Ԋ#/#/[h S'? t"_Z¿x#/a٭)ozӣ}ڟ)30X~rtB&Ȉӣ̽sN-]~[~=.k,9}No#*@W s #/wA#$_${ۯjGCv*8W.eҠIfUUk{T~h$whM 9,Hbzxw G?W\@t/7Tnurڈ$P(?u5!AhcŘd/2p#/ #/?:9~qBߎtzxGCPx>#,<ڽWH'@pZ3=@>XDeAbXҪ4YԎqqJ1J>i`.H)FD8XoIT4IwjGzt(qPGcE{/F4RhD%j64(F6UBn3D:TґIcDCտ޻|GO|zEj ~?oEJ|A|fϺM[1@fҠϊ{XW˓i9БZa A ouQ]VAR7x26<6ӈ!#/^ BL8PVg&CŔ93ĻG#/kL3H/! #$PDJ)(s.`v[ȿc!%M\G#,P|;Y$CFP L4' dDMXsTuZWɚ2GZԌdmw:Cȷج#,B#Ȥ$cd%Rj~g~nzebBt"2 ^&AӋ$'wġ/4}Myi4e1lsn,-.$C,ru6#ԋ#,Kq띾h.܅9"$nCj qzZ$-Ld"mZGp.gh#i^x{5lfHK҃9i6!b>UA5W[m&}ƜسVbǴś/m2BaIv7:^V1ifgd"3](؝He0V $D?qCIgڈV8mbpӝ0#,)܎4om!& &HHc,|k\-wj]CKpGYfIiH\;3cw]'u om0Cn*6-d۶ҹQ[#,<#/:26#,^W\m;QuvwDyd*78(6]qq5#,k[r+V;B'F)9;6TZf0LjӦNqpȬ2օM#7';d%P#,]H'mjM00y_gcbkˠՁo:٦ K4]HzޱII*JCK$x;M9`kz:  rqB17מm[^J׸ȓȌlI'V>Y?S#/kn뚞x]x٨[&'&uuRM|an!ԥAlK;Ɩ)FP6,-e4:R(#,TVD `Mߛ?mӣJϥIhioXcOtXkw ~a7ia\}:Ki!>rlqD>g<FAM,s A5IYG8A1&rn$M]p4ίEy Ͳ1i+~K[sE>iP܈x]#/aF.Li@Ν-2%&`;]SBtb0TXuwWn!7_c|a{= θ"^yyXVM`3ʄ댘Ntgn0Q0$-FtT?צv=).֔3E3HdQ* ķgW鉆Ԟ_/&!EU_](·ZW(݋3ɰ>cٯ#$σ-x 1$I2އ&wTR.ʃ0FB뙌012ϧK2/X,d ̒8O-0NײAA``#$,;#!z[x0nƂĘRVi@9`%æH+; ×#qI@Oo9yos&Bq^L}Tus/K?L,rm8=InmOlRPlg[%^pI;?X!2f*)ٔ?#,<#,JJdr iU%6L[2;BZ#/9b]83cK>&I,\ț|jg}'H[*cwjR!S&'DG\'caOnt8,^9dVy2XnmtGIRN4ef6nŚ(>BޚNќ(rtq/v2A"H92I0.:"7}ӼN t>Hhp2vBT)@ԙɣ>:ݩn9/FޤnB|L_APSO.5)ϲlV-^:v$N;1(2#, hriA1F3[|Lc+YQ_#`#,9YPEl͍<|C;Y⺞ ʐmj致I,-E+#/SNԧ~P;]VK蝳P;!VhuZI[ogFiY#/䎋_+ktC.y3e:/Y]#,tXɈp)>^fļޅ3t}vOow=D0 ȃ՚5]彑ǎj߶)yoυ nDt2un6c+j/ M}9>y[PoQ"4#,{K7ahJܫ,h\-!4#/;/繟ͦsTfW#//9=jѴSeEB#$X9hch(GGc~N2 \lzQjJ͡ >zÛ ʶ]`y?%ad7X]~p6 Lqi1d2r&yGEVfcⒻ8V85לfQVKeq {u{ÜfՄWRn%mvW`neB{ =BKʽs.\LrN ^`h1K&:r5bhZ=#/g#/%2,iq'M~^'`⩓O8x2, AU`370`s6V粎,qtsXKAed9)ZjXlݑ([Xnܟg\6x6@N8M0ժ"I,vdcZ%U^7NL#*K%+Y9},} ax«az6D7m-TPHLUC巷+]ij=nusXAH- ߫pQnsY̱ NdGNr:N1})W38z%~rwĢ3יL?/,H]lZKMeϔlD4S\g?g˙eSֳ{R:bm!4}oV}uM{UǔjAiz*uEo#/l78N$\4_BQSbKTs5˙(H\7Q1QWާws}2os u<1\ o12qf3u-IDl7ѠrAISCh\XKSRa%.&<'k+0?OcVq1CͥtXB"ICgwjrBWzm^8D#b->ZL_=c /P(P9]ʠ2$@h9"1L)~ @+b^@^2].\}%cS\i%qw]Z]Y!#/U qCtkQ,7J 5g>6((tvB'잌Lr:oylM\M_BMʊ%,J8tNX#/Sz3fYR4V|oy ]xfM9%\U{GC-9D:"-pbC-,psx0l5fKew^bn6}3SnQm w[c<ʘt:kg9m.Ow7ڎh*fEKQ=h.w6to~14=6I8}'snn1Lg*t|cW#/f%-9+y;;􋕟4^JMgݻ\=[/ոFq5Z:ad*uۿE<36$tcQ\\?򔟾*QU#bR-IMVѶ3 N$KDB^tyLfɯʇGc8v:Y#"X(1FMS2]:r:L'L?N|[9vZ?jy20Gn.F!QQx][3ku_7pFrOaod?X:)걸R&ėwJSbiK88#$?o70-?~ve,-=$>xɁđ?WTUM ,OL#,rt,;A)[KN@ *2(Fi$ตhAlK:ts~e}( c;P9=F&^.`ꈇ2P7hC%9<4%؀j#/#/dY#,~ʜ膨SB{)LW@@U@t2j 'U(сgdS,ҰŔu$ɄA5'1(,ΔvK.c#$yjbsa?|?͝}cn~V:B{ A&~Jw=aGZ$p4#cKҚ㓚=(Wn:V,VUv#,TY@J, y)\TۿoL|,輾7,::GO[?Dp;F-P΀|6%2(/Ic잸Zwyy#o2P!D7C=в߮լ>I=eJP(ZU5/wrj)k/wBò#$`1$ s۽X1I5=iS1kRA*u&l]ӭQudv4b@8E.;brˍٝ ט:`(#/ ;{#$.u;!]tnȿV6hj"ߍ(exLLqc nDDl#/PIpwLm{~"(v3e!2@T:ik~ oL.N7%LO~/Mvf0Rdmځ|7yOG3}&t|L\՜AESPI4J^m)@3P"ӊWW@U=Eϧ4Н셥^[?^]TqcÓyGwAy*q'#/A#?&Lݒ HM#\B{X~~qj0$Ϥ<%8M+U玧Z@YCrŅц`eR1)sMSgGw,-F~><@ynrC(ky!c7 )5c1bOsWCj) #$b @0 ~EW.ǬoM$D͍Mȷm<!98>Lfi#wGn=/MD\[[m~f'$7٢ɟ6nX }<Р scɆؖEH5  E*ᅯ~4?.e:1}مȒZk1螟7ʇN/|dRRZ"B_ЊrO&W?Xs\wvє1#WgHA^(̥~Um싇  `(Pu!<ܑv_XVuܥV5*^Erۛ|ܢHi7u'g f{7 ~G#$X'ᒙDҖ VB~**Oxq"y`QqNNHw*`&yV ;s!!QU{gelJ[=oam{ڐElԔ bēXoR^vpO_w);ES9z1hLd~wo(ZgzKn.]\F.PwJBpc={qq%~~Dby8H2 Qw=8EvA,RvDs=|OgdF$ǭ> 0$ݾTdP[Ef4`pqCiL-#$Sv FT(TxM4ޭbݶ/h!2;oto)Wц!iO #N*_֙4L r&xX,u=uxArc_AgW>c^HM%Tա*В?P)).#A zxZ: ťTZ5*݅(VW~;jgPˮ/UQd#+OX }U=ـ'd{z !E/P7@??@ p_7$O/R SBAW崤z@g*Ɨ\)Ib#/!x/^ go*CL.g߿S7!b|kDM-M0{saE@RBBUSD;N=mulJ"0o"c's~#, @Aǀ E.7 ԹN#,%#/bc@#$$9nPҀ?l*h βœʼnN蚾F$ ~@w7v=o#/(kzY#$=,p70$\6Cp!%0["DZ"; h9,DuDqoCI`#$ q-#,/0B&X8+`)$rF"0AȡK* a~.6JP#d(`EEa,a"XT#Q%HBl}Z"#/_!A,SЇU?_)fn 4N[b&x}g#$v*l%b'+e\Gp18OKI!+؆P 4Hq9:@[/kRxN)ܞY{;Q#$%665V|eY-lU[W6ɵ\m^&걈QHÕ A,U gE& dMW+:G9&B8T(=)`n^7x9HT3ߓxltHhWqQ B#QF "T&[Vu,mk@!T#Kmibo*(u㯿dNTE*^UɵO%!7\31=t:)E%B*͙ AL ӯt4˦9Ř%xZP!Pn&a#$ >~rDӟ #$iw1$hmy$#$[LՊu}`9T@EȤ B"P0s*Ι4KRE LYk!vf;ykz}Ujab6\v\etoč -A5@>`͎ve#/ Xaݳ]\Hw9w}UxѺcSK)PBӶ1#/_*ň/><8'zuTSC<<#/R?OͲ5N:9.#,ҊkwPSKF--% 6q1whObCGiA )!,mڢ~ P0@Mul*'|<ʄԬ\(:ÆzDG(#/\R#/RH u5׌ikU|y|[Ai! u[A`}WU /ӜO_ؽkVxLϤ0,16#$= cU:ٛB(Ҹ##,TomvxkK>U o*&L;.$3y~wbu 61 9Hg CA39J=U#/K(bCNJ#/,YpΤ[(`pHA${{Jng@*o"3ܔ #,cLBGƈP#/h@=ݯv _Do\c\GF&l.o6linD CD Kda3-UX#,ӯf] tJM/BC]郯i^=B#8q$ŷZez"<&i`4Dv5ʌ.5S!1C@w28ya b뉶sxKuyZVk[U(!RJ/zuYЋ-H&IݶM&L-zm}:lTU)big}ϡе!f NRh N9cXl{SFC\Q=[ ҕvIpe}Np {uΤ]"QzwJ"@#/wú#,?×dH F 7Y2bJpZRl ?0߫"B̡؀!*"Gd`t|QP>gJРVC&K[n>Yŕ^Y_a>{}<@@ceQK#/-n> ׆&0)x#4#6a}%C? p}=G/2~81q4=8Zbo#錖L6w^& >ʻ?-z0ǬAW18&R#/R"兦tw7✼wڲ۝Nt<)#,0t~o u^ǛoC!KG᳦U8XAW_'ML`RZXZɜEE; ܮ:.g=CvC~p?@)r->-#$Lb.65x ܙɉ)ZM2_>,)>>{v@POM#ELHLߟϟo~h֙5yғnmotX2y$t*Bv}-9>a=/nX#/IT~9#,<0]5'0zr2kp>La{';3-)H;F=ib Jn=xg?OGO]ҿ6)!uDߢnaj 0>dD \ #,{%KhC[i$-̐2t@ِxR^zqDY1W{۠w>OwF#,=Q!s=fAH:UFƎ/^eB}Aq).WQƺ]@>lG>|4`?]r.銻JF/*Dȋ\t R122x{1ہU]%EhI8 NZV@Ol}CwC>>mzsFxu|VVL0g:i">gBml}<-_^|y9y#,Ƣ>:YŶc R#$#$t@^D+wX_-~o"aOʂĄV7f|涿%]aW8+~Sd*?}#,Nd*TH9rCQP^дģR!,OG:n7WU7\( .#$2&_\|~91Z#/BAE7)ưeqYhl*BIpĄ~g_+oէ#$0 KZi]}=fbS0ҘKUȻ}(UHVEB'{@ߣ;8!d>tjɞ4t2K՚uUJ)½Nx.ߏϬtpq3 ̙4;o#VV#?vfh=߯yc}`aa`#hz'2.}Z=yǯw_T?P^0g#$i#,#eb۞ƃB3{a>韲Ԝ%2?˷̚5ÐG8u k/3Ee٘>i G`Ȉt5cUh^ݲ#$01#$7mw+a=02 eQ[@36] n:,dp_dMZP(0#$b[jCGS#`A}H\P GJH o|l3Ҋz/l:C]vx̱xn%8 sz$CoU7siT"tK~p}0'Rڬ*xKuXIN>MKt:SMUFAW*oأB>б C0̠7w菙\3ヰy+0MNJ| #,<;*ZqA]l~S#/Szvu<Ϭ靴VrsYQ~q.-"5zλ\Ꞡǔ3N=} 2qvt03M9d#$Hȷ(xbq!(%Uw{_Saϖϲ`Zo!V#$a;}u#/VOaTX'?QLG}<0!=ިEI:)ƭS9Q> ῴÛӯ4pZE9BK<ڝGIFܽIlg;O[Kq6UN_D9/Tӳl{lĢ.fMI?S2"أ1maVm)luXp:GI'w]BF2qM|"]Ӻ-JOY%bc(iDRѨs:s bq]\:h(r'3yz唵t|h~EMIڱq8\{S-v|&J @\Z&=QxޫM#Hq&6KOUj+1("67Pʆd.Up$ r-2_"~72K'o_v<urNrr?y/Y4Nq+}V F=E|"#,UGҬ۟Edݏ0EfCegufm}}pDe7tq~@96n?&*%Bh?Xvxd~-pUFEs8.)S={pHd?Et3]S|$|ߩ+g}cioY; 9+ǯ}l @Zp@@ɺ}dE~(^_g#,i?='p{`AmMG,Ap KcY{y`PsR+s8t%St;~eOM&xvVtbMRv̉RMT}q_!3HvOԈ5hd!^ zr;8m#܆ cqt)3c#,aU#/%;Kxs?`ٍoL0k=#,,cc~r/waigTN ÒsMJ督o?Ra~o9/b IAٮbZiwy1ɤ[=hfUwݍN{TXPD]*DCE4IIܞvOzcpUʏU@,#$ub9_ԎL3q~X}!ٽJNi0e(b<f"'g)!i=^(,$ bKR.&kwC)Ey(TdXXy~v.#zTS ]"@9h\m#$`OtEX)+#/C!/c!nGVt/#$Iʁ/-VK sN#/{#noM 'ПM]k6b ="Gp]CgbѠvGGH`# (0@$AΞ  uڶLU̼:G릂=OႷcrvxPw#,J^!ʀzrvKGRhBD"Bj+;uzCroO8,=lJ^,. #,JCϻ&<}aÃ2?#$Jǰe^'Z|~z>6;du!5ă^iu.7Dfk|z3sD5K'S#$q7&-#/SmLzP>:Q8w76NWU(sZO*,]TqX.SQ%x]}v|lסT5"²pHI97ü7"7F~(4g7nmW^JNgɘe;gTLq>#,㇄0?J 򇁸NEx1w25Uj .%`wD<QE;FbIB8nX޵@r]6#$3TJ#|U#,>IbYuc;rEPmS_v?L_"'(PwVR6$ZgWG^AEҿ}J#LzԯҨgCřpwwвj%9E ѭRn#,;7 ޱֺkX~O98XR[Tyd' էq־ˬdF&{q>z^Yx9m~m[ΧݦŕuYƏ"9gHX_LԟgZ$&'윇Ǖ#\յ+ӷiJtRYƀepy#U$F:`FU\x7=Z:p'I\fAh3E0\nKǒq tKD,ϕ>r ng ۝ߺM֡xMoLՏLɷl[<8Geq2@A4IXb74\#/Ru3ug\C~__0π(/tsu6E#,KrVLZ#,)Eg*m=g^ i[a\ӈ4Y\Z#$(N#,E8oPJb3;:ZTUt%&%Pb~d󄫢o:Y}LA#,|e;$QTD.IN$8|}gGF.#`MÒytۙzP0wNCXwᓨupd5a_#,##,$̔Y䦞\,*Y6]#,^X፞0iɱWI 7JGگK[q@ x.i4{W[dzuN"Ū,v+|XJbZj\{+i(s#n4=V*1 NaLQ:QA%9H30vy 5j^$2}u)-Gye0 4-b`N+bv^;Q/,vn[is$PT.}*Aȷ#^R% %kÝՄtUG?$y`lAd:#/_ Ӿ&;,h?F1]av).̀f,ύ閇jc5LNa"Nk32Y,)Pc!g(*$T Ȝz]o:/ʜ#/El\`!$\l#$"pfب#$<*&A@n~~~<8,[ l#/ ȷ"{vʊSǯ޼.ZJjY _$#/Ժ2ߟW5.Iz)xw#$^@?6y9rD4Q( Ŗvt;#/%lU4<$b|PDtݔ(8'Ej_a:?TZk; ?[S8f#$>Ϸrk 04;Vv8Q6~޽^K:Xk "~ѡp 1gc?ׁUU}Q|t#/YTJ0o_p|?\̓P P`fFFpۙj2Oa.P;R.lh 섃 #,&H'ųV~9j{{,o>]ۙaMKp]@ G!1֌/qTA5#$`N^ͩ=(#,7z HaӚ7uE@`K]U!!S |=N`ny{Ca?R>EA Ob;"X;`]'Xͅ\5#$?[mas>PQCP!9Zۙ'}u 6(nʺ]M߄ nuOUUqӞ Ś-As_S=Җ#OP)Wn86:VWw_rkmd#,T$h a~V{s.! LRRrAtK_"tO42 x+wO\ڨf!f K$H,#$?P3rk#/'ފq!b`ӘZbHbl@*CؾA05Z'@~nZu໎}A6-IukFPil} yX["K dc', aq. ޴Y zed E9;y$sV5ظpMIaW8C>Q~#$>]3o;޲GT%X{~wdM#, HccIJU#/j+0|HB`,)$"GO$8G3ShKFZA{Ɠmpg~Ҫ#$RN⠒J^7=f:Jg0:ԙ͵.Y8yELajvzL^֚֜tgx-$RUURVHN9^:K\?zw<B2 m:`,nw<fΙSn4[VwOe>~'Nfx#$3%,I#S8% Z`f#/1zU6(T<bB‡ 3# 8lqCOKMNC(KDf. kL[1@.rvE~yϫڱJ"H^3sC>Qik*,@#$h᭠w&&'qG1LȆb:!?䆿A CLSa?7 zG:Ct%#/Au(#,h#$SE8ΗeA05,>"ɐK#",#/jP#SpHM)@Lip`v$+1@TQ#/D[W v rCJMtk<J1. BaDp(.D?3}?Qun.`UqkW=xk.k]RoFJM3"1l,W#s%ޣpL+Ѭ֙!>ӒStnl+CEH$LF&*e֦Yk5 #u u:ߘ)߼: o-.B^CeK&怌QHKy-q-a]d>a؈ZjH M)QlЪVY$ rQI|ɒtn5вV $lC ÖRSYۢe{eQ>%M+՞|yj6#/|H߳GHE`}rc{S`067$r780;6)$"Fߎq)DQBH1U@w]}'iύ>:#,]qJҊ,,lb .}BۑL:"yE=ݯ5OȏB%#!51Cf<挷Dq d#/b{RYPP %(*&'kni]#,&~G8OA!h(q=b-?)@(4Pn#$Ǘ^xN6#$hv]/!̈YSʇ#@#$ɵ;J[vm 'T^^B)g@D#,V8XC4׃"! sCh;Scn-8>($#${z7r#/vR$jcؠQ~E {q gm5gP;_gaR֟nP {{BdB,Ce6>W/+'}OJu/UP@,#/ТM!w{{@ć)#,dOXqTS !V+\#$~Iqo2׏Q95~*4F+d=cs:5R'~bwWչaInxm`D]YU dh`]Ӑy@< '#$aZ.C.F%%  6^x<>%YUң%M5OC*g2Gׯ' hJeTN;;(q IdEQ*B:yvhp-7-%\kM|L*<4a"H,G@$Z̫F:!bR%?.'2>qbJgNgIfVOPuٻ|+l5vF[7dNUJ/s<}<j%fgf)_o#$h?*((WPjҠi鴁|1rF6J!*ZF<;'#/:r"y4B'[>%膶S#,bevGk=Prq?#ЮdSEQX% ũS8`Cu;)N81Aȣ:KJOM%]z%Ow|Ǹ`b0Pp%hm#/ϗ bݟC gas>ܹ^[ւ1.?B1*g2#$I,M¼082$ 4@RّMc& )1rAH1$#J*"9. #/H9!RugBdV2I ;$+#, e$t5O@~)۴LAGӨ,3 `#-7ڜ-kq#,#8T$5pMEL#7˼(5^6#, +-מeˮx׆_/?R#$7YO{OX%f dK^E,HS#fiY!ASW׷v3XM]U|%$h6lZwD9"Y ]wu8IG'3}^Tx@6ڟI8V,8sY! Q![GIː:ܳ'v[v6⋰7L-*c+egفuYY缒'7KK((~OS_xy{;ϪGTHV7~=u* M!hUf7L0ԅaB7Y%Y$t38yvkR 4jbA̩hx9I$!E!&UHQ 2 @5Rʄ7 X>WB{5#:1B#IW.q)cQGhIGZOHC/Wj!#\iO#$t~+@d(),g %ULJXE459y|'#,UA݅#$oPC"?gG3 ,1eAVs L)ʏT!_Ğڠy++.}l)2_Rpq'P&1\AAUR"}.>!]ݯLzi!qp0>vFEqsAc}Pqk܏"#$nW>ǐb*A#$u+%Ŋe*t _V-C֧PuXoz@ƧI%h?MpT#,N#$eIოt`x"H,X4ɣΦ\|*&' eU B&K7 .hMvb*VHxy@YH^` HB2~j3"x@Db!nEz7fm1qٺn]oA͸Htӧ7lE6=^AO4G$#,k.תR@`uͶl؁#$PXLE{Zeyy!!#D"vaCDY(}s!Ab_Y N>y@RzwGypLAy.d?ŽgE)]N<<bHڇxIM"vbcHXV;;K 0?Rtyo0]wKv2Ct s$/XvrPvSDGߑ"i†>Tsɫp @79NPŃYypq>%B#$| %#,{b@: JՏe\>v &OsԹ2~H=;{w8K١J!EC*#$=j*{zhV>ˏ66"_!Q3-vOv): V1@u~kht65H7?XP5_??|S=[ 4AX%"!HF#,3+H2.#$6dߺ%O.]@?uK2zmQXW/ݒ0%!L;u2<;6ݰNJaKd?"#/&Йhq9"T"#/+Aᩅq?fjB#,!S[1׈0~PZb@URcK`v\XS\fP#,DRH2m컕#$vF`z04:sx@hT?қoٛ $AfWXl^~)_jvQFH+"z12'CTR_ #}ܿӟoW: T%4S5ْhWWwO(s`O΃϶uSKP\VBZߛY XCf!(]<3ߤ1:wqhi"m$,Th($0y#2;䂾%QP(c02`xSfBJAX)x9$WOg-3ˡd% pNTӀW&Rf0x4z:(BlM,#/J#$h1@S{=NQG?7삏zn&FXݽ6hq#$!*9W#,99p+5;wBEGYgi@%?L-$(r ܢNJq}߭}57% ɮz 5 {}/\\_GHc4E56oQrKl\P EGrn8&9;nBn@'^D.~elQMU|woWyJJbh0Cj#/#hy'-zjn+HguG.,7$Q[)>BjǕo򏨻՗tNGt5憥pkX3b#MƇ@$v>W9=fꃬDe &0G+a/w9OTg٘zd}=|ysgD_N>xD@Z06Q-9by?Mѥn2 y#/@JvÄnj}Z̶M(?j-Qrw~M6;?@~-TwdE^vm;|Dh2޽ B!zoMA1"Z㙠Иt6Jp2O^iNqaHB8|q9"oZe$TA()~!ǝoq+#,IiZ~Ra>52}:E`/xs;hLN%"]b@6B ~ZCo;0Vðu͸.efB]vfkg޹:f"Q_gꃝ6c9vjhv18IՖ4u3#/5"瞽i9qQhHTܘ]VH/^&r6-ib:CO#,A : _5?gBY;(Oa,_YM ,,#$APWr8'ZaQ9ޞ~J_^v¥}u8DLAIk}?/~n |9Q[llQqL1#Fk]S@V5q?pbj#/vf]Qi )1B#:w^scЩTj1Ē_#[&unWx>S2f#$ĴvM㲷EW6bNá;8 ;֩R iU5fCS+fYp`/HAҩZU5fC{p#X7aݰ'gϝk!sC@,,he;.>V!9m)11;nM]MФ4ؒ\\MPsp*MTgW8I 84@ư B1AgM@;H'0|34ln>!b+QaiMzԽ۝TH#X3աXH 8myI5negkxGV,?Bys5r}W[0qcQfz,#,8 B@l!cJVDV4H65h.1LJ$#,D25?0lʺ[6mɑy_?Y#,~򱵟CMDuwO5?*I܌160Ȗ?J uw8Ij#/)5ޙkr5QCg`|23f^{ӊyw+#/Ocߎ?#,11ֳ^uu!D$ )~xHk~?ju=KP$1a2?|Uvvyp%P@U9ӝ>$&m2U)upZHŁD"S^!(("<;'!%-z+Q`>?$K@(k OxX`Nb9^I!5:EbUCP@;?PDwFą\WUQs=$$"9)>LԤCjb CaDD5, Ԭz}l-hF/<ՋL>.TN#/hRH k.m`èkdEM񕉍}r5Y|\9;ͅIY9du# 2eOy#/#$ ״7p_kr=蕃ͼ@mN:ôE@]2C#$V H{:L^J ȡPI#$ BG;HA"") H4@1"`0qI=VmKQJ>,(FZd#$em^)؝j0i1 IBŅy%2k"EJI1oWv7ģwvJ#EVFrF7 F2VWvI|u^r{QEGl: 2B,$QXPI}n0cD$OX+A/7F}TAdeg^Ӑw<4Ō*Ȉs u 5$`Xwh.Ѧygt>6LIo$YD;om#$q`Q'e##$`H?6aKv#$'hUHjey诬#g7D)80!lyW8&̆Mup%$4DRMf4 . bR_#,nXo7k)RGBY}.zD yqm(:ǫ' #,A#,X;]׶It li!,maSz0Dž/BwK?H9:$|GJA y)FX-D'@~W(%TRvu!ro<(u I/g)@$PW! ,_5NH1J[:愝 j$(/S҉辰&m՞Ř̑r`#/;wcGĨKDt I"bVd6jƒlJV++-MZIkV2MZZUfkV$>3Rq#,b' F@^`{`oq`Jtc7d[&nd(`<46:#,C#@cZ*"슡rb2|+ˡ$ -& M:Wʦ憒@47  X2W)jw*6P Z(xGߨN{uqUC&ݨ$[j]8lJU޻(ƃL`*F"RulL@T Hh3tBE P'Hcj͹W+rL5%lTZj"^g<csL#$#$@TE]xVǘ}gi8\"m#$DA^Gx]H# #$7{=g(>S~'0Igw T)1#$ݕhhD"#UP/{;|tSJAq!5)_[K(ɝ#"Z 7$V- 0jCKr?&V~T"`dCc"(ew  ;v>jtmB^P/ PX l#/̀8GIkl(2h:#4)9|`sz&(ǐLy*_]_5 abh׽}!ؿwfw[#/=l?FS48|^+*` >D=ҝFWxч>|lND-0Mu$*Bz 9 훻5gl N .[O9/LV8cb¹s {"HD{JJ|CƎ}.y!<ǴI%OU%rJ5j8Mr%5'wQPL b`H&P P"Xϝv^H#,ښܣ#$d|9<]B%=Ac+#%KM.~\ Dvu!XۡX:&+&MՄilcCMr{\t@skKn:v˧~:ms8v՘Y{HۭT&Gnl=k_Mhz:0l6K.\팩l7< z M[g^MW@g#,+ű|F&hջ(t؏˿g_Z#/A r"+D)_Dz]B|CCG͇^ϯv&Cuv`FzwH[TSPKThVB(s݇V{s!Mg\b/:e#$5\c #$nEcFH<$"C>plءS'+4ADDT oJ-gj$|0k1P3@ch1Ecn3s}_27un6{;Ĝ&K-y^%߯}zwirsD鿈#,f$M粀-|B: g#/4pׁKDC}|84O׊Ȍ4VK*X1{@R^ܙȂ! HjxOuzۧHlm]j&:#/:}B#$eH;o^Pz{~5#,̒0.Žs˲&y#,kװv|T ٢dʇ)ZRI֦tԍdG]IqB'}_ |yN&lyoNS5@1FE4׵zeQ ANf-jڠh@yʱ0K`jIiU%Nl\6ڋH#P+#0+#/*R`B.[w#6QnBe#$HB+ z/khjk%Y *Hv)8l)D:ןg#s[_ORRY@6t2k6߱uOeg#$#$C#,l ,uz?t] /2Q[W: 4IF/cah1PS Uj oE=?mv Oj+M#P0#/yiR5+'#/::fyaNqB{;;#*mck*,R"TdP!r7H[,hZT"XRvfC(q9ȒHE(!sabEx4W9.y$&;:қ> ?KuUy}>#/`"^%Jn~M);AJRM,m&mUUO0{ IUÀVgVQ}*z m C!Dgr0< Q.Gc|R 0#$DRhj)o]DоOUBHp׿L(:#,G{x*%(?]lhX,Psp-#/H5AH|: @VF#,okQ;>]mtMq:v,1gbN&RI$7MW@Ȑ#,&(x!׎[l~b@dpIDhN@x7 fj2B+N ( Hgó9Uhoz 6FZKY()ѠE]M0N6Ĉs`2B5hi"a =G ن3ܱ̐xشV|<ީni5i0K "R#, Fяǖuv^^DpJiLwHSɸ#,a`M| *1{9d_QtFͧÕ:Jl;02x2w&us[O(I,7m=MĒ䵒~ɹlpkڝjemxv~-)b{|4}B#,#,[׽ 4ؕ!rgN;8#/0ZM(RNOL@Tk=_^u&.ϑk2cyOW|DZ'i.9Y41#$#/i'}-Qc@%Q,D(Q#,"ҒcqsF0nRdƕhH&4!BL iٚ,#$!3#$,(wSm5FrS㋍w4xz8:TF/p8hEϊO*4hM2jk?d!$F|4EoVmA%*UĹۈ,#/kQmxkQ5mmWKK>ϴ_ru-ޫ[ ,oSfbtl$yֵ$$$3)VfIJM1I-&jL%!MJ)%#/+K2kI$S+f2bf34ĥC#i,LRJK(a QiQ&S4c&J6BeATf Am1%4Aiw$lk-o<(y#,a;S.y6KO}HRGxZ붖5R!M wK%:L;eVqs\i%x4mǎHw nkK gÿ6pbV]RKm7#,8~Q-wd*t*. nsP;;H;[a:y#Xc~M;t>ɇ#K';I!RC,7RoGC\CePnx{K(;^{ў @JTg;܉l} w83x#,vفWfe2j$%jLg~'|J=ԇn]$ PVm:ruHDɯ3%5)DO:25+jF8Ve2jP̦ .posYqFXn&sP24c4FвCѣQEMJ5)ֱ,K,6[|f02!n6#,ΩсĐLqǎ !6te*V#/烨i72;!67LLtџkhF9p+i5zY̳~s[icXt؂kG#$,֚iW6RMhţZMDx== Y0gʈ42ˍ%flg&qz1]:޷t:Ӻ6=V_ٲomcXqs['FtQ :I{KAj=1$59:Low4(q:/qnWd/gJn2Na663#,G#,cvCpTQDI@(L1c!1[#,m&3fZpidcRNLܢ7L:T䱺׬U58&#"B\7$iu%HjTHʧd\:vfk.C-,72#,)FyMaڝL -THliv3ss4tӕӶWCBiuM)C&Ŧyp93jR~4=#/YE&)+afC\ĆTcHvLspњnU|IZ`pX܌vqʤ! e"M4.*OfL"Z-,.$P8g}CNwk_#$,a\k(pGSb)(n#owY=hYGzLTLTl68'-#$ьQ 6FcC우z|JɁ[8#,9|`5Y0}Xę+ 9I9gVZM kYZl Vx#,Nz#/#,2NL$13PiI+]gw{=ng|2Y "4@ACb Vf@juLr+oą%ꚡL"RP #/eIu! Ձ%3I89;#/#GH#$8,cmͥk[.U3BDvJlRQ]+y"9a48yr%6(j"#,.!"Bp5əkcp>%p%R0Di+ fd(p8; ` pUC<;& Fz0F nhg4g.3ho$#,DN% C0 F94SldωPBI#PaL#/fiu"j3^"btKFL舅 \1t&]"aY2 ]u8|^p T:C=#/GNf5mϦw#,@DL̯M)`5\~]bUV0-Jk_M1 8##$"ɲE'5oW[`0i#$-=Yk82[@ P酂INUI>w.g{ΐRZ\Jfo7~؋:_mP4ͺX#/>Grubt6T2K͎&̻CF1bzNu]\䈙#,#${Hc4rp?ևtB)$F;!uU#$(iƏm~4mޥ%3Q#/"PP9[LL) LD5=Y!&tk!kjÊZ\axLP_qn_W#$oӵe\ySWwUٍn.nn-sd5<{wdH}A, (|UGLK&J-v6#,|=q%Y0 @;(96ضp̮E3Pa -GJ ZLic$YƄB&&#$%/BfR@l@E`+KD#0Lq]ʄV!o|"`GFhxrF0#/K{oRCf)w߄<̴kdE4B4ӿn.d#u.!66%[GIhHCZ0 4a#/^pr)#$`Db+Fi<-vܢ*BUX.żb]p.Z!QY/}C $@39$G#$3^^UVam73F]m6~RkLp7.Fjkd\}G\)M9"H_Sm#,eDwφ%5tDiޜ4LR#/ZS[Gjjhql"ppCfPf"Nlp^F:t#,Th9w`wA23L[#9I1ؒݜ}=. pBjzSrjxWF(E!Z´ڇ.o. 6۳#,k\f`xkDiYYڷ4$ 4(p2Th"uu2;HB,&l`2&%#,&xՄ *BLP16qle%3$0 @A]^]{{g#/VdϒM8Qb{(=G+t@}c >8KIvO;.?hz KBbUs#/w]ּܻk[*j[Ij6H>f0'wz>(r|򇯩 4ӊk|tu+W@n.rr$E#,{:iY%leʵS0:2KYk2k/ylcR׭F((fE)xpx6GŹy& I-.)Te]%J4D\5&艹pDt7ۇvJ4c:ڝo:hOyU6oI&1" #,JwOX-6QLR1ISv̓3#]7jg=5fgIU#,7 oSik_3OR>;c d*MhÃ#,TC Kʊ{ҩ|&5i,#,( bņcVJd@)5.+d=vF.62*I#/:4,h`БKhPT4I$YY.*RDUai"X/,S*v4,#,B@T*l]QRc,2FK 퍃276UWi5t)7]L zBq9QA}$ fX M"h7LxҐ#$RCGg`2TԱUN9*GvtvF<&#/TcX:*UR YZ2"S FV$שޞx8%hcU}9,mj#$AMiK٘ˆF.mx*P~]('~>7 B8Ͼ5'#/$S1Rm;5}]c{&)t8.X9†mS#+/n(4~.Bq1*LPT_|eI=7ޥ||Üuق Y#,܅$j(*n(ugK}φlqGF ]aPȵ|u#evX!Q#,۴"H<#$b0mxQ@R+rNd#/t(ݓ#$"2! $K5ZszVbN}4,.y V*JNjGM9:T5[W^i%?Z{@<wغlyWVS=4d8u8 w`*͐YE#/e"V.?)& m ,Jk+-1VR@TbќD1h@mZ#,xjIZQIoMhyݣC#,chݶg6&Kq,Te!4t2`jH¦?HkFݴ@I[ ǝ.qJ<BGhQhXv -v=F:qHѪJ.2;p>0d'lzCR={LR}Qw#/BH 7sRpӷz u=šd0-J0U F$\/Rף\刐@'3snn}a>Y5&q?Ni->PcGs$L¼,ET48ܒvP-ҍd3)32mdB5r͐TF#/߯z97ƇzDŽ!j*Xc01,8g:160bX#/t_ `;P R+sg8Kxu4ʹmXLh 4ԤhEd47  F2e4{&pp}lqS=;+RΝP" 6dl#;]ymyzbz̝:%hԐ8f^!,j"jֽ+%ZM:e û(TYB @YqZﵾ3 x  d-V)Q481D=h @dPA ȅgH}q%@WS^#,d4+R!x"TL$za\ McsIr3"1wSsDMN"I ~^;/k9!)584ҋ#LF;"eݴ66$L9pK4#$1wXROaz#$=;No+fy*wЙF|"TXBR1,!E@oE1&$,!I#N\/#$] : G[R#$H4 PEw`X2#/H*\^ᗮg8HƛVBI6;CgEB WŎ^-hSJ]I0"#/tJyt2v+,u#I^'~-F EHvjD|2ڽ=#,2.`:l QQ^#/[jXY!bq!g~e>@J8&Ytzo9z<âWs)PN;1zDl(-& `RA2*ZZIL0U`rw0K:|}Q#/F,0%'`{.؋Z^˗xo"fbBw+֮֫Q]"تמ3ۼv*RJ^dCV(iH@M6;D9;bm5כd>^p#$F !rOweJև31}}Rgs%_^5Y͚-u||J~|:jɵVFa`*23a$*+{ihEjFU4i]t\ۡɉ,ŴjmbVƔU^mkYf߿ɶ+V*rtt7ĸe h 'kг)Ɩ.YHe"JJPB7&r2-o!%#$8w,H-ሪ0 !7Dۗ#Wg8hjnI`l3s=QPE8GBҔN#$mU*VzPT*dWfTp9 z :8}ފ1CvT*5HE,2*4EQ[n_Oӛ^ldi8แ{~][TyP"i1oEq8`ZA#,ơ@ (,]P3EP\ m 9{>0Ph#/Qa\MW>nlpTe(#$"BDG4I#,`>~]EcK>[^En&߭=ŇwuyU?G0P"$<ˋ#,u1XސxsĄ# [MQM> r;=EspyTI$@S)*UdaA#$dP!;f ."+Lfa'v5Cq2WF#/ł d/=bp#,=4||޵?`֬}c ؞iJ3:4GJA8$ $ % v_lq}얩-%"iyHv}.JϗKA\:?1Ԁg>'u{jHf)@PvAKfT5uԉE@Đs|yO$ u7ih5P**Q3n) ^J2#$v#h#/ L h^uI#/muX".Uw}r$M:g:F0 0#B &Dfo*QMQoR\!$Z*SS [Uʍ#,161Y+B)6]Z`im{UڹZ1mj#/y1&&e5kHƃ{ʹ[pƽɵxB1+=!`3VMF`L%Yloe5tp`ThZCd-\=eXwY mC7U66'wNb6+خ[bLkwƇIaH/Y5Mhިtc Ei4&FBBԛ:bn@ 8ZԬ;MS+Ccl$($Q<7և}l'DȌ?y0jO#,>oP33)Y;uPč h`'SI)İDЊ"HAq}_"h~*D<%;^ m=cqC3Ѡa#$RAV(FW"XėB&1P#/(7]u|BSO'6ǐ&@8oUQ E_V4x/K&KyS*YUs[ůye[%k#,Ŋ껪-zj(THS#$݄. .UL)%&Ilm#,4#$#/DdyEX!" 2(j)\+Hʢ>s0"He`ɖ@29$"ב *4D l?zaL-~/}mXp[,'}@A%ǻ#Ǿ#,08!DWbUTUϟ\6F;*4\T-Yn9 .A.Xr,sO/H)QP ,j##$CgQu؈xh: ()j (Җ3iy]4SԮh#0P"(TB:'3 r<=Fdom@YȈ^(@:4B  kx`Jͧ};#a$9ӪLM&MPo#,g8bCְ :qj5,bcC&[E pc#,v Ãܘ}#$(r7Tж/-OCXDI"BLP'Q 8ᛕ>OԡHT {=i_0$P0ZiC`l$s@f{Σg4~#/(jct3$Ǵk#$C1Jtff#:I0(W2g!T" (@!).TΖ*B5M >ɮ绉pC'F-ϪɝͿ҈?ˋ4YI2r*Ɉss/iL=Xʤ'N}IؚfߨCw u@X)"lԔ,1Mm-IkM*erڙȴP+$U1,[eU+M-f[e-b҈mL٪MSlVF6P4?;J?a>m\"f"#)5$n5F]M\#,£_kj奧թwuox#$(J5 =H$ⱁ LP<q*2ETwĤ,0}Aԓ(lH.{K^$(Ϙ#/z<8D (TUD ZqCX~+a!#$/H#/h" {R }*zО!{9^űxʥm#,^Ќ"#/o=Efwtq\/K 2MN(izI$^#/Np#,+UkHE%l0w֡r 5wyc(;0MRx$kAB#/PPv+"!А$pI;j8J,X;rl(~%>ce KF2S^F4}[Q{pw:_q8t^0]鮐_tba {xq?Ri!!#$56,Lr`O@RMR#N#/))e% h0@P0|~Luڵ/̾U++ʌ$=L&7`{ӈvބ'&ѬHUB0G%ViLm p&XUȷlj@YT0X#$I#$ l@&(@hdEiR@Y,"m0#/҃Jd׻:Jw6vǡׇM;G6Cw3NQq !sz) =~'!eV[Z#$#,@jCM&A:&"wT*z%0<$@U dKiyۚyFY#/(d0B*LBHT*F2 >QAl G(]kRAAPGpa<I -"##$K"m aF"h}3ZaMi Ʊ4 #02 p,PTUi(#,ie5YB-eSh- AKI{6&6pQ5bP#Xed9qB[aȔf#,DHF̗#$L&D#$鄂J ;[E7rl!0u%ɲو##,%#/3ھ ;#/ehd #/X@$G1%<9z;g`Pl}>Z\[޿P j#$JX U|XC)DɔĮNh(C-ƭ]Vi,Jؙh-&RUbT#$}lX[$)#/`"gjw$10W,%6tmS6">Y!PJpq3cW 5AG[GP y`CzOGq#/Wm*%m>k}LidTA7#,Ǥ{׏x}h`° j7 #0뚆q|L=$F*1mONAb,Q&3k&72^A#$4ߵC0[E2,)')|eVط>f c}7;QrZ'Hykh1&z>{OkDC߯tf(5;}H*p5=}e#/N =a2r1)lfo|ƙ׏j]N^y#eX3oabm>$2/xsF /}OrSP6dj؎S̓#/TOҐ #,:>KшF1"#$H WvŠILFB3TKEXT",!/R@0QD#,[A\uAb ,Q-d(QbH;(Vy۹uުjۤ#/D!#'v#,_ҏz"N=zE<^I&1$ե[v$|vٛysS!>m#,:4 نYa!p tRyj(= :jC4ti7qrV,%el]dlΗM@AY Э#+F43@4ja;Vu1"BB30-W;1mّF&mgV.,P=#,nWaP#$ARy6#o!cw}Z:|9.``}j.z:棰|8mk]b0Va)S}#ծ61VL#kcë8:SQ0~}?jB,NuF/}3t\n6b>tEGtLIbUؘ^2#/#,ehնfa}C֡py#o&`#,B#/b|\@8z 'xmo#/tC|׉Ciְ} 7P.Yf@@=Rn)ke <$t"L"b(keݜj#,rDL\&23uA0"zBV#$Ъ@Uo4l^CxLFxܽ:BM!p<ˡ1JL#/3($rX1ߜ]DI"x;%?X,)]1AdfA1c3[5hJXh>i ,#rL Rz} A0 TJ!\C#ÊATNA9J\Պ[k-ƍcTѬ6[W\EMl142];tQb 4Ȑ#, Fd*Vo:6v3H7,w>7NbMJ=} 6eѯuSA]5t<CQd"a4d0&3b(؞C9~`E hy+ #,!"!w v0a;}Aɇ_@&vlp`v㋜@;$8߮_2CFCC[|C0a#,ڳͪBfC:ȥ5MRiEߵ뮘% gRHBoy(0 e:#/ X#,W9*J >2GǾ^Lw"ZzdIht@=l@b1DHB(R#/QfHk38  p7$d2A4Uj@qٟe>{}O뱎k&BT6Pbd&=vqb!&C]2K#}6aܭVǤ(uUEBm=eY #,5aAUG<eem:W^ Xx!DѺZ˩VS?Rz%]P4T#,uFB#/db#$ޛ%'pHhLmoٸ!d&~l5ڿwυyvIZŰ6JsnI!J-+\x4{*8ɴP n܍ka` 1iK0K(HVmt|;x4mjB[sˑE^̳#׵!fBh$Vex(LCFXf6ZIYZA%F")"T&0FV\F o C+vHݍ ̃MN qT7v:V&&9kckR"O`il(Aģ&ҙ#$*M\2h}@q-SJ^Py#,!M %(iD@*#,iR*#, H0c(BbcI\H$G=NQj#,b޺>K2):'uZ\]@+UT9>X2?)(Hgo6yOF>~o6}w<]Sl&OyCɽbъFg׻Ftt{4zs"UkCCmc4f33nNf3nawhAf#,*&cv hU6dI#/껖Ciyng~CoQtPh]a&,h#/?rtDɏ.Q7gg^iȻtڃSKMjMi#$cB_h&wcNewo!b#.jkR#,CS)B0|;6\|>`io Y!#$ :FE%l#$H6Ƒ@ؘY&TԪQ4;U\#,6#,j:,U^u˦ۻs!˫S,-"1R\㺮ՋRm*Ʒtvym*KhIB#,/ɋhXiBVY]Z(D!B4V k*9"0 I2MPVL!P̅`H@](k H%I(X@QQj)X4*E6+5IYLɉTDmLɲlklj3#/"*@}㡠}b궼j*DI,o෫=w.g6M+(r/Q$ ,VjiV9hGoZ-Ů3Dmwg]kIK[smImr泫kJWݚE)H fFH@4& $AT Cp&q̐!dFXCax[D(6a y$50^5+)K˃^,La>5E0.`*DTEf#$xٰ$O͸fc114&1 ښn 42G;oq)mwyڴ! 6xLl) 6|U90NP s'WIv6_7{hSK͆dė+ h%ʗVMq#/EF#/6!/($X$)#$y#,q\w;QqM H#/ D _>: &/q!@$Duiŋ[Uj%NRX3#(a(Iƕ""ʪ b`'H+l1mjUu\D]"65E=SIDNV'pv:#'\I˗3&I< j6lۻ-s;9`S@$xLDz~^k{`xg|@CF'~a!Ěq8PskZ#,d,d?FM8C#$͡qΆp|Q< CuH]#,2˗ TY#/lxWMgKf@_(#,@7"Ihe'hA6#,+1QͪcBb[k8(DR%+Hh#$)`;Y@"☮-%uAȯFgd_$-'>&6#mգ#$3U:sNEO>lM sUtUݬES#1 gJq;S #, C\VHF)O`Y־ɱ^iwYrJ:y#,NneiȚI 6ɢ%& snW6to.ul:+H^-ɶ9My-˹TQ#kj[F,m:ܦ̲l;7NZHNrYh堐JB+P$F+GlEvK"OTp:"#/:L<`8#$v} BRD OE7'<Ýߩm#/nY)#,PQ#/f tnl>#,`< H U:#$EQP*B)uWf׫N٫rYdj TNp,bF"0w\#,y'7 $d=EOD[a3FT5e&ڗI,4J\C}C^.v51,&8(BIa]f<dTw±mZ*Nw|5P/*½a_?q^.ZODh9T뎴n~UAuG",Iꡦ#/\j"ЁD뙬\Λ[@-#/o g ޿~^P?A0#i Lѧֿ>I$#$t> *hBxϖ=%XV)uA_dEQ{xKrYopV]Lw0(hgEW p PEG2Ög4ģRFŶȄ8inDhX&6.+͑ϋ #4ON'8"#,$xAd6Hǽ;Cikbۇ(ެb۲JCD۰Y"P 4pQ̮^'b=ois EQa}ݑªvsFٓeՓLc?t[k4ip,ڶ+hB㜏cCԿ4}L9tE5&1++s>Y !#V1@x`؃z'Z3xm-1I4ddY9&=!Fv"GD!A&ќQl  Zvb|)J6P'srF˛6ұTeB'KۼtN*Lm t#/Ɍ֑*-5Uw~޸ׁ*h@6RM#,(Bf ݗ8k`s9S(n#,.~+x#$I ܧzCP_o[ZZ&4(v/WA֩zkLFdm"6uǐ{Y>g;%<ͬ4 om9p,{+lxf#,qe&C&YMDW@#,<&=Ts^TI ${¸ro8Be_*<T^Tzzyq ZQvNGk/Ya>ðQL #$@1#/F#w4u 㨓Q :~#/^!` cW8fVRE)LR}5*4U 9Ȑ@,tiP.xG%lo#/t3?jKTkf5" ld]P6мY-/wL3/H_\| 01>UڵZA+_z^ ;}ۥtkmEVVw#/P52Cu`+qe] ^Bk#/A/؆hۻ0oBBF#/U1ȹI#%1Wy\.\i N<<Ä[h $-˹gtɏӽwhHD"$ʆ8c݀xHBԭ#, lH6~$g#,}"\Do~3HMmi9\]IXmfD`@:밻^5I mCM=6O#tQ=C֥Z#$EWz'263dј 4PM6DE&!&4J1*FMHId,b?N\_vx!l*΂>&Û}nfݳ(716#,)5,k7yPʀ&W1?1oBG^#,4`m@@{jE#R5sQ!1QLBV̆L_#,i5U9|QI>c&A8bjMwd҄@"飐2^Ʊ`O׬3tg-#K c_{!5H6|)x$&_[;Q2|#$'D E%S)JM8Ҏ#$`RZbd-7QU;JnVݮsuv16M F#,V1r!m؛h@1AH/+lJT!v%Le2ś+QWHH B#2p*[mˌ[s̠e"]@-f@p ɢ.x*U,S+.&L+?iz,G.c*dC<'5R_uR WE nT:)ŚPy`-ppln8$FEh$#yW!fj)WMSEEm%#EZDHe=TlFahiB0hUT#,Ymۉc")65IMX#,nJE*:R\+ PBunA#/piZl#,-ao#/T#,#,#$\ĽS0%H%*p]U\j @ @e1xfdǝE2V#,07Cs`Nh2Ga%cQf=,$]:5AЍ5U!ǝ%^]3cXE-64x5H1#,]`ѰEQ$DؚgS#/hFlPD0#/6 TPE& lLP{IfE$C4HPv%RŅ[TҲ+cTd޺[q=\ܕ_ |IAbXbau+U,xJ໼1im0 b *(!"e#$]ێ/U}RiD_TY>2EB@!T4ڑBg{)"{*RTC^Av̱#,* p2}JsQ\aH%7VCדz,]ݱ鐖0m4 gHQ6Lm֠1̥E1 #/},(i40jADGpu#/.<_#aa6Ļ;c\%ޜٮ[섋:)0|^{-k ;J%lJX!@t%C˂PTPX.dp&:={J*ȔA־)<}SSe#:#,uy8gK? N[}G:HX0";̃`Յ&QT*:7u`wbnCqؖ ګU&PAJ?@܈~(P2M@{%L"b &]ƈƐhb$m% $B)(;{x)iuHbރ`15" #$D@D۪Э`}ы*vxlH((,IcWo}* @B?#$#$HeՌ4 4l"OEiAսCBR#$s#$Řj Ĉ)lJźXTʇ2`9@FDLbSU:Z$0lbhlY%)II)hХ[DZڊږjLXբMl cnAFHm l#H888cx܃IP#v#L GA#/Ҁ"QatpM1 "c$(UL4"%鵝nrE@ ]FB*#/@9X.vҖZ (h7wNõ?"^ݛkD"HAlaV:t\E(D#>?s'eO>L+KZ(QJ RF@.TH#/L@p*(6 0*bc}UP헁Qc ܢXhD)YwYci f4!#$HN#,ޝw*ra HKTJ"pۥuTe .ZbUjsm[ nڨTb4{6̤Big0]!>WZ @CͺC.&o|zYF"``΃ՁRՠբ4/!(KšET5d5\"C#{`!U94#/d{.+';<6D!JCN'cGdq[r56SIt,D'ހX> *5ўdW\n%fzETR!#$[#$]qc6:btu0bY|uƶ݀ ἻFzZ'\ptS>88-,bIN7.SOl:g=yw4D d)ULHIvzZjM.~2 #,L>f0iڻD֖Y!bSTl p,5қ=x~ٚÎ#huyUE3u=a~ja67{}b;Dei<~:]zc_=M)K8;vXqXi1ЙA0̙\KO3fCj0#iGQ1l:nvyS˔(4+] owd<㝴c ގ>yTlώ1'q^)@mBelJH:a19"wx]1yf;KB+$gBJֺ9#/) 'Kءp9`s&0="0Ad8)ΐ׬.7(fG%P@7V߭rv ch9#,ֺ.(#LC*#$@=*X*Lha-Hob徺uYؤvfr(ZnUbl*s+Ԧb,#$l3H#/"{i6D%f\Ⱥx`~Vh&p9f7Z0Cߒ֋N21D![شiUWGbg%#uYD}[tq[b0V5vf Dv:e;F#/ǗMXu鉾b~#2.9m`5/$uRdN-(ku ǸyFY_hL0xNq]3Ͷ #/4vK#/zpB^$w۾ <P2yQgp`rХK@I5/5D4ś gQ%5\${qy@vjhYQnz2wo.O ̗zG Ba#/7Vm艬5<XC{H缹a! Y˝`P—&cuXg2NƷ^򅅗4E!N岩T45ȶ&tL=Inyr%!)BnLZPjb\S#$P}XeR*1K+ud/io #,hHif-+W SJ݁:kN4bJ:`@,׆cɠ(2tHqbePDiOR;]xa%dH̡LZLZcsd0nlcoMۼ^"ɮJ墣ܧy8܍<#mv4޸5+Z3x#7T`,ip Qlnfn07#$mױ4׎vFjhMUzmkZALk.:?F!UJPmNzOs}M#/5w e> #/#$($Fh %Q5ZcR&F+b3B#$`,P*U#K d]Hza,)H0C$(}mF+6  B;#/-CLaˣU8nWUx#ӈc=v#,"t> MuN)%y`c6=E9,:ۼػ/y0΄"|$0ez¢T*r=I0l8)б w͞A#/pUxYw#$ȏlgDH%KL+"VGNhӍ8_R3Bmݽb2F[BH#/yv+ôkXgśv"~L424(B #"#,wKޖ#/X _bER.̂e"ꋳRU#,LDI#e#e؉8%qs#,B3eh@ e /Ha\v4Z#,(X3W5kyt\2)KLSWːj>bo3da[&&7, VJMjfLX|rX[[#50CL+7K#/ɽۣ%QXY4¢8T($3@̷gnՠ PJP KqĆglxgAH#/L#,D4(4(эz@ۇs rmSM\ koLW0dQ;r9Mgd#$a5)2:rg'+vuh(?hӵޥvA=38LS.<9ݓM7%89-tыήѮ*߂w|9S 2 TsBA 9#g:0*7#3I4"mIEHy!U4"#,$\U(jf8:є`#/%Xp,6aXd2B"$ЗsQvSBkeɀ440|ȹ)9fDV1#,-SWHhM%6Y* n3,UD7u5z2ҋ]muqvًЩ\tC;$j2 *#,},@"F6,)odADàbPn2B5ai[*lN!@0N#$\a;H4WEIj`6%"&#Q3#$SmɦLb(3t!ʼnE@78E3*L"[!agE"b >yyQkkU@(צp~Xj;D!#/,"B@M@*#$P@=|Cr#$pF]#/qQ#,#/Gަ%1AhSpŀәe7z-b9t+vpw"f j }#$:`i~6)Wҡ$P)N ) Շ@{Ǯhmޚd<Upw%D.TÕ8Uj4tْUlEHdߓsZhvgssȌ8"s!#,TV(NASej+mμa4}o{VvXوȹF̡3Dw~ҧ3;Lk#=U, b!GI<}R1[ '|.^I#I1H5$ԲX՘jaT֘jMqqCFf[r7¿V^`s)p&M#$7ƛhmNCMԥь;;&iL 57M'w G˹,:"lœqx4km EF#,&F[-]\g{q!:#/M )kR9F!)A C"S܀#$PYl!V\92Cc󊛞C2!~-V VEsv*5ޭh`f-V_q`33[&2Eгc1#,") wY+gw4 )@v* tԁ"&R:X*zz/~q0cxTNAO#$###$@ U6[oVF]ϺС}-(+Qo{>⍞w{8VD݊/x JKk#$ ͚CGGc*#$hSk :A'0p!,#/ Y ̏QI.$6F_Gy\7z-4VQMQF%t#/6MrCݼ[+)PV%FBj'H~gp ?}FT!0O#?0ˬ:t @IbTQ6RcАbZld**R66!*6F_H( TmiJR$<#,ADA2Ka `##$ |)kY\]O#aMGK}UMsdScXZʱF#T- O^wZ6D@F/n|L]>_G:$Vb?;K!?7\`$ia<=ċv G,"jI^Ņ)Rti1B fL *`,F)K'rkp!x#,#,OEMJ?DjA#/RF"ThDړJMRvW昐6լ#$?#,(+и^TYY^ zz| F)Vxpbܵ*#2Pvfm>t' 6 ;m^--lA:=ӤyF?8} 0eXHX"?W#$]5#L")%I51R`Mo}^&Ehi4a#ow D)#/FLʺ?#$9T(LXQ袚&f KP>Qr/mߖ[[*EkWF-ڔ5F6XXcB}D᎞é͝JR!x3`~c )p4Œjxݤ| cK${dUiGlmbJ8,,"rɀQDyboofSE*[l+kkkmMoJ*-7feW\ݵsȳjƚ&wWw[I)hV@U#,V$Jt7-M֘EyqM-ԙe^W^yƣYFS-e]ۢ[.+s28xbBKa#,!4A?h׍Ufjbɸ}!a0RlT~ #,}4Kh$ AT.^I7brن8c~ ^M-r{u亁pBKr#=X'la%#/۟#E3ȗR`Tkq.$-ݯ|2 C2M#,UJ]/αo#,1}&f#/v}yD"8QH%poRcxkMN$aT&>9ڬr_#4#ZOuҤ1$׀H0l17!dI<}M@d)&3/S=MTY#IC"J݁',=ȮN0FW`)%0'ܠ#Z V%TR73rd5&G:0@٩@ǂhvf}=+zCbUQJ-%ՙ8k:9v'WwNsQ0 % 8w8')XH=nY)a1Ο 궦qBhR\ ;[y(dB7EN@ĮDqxjtNT#,"@bp1ɳyQQ).@A lޖE%2D x%\E #$Pl~&Nx?̪@HHد#z B+ݘ \5lDX-M(Px!h@A"e &x[@( 4#/7Mzz<5/2#,̴t x!)3 ɨn8ߺ[ -icn3[3?``ڕ5$C[#^in8i0c 6D"U;7W֚D˸v0&#$ѺhWR KeTK,>#,<ٔިvxqsL( ۍlr3QQBi4H]Ra&Otad L֡);Έ!6vuaᝲoES|;6UNg1'"a]’ !W.m khZyJ/_u^[mڽ>OH8/-oCuj.bagϼ~f7*QPxHワ$t%忲 OzB39>Iފ@זfT9P^]*zg_f%iSGo f0UOG\U[el)Jot6_ $[۲ƼmIocbmomFez$,#$0 Rn݂uB$9bCf᷅8I#,.DGeÁv⅘P%"(O`C?Fxňb;NJPDa%P)`EB%H B@I E;G>hiFBt}}wN2Yi ^!GF ,A;?>P}'A'ԐȤFO@P_sX[T< zbe Vt<.Z~WO|!aTrCΗuǙuЏw@{9aw8#=y#,_+?B[2D?_Ԝ~~Ë#iS+u#K7run^#$ʠ=vb:'RRZJqTE3oU,#,L;qӧm3,M/-80ay$fu^;WzvdKMqAu|31co##,~#ħ#,bI(P5֮D*{" .p!z +#<== +#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAlyFMaIACgkQSbTGfAUn\neqoHkA/9H11S84e1r7tI8QGS15XwN1bV3KR3guTtnIAt+TaILfTne69BNMZi5iA1\nkRzKt+QcCfjI3HsV5pLJ1X4y4FZEPx4kRtVkFBZYEskl0MNwRLQsKyVEB9T9/dhb\nGP36OFXirG0iGg0qu8IaUsSLfAk/dW++dYqfB1T704FC3LyN05ijMsu5m0hTrTQE\ncUbNbj5cm9gifkbECuaIJQEhvM5TCMR1r8yoWINtD9gLKwrbNAv9f4qdFpqooLRL\noLSPWUSzcq4jkGM4jHosNh3kza5CNoGPsL470tL4u54BgIJuDO8aWAO7pDKWJgn6\nswuVol2v7QvlEb4ejErhP65yYwHc+GbDcY4FDTzL24yt4p9QZils7kAtojIsOIv/\njnE4BpJA44ymB0SKIJbK8VJfT4O71U2EayXGvSpdbGRQt0wD96wDsexLXNPoT+m7\nLiQABIJrYYMZPd6TR6czolS1PjsIb7UTIw3sF16G17xuPM0zDxlULCB2D/eMCU+G\n3xoYj0EtNY/A+xpWQO9lOQ7LqJVTjSChDFDajguslY2vxD735xbGDbGCLtAPQRjm\nVFC88WTnGy/cZ3fFlfLdaCRhpu24y9WVlTYgchSBV83gkD9gnJTvJ4q17PKUvxis\nBMysLiz9gVqT8Ic6xbCThRQ5K+pqvn3isYxz/iEm2zGL1/V7PuU=\n=wpUd\n-----END PGP SIGNATURE-----\n diff --git a/wscript b/wscript new file mode 100644 index 0000000..d2a66c5 --- /dev/null +++ b/wscript @@ -0,0 +1,147 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from __future__ import print_function +from waflib import Logs +import sys +import os +sys.path.append(os.path.realpath('scripts/waflib')) +import fwgslib + +VERSION = '2.4' +APPNAME = 'quakewrapper' +top = '.' + +def options(opt): + grp = opt.add_option_group('Common options') + + grp.add_option('-T', '--build-type', action='store', dest='BUILD_TYPE', default = None, + help = 'build type: debug, release or none(custom flags)') + + grp.add_option('-8', '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, + help = 'allow targetting 64-bit game dlls') + + opt.recurse('dlls cl_dll') + + opt.load('xcompile compiler_cxx compiler_c') + if sys.platform == 'win32': + opt.load('msvc msdev') + opt.load('reconfigure') + + +def configure(conf): + # Configuration + conf.env.GAMEDIR = 'id1' + conf.env.CLIENT_DIR = 'cl_dlls' + conf.env.SERVER_DIR = 'dlls' + conf.env.SERVER_NAME = 'qw' + conf.env.PREFIX = '' + + conf.load('reconfigure') + + 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 "-T release"') + elif not conf.options.BUILD_TYPE in ['fast', 'release', 'debug', 'nooptimize', 'sanitize', '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) + + # -march=native should not be used + if conf.options.BUILD_TYPE == 'fast': + Logs.warn('WARNING: \'fast\' build type should not be used in release builds') + + # 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 + if sys.platform == 'win32': + conf.load('msvc msdev') + conf.load('xcompile compiler_c compiler_cxx') + + if conf.env.DEST_OS2 == 'android': + conf.env.SERVER_NAME = 'server' # can't be any other name, until specified + + # print(conf.options.ALLOW64) + + 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 game dlls for 32-bit target') + else: + Logs.warn('WARNING: 64-bit game dlls may be unstable') + + linker_flags = { + 'common': { + 'msvc': ['/DEBUG'], # always create PDB, doesn't affect result binaries + 'gcc': ['-Wl,--no-undefined'] + }, + 'sanitize': { + 'gcc': ['-fsanitize=undefined', '-fsanitize=address'], + } + } + + compiler_c_cxx_flags = { + 'common': { + 'msvc': ['/D_USING_V110_SDK71_', '/Zi', '/FS'], + 'clang': ['-g', '-gdwarf-2'], + 'gcc': ['-g', '-Werror=implicit-function-declaration', '-fdiagnostics-color=always'] + }, + 'fast': { + 'msvc': ['/O2', '/Oy'], #todo: check /GL /LTCG + 'gcc': ['-Ofast', '-march=native', '-funsafe-math-optimizations', '-funsafe-loop-optimizations', '-fomit-frame-pointer'], + 'default': ['-O3'] + }, + 'release': { + 'msvc': ['/O2'], + 'default': ['-O3'] + }, + 'debug': { + 'msvc': ['/O1'], + 'gcc': ['-Og'], + 'default': ['-O1'] + }, + 'sanitize': { + 'msvc': ['/Od', '/RTC1'], + 'gcc': ['-Og', '-fsanitize=undefined', '-fsanitize=address'], + 'default': ['-O1'] + }, + 'nooptimize': { + 'msvc': ['/Od'], + 'default': ['-O0'] + } + } + + 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', fwgslib.get_flags_by_type( + compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + conf.env.append_unique('LINKFLAGS', fwgslib.get_flags_by_type( + linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + + if conf.env.COMPILER_CC == 'msvc': + conf.env.append_unique('DEFINES', ['_CRT_SECURE_NO_WARNINGS','_CRT_NONSTDC_NO_DEPRECATE']) + else: + conf.env.append_unique('DEFINES', ['stricmp=strcasecmp','strnicmp=strncasecmp','_LINUX','LINUX','_snprintf=snprintf','_vsnprintf=vsnprintf']) + cflags = ['-fvisibility=hidden','-Wno-write-strings'] + conf.env.append_unique('CFLAGS', cflags) + conf.env.append_unique('CXXFLAGS', cflags + ['-Wno-invalid-offsetof', '-fno-rtti', '-fno-exceptions']) + + # strip lib from pattern + if conf.env.DEST_OS in ['linux', 'darwin'] and conf.env.DEST_OS2 not in ['android']: + if conf.env.cshlib_PATTERN.startswith('lib'): + conf.env.cshlib_PATTERN = conf.env.cshlib_PATTERN[3:] + if conf.env.cxxshlib_PATTERN.startswith('lib'): + conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] + + conf.env.append_unique('DEFINES', 'CLIENT_WEAPONS') + + conf.recurse('cl_dll dlls') + +def build(bld): + bld.recurse('cl_dll dlls')