quakespasm-0.93.0/0000755000000000000000000000000013204512422012446 5ustar rootrootquakespasm-0.93.0/Quakespasm.html0000644000000000000000000010246313203362342015457 0ustar rootroot QuakeSpasm

QuakeSpasm


Page last edited: Nov. 2017

1. About

2. Downloads

3. Hints

4. Compiling and Installation

5. Known Bugs

6. Changes

7. Todo

8. Copyright

9. Contact

10. Links


1. About

QuakeSpasm is a modern, cross-platform Quake 1 engine based on FitzQuake.

It includes support for 64 bit CPUs and custom music playback, a new sound driver, some graphical niceities, and numerous bug-fixes and other improvements.

Quakespasm utilizes either the SDL or SDL2 frameworks, so choose which one works best for you. SDL is probably less buggy, but SDL2 has nicer features and smoother mouse input - though no CD support.

2. Downloads

3. Hints

Visit the FitzQuake homepage for a full run-down of the engine's commands and variables.

3.1 Music Playback

Quakespasm can play various external music formats, including MP3, OGG and FLAC.

3.2 Controller Support

The SDL2 variant of Quakespasm supports Xbox 360 style game controllers.

The default configuration uses the left analog stick for movement and the right for looking.

If your controller doesn't work you can try placing this file in your Quake directory, it is a community-maintained database that adds support for more controllers to SDL2.

Cvars

Buttons

Some of the controller buttons are hardcoded to allow navigating the menu:

These buttons can be bound normally:

quakespasm.pak contains a default.cfg which has been updated to give some default bindings. L/R shoulder buttons are bound to weapon switching, and L/R triggers are jump and attack.

The controller support started as Jeremiah Sypult's implementation in Quakespasm-Rift and also uses ideas and code from LordHavoc (DarkPlaces).

4. Compiling and Installation

Quakespasm's (optional) custom data is now stored in the file quakespasm.pak. This file should be placed alongside your quakespasm binary and id1 directory.

To checkout the latest version of QuakeSpasm, do: svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm

4.1 Linux/Unix

After extracting the source tarball, browse the Makefile and edit the music streaming options, then


make
cp quakespasm /usr/local/games/quake (for example)


Compile time options include

Streaming music playback requires "libmad" or "libmpg123" for MP3, and "libogg" and "libvorbis" for OGG files.

The project can also be built with Codeblocks (project files included).

4.2 Windows

The QuakeSpasm developers cross-compile windows binaries using MinGW and Mingw-w64.

The project can also be built using Visual Studio 2005 (or newer).

4.3 Mac OS X

A Quakespasm App (including program launcher and update framework) can be made using the Xcode template found in the MacOSX directory.

Alternatively, have a look at Makefile.darwin for more instructions on building from a console.

5. Known Bugs

Brightness issues should be fixed with GLSL gamma in 0.90.1, if your system supports OpenGL 2. For reference on older systems:
Some versions of Xorg and SDL have brightness issues.
Try setting "export SDL_VIDEO_X11_NODIRECTCOLOR=1", or if you have Xorg >= 7.5 and broken brightness, these patched libSDL binaries may help.

6. Changes

6.1 Changes in 0.93.0

6.2 Changes in 0.92.1

6.3 Changes in 0.92.0

6.4 Changes in 0.91.0

Bugfixes

Visual improvements

Interface improvements

Code cleanup / Other

Raised limits

6.5 Changes in 0.90.1

Bugfixes

Performance

Visual improvements

Interface improvements

Code cleanup

6.6 Changes in 0.90.0

6.7 Changes in 0.85.9

6.8 Changes in 0.85.8

6.9 Changes in 0.85.7

6.10 Changes in 0.85.6

6.11 Changes in 0.85.5

6.12 Changes in 0.85.4

6.13 Changes in 0.85.3

6.14 Changes in 0.85.2

6.15 Changes in 0.85.1

7. Todo

8. Copyright

9. Contact

10. Links

quakespasm-0.93.0/Misc/0000755000000000000000000000000013204512422013341 5ustar rootrootquakespasm-0.93.0/Misc/quake_retexturing_project.patch0000644000000000000000000001056013153013704021662 0ustar rootroot Index: Quake/gl_model.c =================================================================== --- Quake/gl_model.c (revision 1463) +++ Quake/gl_model.c (working copy) @@ -429,6 +429,8 @@ void Mod_LoadTextures (lump_t *l) extern byte *hunk_base; //johnfitz + extern cvar_t r_externaltexture_fix; //mk + //johnfitz -- don't return early if no textures; still need to create dummy texture if (!l->filelen) { @@ -490,7 +492,17 @@ void Mod_LoadTextures (lump_t *l) if (!isDedicated) //no texture uploading for dedicated server { if (!q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp + { + //mk -- begin + if (r_externaltexture_fix.value) { + if (strstr(tx->name,"sky4")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==13039) + q_strlcpy(tx->name, "sky1", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + } //mk -- end Sky_LoadTexture (tx); + } else if (tx->name[0] == '*') //warping texture { //external textures -- first look in "textures/mapname/" then look in "textures/" @@ -540,6 +552,52 @@ void Mod_LoadTextures (lump_t *l) //external textures -- first look in "textures/mapname/" then look in "textures/" mark = Hunk_LowMark (); COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname)); + //mk begin + if (r_externaltexture_fix.value) { + if (strstr(tx->name,"plat_top1")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==24428) + q_strlcpy(tx->name, "plat_top1_cable", sizeof(tx->name)); + else + q_strlcpy(tx->name, "plat_top1_bolt", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + + if (strstr(tx->name,"metal5_2")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==49173) + q_strlcpy(tx->name, "metal5_2_x", sizeof(tx->name)); + else + q_strlcpy(tx->name, "metal5_2_arc", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + + if (strstr(tx->name,"metal5_4")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==20977) + q_strlcpy(tx->name, "metal5_4_double", sizeof(tx->name)); + else + q_strlcpy(tx->name, "metal5_4_arc", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + if (strstr(tx->name,"metal5_8")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==48444) + q_strlcpy(tx->name, "metal5_8_rune", sizeof(tx->name)); + else + q_strlcpy(tx->name, "metal5_8_back", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + if (strstr(tx->name,"metal5_8")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==48444) + q_strlcpy(tx->name, "metal5_8_rune", sizeof(tx->name)); + else + q_strlcpy(tx->name, "metal5_8_back", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + if (strstr(tx->name,"window03")) { + if (CRC_Block((byte *)(tx+1), tx->width * tx->height)==63697) // e4m2 variant + q_strlcpy(tx->name, "window03_e4m2", sizeof(tx->name)); + Con_Printf(" using %s\n", tx->name); + } + } //mk end + q_snprintf (filename, sizeof(filename), "textures/%s/%s", mapname, tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) Index: Quake/gl_rmain.c =================================================================== --- Quake/gl_rmain.c (revision 1406) +++ Quake/gl_rmain.c (working copy) @@ -101,6 +101,8 @@ extern cvar_t r_vfog; //johnfitz + +cvar_t r_externaltexture_fix = {"r_externaltexture_fix","0", CVAR_ARCHIVE}; //mk cvar_t gl_zfix = {"gl_zfix", "0", CVAR_NONE}; // QuakeSpasm z-fighting fix Index: Quake/gl_rmisc.c =================================================================== --- Quake/gl_rmisc.c (revision 1406) +++ Quake/gl_rmisc.c (working copy) @@ -47,6 +47,7 @@ extern cvar_t r_noshadow_list; //johnfitz extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix +extern cvar_t r_externaltexture_fix; //mk extern gltexture_t *playertextures[MAX_SCOREBOARD]; //johnfitz @@ -231,6 +232,7 @@ void R_Init (void) Cvar_RegisterVariable (&r_noshadow_list); Cvar_SetCallback (&r_noshadow_list, R_Model_ExtraFlags_List_f); //johnfitz + Cvar_RegisterVariable (&r_externaltexture_fix); //mk Cvar_RegisterVariable (&gl_zfix); // QuakeSpasm z-fighting fix Cvar_RegisterVariable (&r_lavaalpha); quakespasm-0.93.0/Misc/systest.c0000644000000000000000000001616311643523122015236 0ustar rootroot/* * stupid test tool that reports the type sizes and * their alignment offsets in structures, and the byte * order as detected at runtime and compile time. */ /* * endianness stuff: is supposed * to succeed in locating the correct endian.h * this BSD style may not work everywhere. */ #undef ENDIAN_GUESSED_SAFE #undef ENDIAN_ASSUMED_UNSAFE #include #include #include #include /* include more if it didn't work: */ #if !defined(BYTE_ORDER) # if defined(__linux__) || defined(__linux) # include # elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) # include # elif defined(__sun) || defined(__svr4__) # include # elif defined(_AIX) # include # elif defined(sgi) # include # elif defined(__DJGPP__) # include # endif #endif /* endian includes */ #if defined(__BYTE_ORDER) && !defined(BYTE_ORDER) #define BYTE_ORDER __BYTE_ORDER #endif /* __BYTE_ORDER */ #if !defined(PDP_ENDIAN) #if defined(__PDP_ENDIAN) #define PDP_ENDIAN __PDP_ENDIAN #else #define PDP_ENDIAN 3412 #endif #endif /* NUXI endian (not supported) */ #if defined(__LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN __LITTLE_ENDIAN #endif /* __LITTLE_ENDIAN */ #if defined(__BIG_ENDIAN) && !defined(BIG_ENDIAN) #define BIG_ENDIAN __BIG_ENDIAN #endif /* __LITTLE_ENDIAN */ #if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # if (BYTE_ORDER != LITTLE_ENDIAN) && (BYTE_ORDER != BIG_ENDIAN) # error "Unsupported endianness." # endif #else /* one of the definitions is mising. */ # undef BYTE_ORDER # undef LITTLE_ENDIAN # undef BIG_ENDIAN # undef PDP_ENDIAN # define LITTLE_ENDIAN 1234 # define BIG_ENDIAN 4321 # define PDP_ENDIAN 3412 #endif /* byte order defs */ #if !defined(BYTE_ORDER) /* supposedly safe assumptions: these may actually * be OS dependant and listing all possible compiler * macros here is impossible (the ones here are gcc * flags, mostly.) so, proceed carefully.. */ # if defined(__DJGPP__) || defined(MSDOS) || defined(__MSDOS__) # define BYTE_ORDER LITTLE_ENDIAN /* DOS */ # elif defined(__sun) || defined(__svr4__) /* solaris */ # if defined(_LITTLE_ENDIAN) /* x86 */ # define BYTE_ORDER LITTLE_ENDIAN # elif defined(_BIG_ENDIAN) /* sparc */ # define BYTE_ORDER BIG_ENDIAN # endif # elif defined(__i386) || defined(__i386__) || defined(__386__) || defined(_M_IX86) # define BYTE_ORDER LITTLE_ENDIAN /* any x86 */ # elif defined(__amd64) || defined(__x86_64__) || defined(_M_X64) # define BYTE_ORDER LITTLE_ENDIAN /* any x64 */ # elif defined(_M_IA64) # define BYTE_ORDER LITTLE_ENDIAN /* ia64 / Visual C */ # elif defined (__ppc__) || defined(__POWERPC__) || defined(_M_PPC) # define BYTE_ORDER BIG_ENDIAN /* PPC: big endian */ # elif (defined(__alpha__) || defined(__alpha)) || defined(_M_ALPHA) # define BYTE_ORDER LITTLE_ENDIAN /* should be safe */ # elif defined(_WIN32) || defined(_WIN64) /* windows : */ # define BYTE_ORDER LITTLE_ENDIAN /* should be safe */ # elif defined(__hppa) || defined(__hppa__) || defined(__sparc) || defined(__sparc__) /* others: check! */ # define BYTE_ORDER BIG_ENDIAN # endif # if defined(BYTE_ORDER) /* raise a flag, just in case: */ # define ENDIAN_GUESSED_SAFE BYTE_ORDER # endif #endif /* supposedly safe assumptions */ #if !defined(BYTE_ORDER) /* brain-dead fallback: default to little endian. * change if necessary!!!! */ # define BYTE_ORDER LITTLE_ENDIAN # define ENDIAN_ASSUMED_UNSAFE BYTE_ORDER #endif /* fallback. */ #if defined(ENDIAN_ASSUMED_UNSAFE) /* # if (ENDIAN_ASSUMED_UNSAFE == LITTLE_ENDIAN) # warning "Cannot determine endianess. Using LIL endian as an UNSAFE default" # elif (ENDIAN_ASSUMED_UNSAFE == PDP_ENDIAN) # warning "Cannot determine endianess. Using PDP (NUXI) as an UNSAFE default." # elif (ENDIAN_ASSUMED_UNSAFE == BIG_ENDIAN) # warning "Cannot determine endianess. Using BIG endian as an UNSAFE default." # endif */ #endif /* ENDIAN_ASSUMED_UNSAFE */ #define COMPILED_BYTEORDER BYTE_ORDER #include #include int DetectByteorder (void) { int i = 0x12345678; /* U N I X */ /* BE_ORDER: 12 34 56 78 U N I X LE_ORDER: 78 56 34 12 X I N U PDP_ORDER: 34 12 78 56 N U X I */ if ( *(char *)&i == 0x12 ) return BIG_ENDIAN; else if ( *(char *)&i == 0x78 ) return LITTLE_ENDIAN; else if ( *(char *)&i == 0x34 ) return PDP_ENDIAN; return -1; } struct align_test_char { char dummy; char test; }; struct align_test_short { char dummy; short test; }; struct align_test_int { char dummy; int test; }; struct align_test_long { char dummy; long test; }; struct align_test_longlong { char dummy; long long test; }; struct align_test_float { char dummy; float test; }; struct align_test_double { char dummy; double test; }; struct align_test_longdouble { char dummy; long double test;}; struct align_test_voidptr { char dummy; void *test; }; int main (void) { int tmp = ((char) -1); printf ("char is signed type : %s - char is %s\n", (tmp < 0) ? "yes" : "no", (tmp < 0) ? "SIGNED" : "UNSIGNED"); printf ("Type sizes and alignment within structures:\n"); printf ("char : %d, packing offset: %d\n", (int) sizeof(char), (int) ((size_t) &((struct align_test_char *)0)->test)); printf ("short : %d, packing offset: %d\n", (int) sizeof(short), (int) ((size_t) &((struct align_test_short *)0)->test)); printf ("int : %d, packing offset: %d\n", (int) sizeof(int), (int) ((size_t) &((struct align_test_int *)0)->test)); printf ("long : %d, packing offset: %d\n", (int) sizeof(long), (int) ((size_t) &((struct align_test_long *)0)->test)); printf ("long long : %d, packing offset: %d\n", (int) sizeof(long long),(int) ((size_t) &((struct align_test_longlong *)0)->test)); printf ("void *ptr : %d, packing offset: %d\n", (int) sizeof(void *), (int) ((size_t) &((struct align_test_voidptr *)0)->test)); printf ("float : %d, packing offset: %d\n", (int) sizeof(float), (int) ((size_t) &((struct align_test_float *)0)->test)); printf ("double : %d, packing offset: %d\n", (int) sizeof(double), (int) ((size_t) &((struct align_test_double *)0)->test)); printf ("long double: %d, packing offset: %d\n", (int) sizeof(long double),(int)((size_t)&((struct align_test_longdouble *)0)->test)); printf ("ENDIANNESS (BYTE ORDER):\n"); tmp = DetectByteorder(); printf ("Runtime detection : "); switch (tmp) { case BIG_ENDIAN: printf ("Big Endian"); break; case LITTLE_ENDIAN: printf ("Little Endian"); break; case PDP_ENDIAN: printf ("PDP (NUXI) Endian"); break; default: printf ("Unknown Endian"); break; } printf ("\n"); tmp = COMPILED_BYTEORDER; printf ("Compile time detection: "); switch (tmp) { case BIG_ENDIAN: printf ("Big Endian"); break; case LITTLE_ENDIAN: printf ("Little Endian"); break; case PDP_ENDIAN: printf ("PDP (NUXI) Endian"); break; default: printf ("Unknown Endian"); break; } #if defined(ENDIAN_GUESSED_SAFE) printf (" (Safe guess)"); #elif defined(ENDIAN_ASSUMED_UNSAFE) printf (" (Unsafe assumption)"); #endif printf ("\n"); return 0; } quakespasm-0.93.0/Misc/fitzquake085.txt0000644000000000000000000014324711364571402016365 0ustar rootroot ================================================================================ Fitzquake version 0.85, Feb 12, 2009 Filename : fitzquake085.exe Author : John Fitzgibbons Email Address : johnfitz@u.washington.edu Author's Homepage : http://www.celephais.net/ Fitzquake Homepage : http://www.celephais.net/fitzquake Fitzquake is a modified glquake based on the source code released by id Software. My primary focus is fixing a lot of the rendering bugs which made glquake inferior to the software renderer. My secondary focus is adding conveniences for mappers and general users. I am also slowly adding support for new modding or mapping features such as skyboxes, fog, and colored light. While I have made extensive changes to the code, I pretty much use the same OpenGL features that the original glquake uses. Therefore, if you can run glquake, you can probably run Fitzquake. I am not finished working on this project, so bug reports and feature requests are welcome. Acknowlegements -------------------------------------------------------------------------------- id Software, LordHavoc, Bengt Jardrup, Baker, Aardappel, SmallPileOfGibs, FrikaC, Vondur, JPL, Negke, preach, Topaz, Tomaz, Tonik, Radix, EvilTypeGuy, NightBringer, MH, Tyrann, Spirit, Fett, Maddes, Craig Wills, Heffo, Riot, Gleeb, Speedy, people in #flipcode, & others for their feedback, tutorials, code, testing, and assistance. Copyright / Permissions -------------------------------------------------------------------------------- Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. History ================================================================================ changes in 0.85 --------------- - increased limits - added a network protocol 666, which supports higher limits, entity alpha, and more (see command "sv_protocol") - can load and play all known limits-breaking maps, including masque, warpspasm, indian summer, sickbase, 768_negke, digs04, omlabx, and bod. - increased MAX_MODELS from 256 to 2048 (and MAX_MOD_KNOWN from 512 to 2048) (requires the new protocol) Prints a warning if you exceed the old limit. - increased MAX_SOUNDS from 256 to 2048 (requires the new protocol) Prints a warning if you exceed the old limit. - increased the upper limit for the "max_edicts" cvar to 32000 (requires the new protocol for entities past 8192 to play sounds) - models with more than 256 frames can now actually use those frames (requires the new protocol) - fixed crash when marksurfaces > 32767, raising the effective limit to 65k. Prints a warning if you exceed 32767. - fixed crash or other weird behavior when faces > 32767, raising the effective limit to 65k. Prints a warning if you exceed 32767. - fixed crash when nodes > 32767, raising the effective limit for leafs+nodes to 65k. Prints a warning if you exceed 32767. - increased clipnode capacity from 32k to 65k. Prints a warning if you exceed 32767. - increased MAX_MAP_LEAFS from 8192 to 32k (these are visleafs, not total leafs.) Prints a warning if you exceed the old limit. - increased MAX_STATIC_ENTITIES from 128 to 512. Prints a warning if you exceed the old limit. - increased MAX_VISEDICTS from 256 to 1024, which should fix static entities flickering in large maps. Prints a warning if you exceed the old limit. - increased MAX_EFRAGS from 640 to 2048. Should reduce the "Too many efrags!" messages. Prints a warning if you exceed the old limit. - increased MAX_LIGHTMAPS from 64 to 256, meaning you can load 4 times as much lightmap data before getting an "Allocblock:full" error. Prints a warning if you exceed the old limit. - increased MAX_GLTEXTURES from 1024 to 2048 - increased MAX_BEAMS from 24 to 32 and MAX_TEMP_ENTITIES from 64 to 256, so you can have more lightning bolts now - increased increase MAX_DLIGHTS from 32 to 64 - eliminated "packet overflow" errors in single-player mode on all known maps, by increasing MAX_DATAGRAM from 1024 to 32000 (NET_MAXMESSAGE and MAX_MSGLEN were also increased accordingly.) In multiplayer (including COOP,) the MAX_DATAGRAM size is 1400 because the MTU on most internet routers is ~1450, and anything above this could get fragmented and lost since datagram packets use UDP. Prints warning when packetsize exceeds the standard datagram size. - increased signon buffer capacity, should eliminate sz_getspace errors (example: hard mode coop crash on Travail's qte2m1.bsp.) Prints a warning when signon buffer is too big for standard servers. - "no free edicts" error now spits you back to the console instead of crashing - visual improvements - added model interpolation for animation and movement. See cvars r_lerpmodels, r_lerpmove, r_nolerp_list. - added interpolation on the gun kick motion, and ability to disable gun kick entirely (see cvar "v_gunkick") - disabled torches/bolts casting shadows - more content support - added support for per-entity alpha, and it works even without a custom progs.dat, works on static entities too, and both bsp and mdl models. Set values directly in the map and/or control it with quakec. When setting alpha, a value of "0" is interpreted as "default", which means 1.0 for most things, but for water polygons it t means obey the value of r_waterwarp. A value greater than 0, up to 1.0, will explicitly set the alpha, and override r_wateralpha for water polygons. A value of -1 will explictly set the alpha to 0 (invisible), and override r_wateralpha. (requires the new protocol, except for alpha -1, which makes things invisible even with protocol 15) - can playback protocol 15 demos containing the nonstandard U_TRANS bit, as recorded by nehahra, joequake, and aguirre's engine. When the server says it is running protocol 15, and the U_TRANS bit is encountered, fitzquake will assume it's the Nehahra protocol and parse it accordingly. - added support for -quoth command line switch. Loads quoth content regardless of current gamedir, and enables hipnotic statusbar layout. This allows you to load a mod that requires quoth, but is in a separate directory (i.e. fitzquake.exe -quoth -game warp) - fullbrights now use additive blending instead of alpha blending. This allows me to support external glowmaps, and entity transparency with fullbrights/glowmaps. In some cases, additive fullbrights are rendered using multitexture, but only if GL_EXT_texture_env_add is available. The command line option -nocombine will disable the use of GL_EXT_texture_env_add. - added support for external glowmaps (names must end in _luma or _glow) - added support for external liquid textures (name your files #water instead of *water) - added support for map-specific texture directories (put textures in /textures/mapname/) - interface improvements - improved crosshair accuracy somewhat for protocol 15, and improved it a lot in protocol 666 - rearranged scorebar so that map titles as long as 40 chars can be displayed without scrolling - statusbar displayed values now cap at 999 rather than displaying truncated numbers (1001 will be displayed as 999 instead of 001) - tab completion cycle order now alphabetized as a whole rather than grouped by type - added scr_conscale cvar (see below) - added scr_crosshairscale cvar (see below) - added scr_sbaralpha cvar (see below) - scr_showfps and scr_clock display now use same scale as console (scr_conscale, scr_conwidth) - video menu now shows aspect ratio of selected video mode - added aguirRe's trick of making the window perfectly cover the screen with no titlebar/borders, if you create a windowed mode with height/width equal to the desktop size. (not tested on dual monitors, though.) - developer features - added ability to monitor various stats that change dynamically, such as packet size, temp entities, and visedicts. (see cvar "devstats") - restricted spammy messages "packet overflow", "beam list overflow", and "too many efrags" to repeat at most once every 3 seconds - r_showbboxes now draws point entities too - "mcache" command now gives you a total number of models (note, this is a list of all loaded models, not just the current level's model precaches) - "soundlist" command now gives you a total number of sounds (note, this is a list of all loaded sounds, not just the current level's sound precaches) - "give" command can now give armor values above 200 - added imagedump command (see below) - "x is not a field" message is now shown only if developer = 1 - bugfixes - added Baker's fixes for Intel display adapters - fixed tab completion cycle never reaching certain commands/cvars in list - fixed -dinput mouse doesn't work after vid_restart - fixed rotated alias models incorrectly culled - fixed sky animation on brush models - fixed "invalid pixel format" error when switching between two video modes with the same bit depth, on certain chipsets (Baker had this problem) - fixed alt-tab fullscreen trashed window on some nvidia, intel cards (from Baker) - fixed some TGAs load upside down - fixed non-square PCX loading appears interlaced - fixed PCX loading from inside pakfiles appears as random noise - fixed player skin corruption bug (thanks aguirRe) - fixed improperly trying to colormap the playermodel before a map is loaded, after switching games (could cause a crash or discolored textures) - fixed too-dark fog on world/brushmodels with -nomtex - fixed overbright models too light with fog and -nocombine - fixed r_wateralpha support on brush entities - fixed grenade shadows spinning wildly instead of staying on the ground - fixed solid fogged sky was not quite the same color as solid fogged geometry, for certain colors of fog - fixed trigger_changelevel with missing map causes apparent hang - fixed hang when r_stereo and r_lightmap are both enabled - fixed "W_GetLumpinfo" crash when a graphic is missing from gfx.wad. Prints a console message instead. Missing graphics will be drawn as a checkerboard image. Note: conchars is the one graphic that must be in the gfx.wad, since the console won't even function without it. - fixed accidentally stripping -hipnotic and -rogue directories when switching games - fixed give command doesn't update currentammo - fixed changing gamedirs while a skybox is loaded sometimes corrupts textures/lightmaps in the next map loaded - fixed unnecessary creation of "glquake" subdirectory - fixed 0Hz/0bpp showing up in video menu sometimes - fixed -aliases triggered on alt-tab - fixed con_logcenterprint considering identical messages to be duplicates even though the map has changed in between - fixed hang in proxmap2, when going through one of the teleporters (thanks spirit, mh) - fixed lightning bolt memory corruption crash (rare) - source cleanup - removed all ASM code from source, and removed gas2masm project - cleaned up unnecessary library dependencies in source release - renamed local variable "errno" to fix a compiling problem on visual c++ express edition - added dxsdk folder to source release cvars: - devstats. default 0. If 1, prints various the current and peak values of various stats that change over time, such as packetsize, and edicts. Note: even if devstats is set to 0, fitzquake will keep track of peak values so when you turn it on, the peaks are accurate. Devstats display scale follows console text scale. - r_lerpmodels. default 0. If 1, do interpolation on model animations, with exceptions for torches (r_nolerp_list) & muzzleflares (interpolation is suspended for two frames if EF_MUZZLEFLASH is encountered, on the assumption a muzzleflare happens at the same time.) If 2, lerp all animations, without exception. If 0, disable interpolation (to mimic standard quake.) - r_lerpmove. default 0. If 1, do movement interpolation on MOVETYPE_STEP entities (i.e. monsters.) If 0, disable interpolation (to mimic standard quake.) I recommend that you use r_lerpmove and r_lerpmodels together, or turn them both off, otherwise monsters will move strangely. - r_nolerp_list. Contains a comma-separated list of filenames of models to exclude from lerping. Filename must include the relative path and extension, for example "progs/flame.mdl". The default is "progs/flame.mdl,progs/flame2.mdl,progs/braztall.mdl,progs/brazshrt.mdl,progs/longtrch.mdl,progs/flame_pyre.mdl,progs/v_saw.mdl,progs/v_xfist.mdl,progs/h2stuff/newfire.mdl". This should include all torches in id1, quoth, and bastion, which look bad when lerped, and the zerstorer chainsaw and xmen fist, which also look bad lerped. - r_skyfog. default 0.5. Controls how much the sky is obscured by the fog. 0 = sky is completely unfogged, 1 = sky is completely fogged. - scr_conscale. default 1. Scales the console text larger, but only has an effect when scr_conwidth is 0. - scr_crosshairscale. default 1. Scales the crosshair larger. 1 = normal, 10 = maximum enlargement - scr_sbaralpha. default 1. Controls the transparency of the statusbar background. 0 = invisible, 1 = opaque (original quake behavior), in-between values give different levels of transparency. Pro tip: set it to 0.99 and it will still appear opaque, but the 3D view will expand to fill the area on both sides of the statusbar. - v_gunkick. default 1. If 1, standard quake view kick when you fire your gun. If 2, interpolates the view kick. If 0, disables view kick entirely. commands: - imagedump. Dumps all loaded textures from opengl into tga files. This shows the textures as they exist in opengl texture memory. Texture names containing '*' will be renamed with '#' instead. - sv_protocol. Sets the network protocol used by the server. Default is 666. Possible values are 15 (standard netquake protocol) or 666 (new fitzquake protocol.) Changes to the protocol will not take effect until the next time you load/reload a level. When running protocol 666, standard clients won't be able to connect to your server, and they won't be able to play any demos you record. When running protocol 15, only 256 models and 256 sounds can be sent to the client, which means you would see invisible entities and not hear some sounds, when running a map or mod that has more than 256 models or sounds. There is also some other data the server will suppress in protocol 15, such as per-entity alpha. Also note, dzip's special demo compression does not work on demos that use modified protocols, so you will get a compression ratio no better than standard zip compression. changes in 0.80 --------------- - map loading is about four times faster. This is due to some optimizations in the texture loading code. - video mode can now be changed in-game. You can change resolution, color depth, refresh rate, and switch between windowed and fullscreen modes. This can be accomplished easily using the video options menu, and is also available at the console using cvars. Note: if you launch fitzquake with -width, -height, -window, or -bpp in the command line, Fitzquake will use the command line settings and ignore whatever it reads from the config files at startup. However, you will still be able to change the video mode from the menu or the console as you like. Also note: to change video modes by execing a config file, the config file must include "vid_restart" after the other vid_ cvars have been set. (see cvars "vid_width," "vid_height," "vid_fullscreen," "vid_bpp," "vid_refreshrate," and commands "vid_restart," "vid_test") - added control of entity count limits (MAX_EDICTS.) The default maximum has been increased from 600 to 1024. It can be raised even higher (up to 8192 -- this is a limit set by the network protocol) using a cvar. (see cvar "max_edicts") - added control of vertical sync. (see cvar "vid_vsync") - added control of anistropic filtering. Anisotropic filtering is a method to improve texture clarity on surfaces when viewed at steep angles. (see cvar "gl_texture_anisotropy") - console buffer size can now be set. The command line option "-consize" allows you to specify the buffer size in kilobytes. For reference, fitzquake's default buffer size is 64k, and glquake's is 16k. 16k is also the smallest size fitzquake will allow you to set. Note: this buffer lives in the heap, so if you want to have a HUGE buffer, you might need to increase -heapsize also. - increased max surface extents from 256 (the software quake maximum) to 2000. For reference, the glquake maximum is 512. This should eliminate the "bad surface extents" crash when running some maps that could be played in glquake, but not fitzquake or winquake. - increased max vertices in an alias model from 1024 (the glquake maximum) to 2000 (the software quake maximum.) - increased MAX_CHANNELS from 128 to 512 and MAX_DYNAMIC_CHANNELS from 8 to 128 - screenshot filenames are now in the format "fitz0000.tga", increasing the maximum number of screenshots per folder from one hundred to ten thousand. - vid_describemodes output cleaned up; now displays a list of valid refresh rates for each mode - added a more intuitive cvar control of slow motion/fast-forward for demos and live gameplay. (see cvar "host_timescale") - "reset to defaults" option in the menu now executes the "resetall" command before loading default.cfg - game command now writes config.cfg to current gamedir before switching - +mlook is now saved to config.cfg - changed smallest allowed window size from 320x240 to 320x200 - console cursor images are now hard-coded, and the insert mode cursor is now a vertical bar instead of an underscore. This was prompted by several popular mods (OUM, Xmen TC) using nonstandard cursors, which are incompatible the fitzquake console enhancements. - gl_farclip now defaults to 16384. This should be high enough to handle even the largest open areas without unwanted clipping. The only reason you'd want to lower this number is if you see z-fighting. - added cvar r_drawworld (see below) - added cvar r_showtris (see below) - added cvar r_showbboxes (see below) - added a command to cycle a cvar through a list of values. (see command "cycle") - mapname command now works on dedicated server. - added a hack to fix those white edges on the bottom of the large box of shells. I feel dirty, but they look better, now. - changed stuffcmds behavior to allow hyphenated map names, config file names, etc. in the command line. (example: "fitzquake.exe +exec my-config.cfg +map my-map") However, you still can't load a map or config file when the first character in the filename is a hyphen. - skybox loader now reverts to scrolling sky if all 6 skybox faces failed to load. - tweaked circular particles to match the apparent size of the square particles. - fixed crash when loading mods with large player skins (like the chainsaw mod, and PerQuake.) Colormapping can now handle any size player texture (limited to the heapsize, of course.) - fixed bug where a replacement model in a mod is messed up becuase a matching .ms2 file in id1/glquake is present. The fix is simply to disable all mesh caching, which slows down model loading a little. - fixed freeze when gl_overbright is 1 and either texture_env_combine or multitexture is disabled or not supported. - fixed "numgltextures == MAX_GLTEXTURES" crash when viewing multiplayer->setup menu. - fixed "numgltextures == MAX_GLTEXTURES" crash when playing large maps (nesp09,) due to frequent model recaches. - fixed bug where entities and water were not being drawn any frame in which a model had to be recached. - fixed skybox texture showing up in the place of other textures/skins/lightmaps, when the previous loaded map had a skybox. - fixed missing polygons on edges of screen when underwater and r_waterwarp is 1. - fixed missing polygons on edges of screen when r_stereo is enabled. - fixed long mapnames that scroll are truncated to be shorter than what appears in the console. Well, sort of. What I did was increase the maximum length from 39 to 127. A mapname longer than 127 will still be truncated, but these are pretty rare. - fixed rogue's teamplay skin showing up as an all-white texture. - fixed checkerboard texture shows up sometimes when an animated texture has fullbright pixels on some frames, but not others (reactor core in junk.bsp) - fixed color 255 is not fullbright - fixed fuscia dots appearing in corners of resampled textures - fixed mapname command crashes when client is disconnected. - fixed cl_nolerp 1 screws up speed of demo playback - fixed annoying client-side step-up smoothing when in noclip mode (except on nonlocal servers.) - fixed scr_clock 2 displays hour twelve as "0" instead of "12" - fixed skip textured surfaces still drawn even after running tyrann's skip utility - fixed crash when loading a map with a sky texture where the "sky" in the texturename is not lowercase - fixed hang in e2m2 on easy skill, where you can shoot one of the buttons at the beginning and then trick-jump through the open gate. Now it prints a warning message ("SV_TouchLinks: null link") and lets you keep playing. - fixed bug when viewsize < 100, and r_oldwater is 0, where you can see the water textures overlaying part of the brown frame around the viewport. - fixed bug where imagelist and r_speeds2 would report the same megabyte counts in both 16bpp and 32bpp mode, even though texture bpp should match (and does match) framebuffer bpp. The numbers are now different and accurate for each bit depth. - removed cvars "vid_config_x," "vid_config_y," "vid_wait," "vid_nopageflip," "_vid_wait_override," "_vid_default_mode," "_vid_default_mode_win," and "vid_stretch_by_2," none of which glquake ever used. - removed cvar "vid_mode" and commands "vid_describemode" and "vid_nummodes," because of the new video mode handling. cvars: - gl_texture_anisotropy. Controls the amount of anisotropy in texture filtering. If 1 or less than 1, texture filtering is normal (isotropic.) If greater than 1, increasing degrees of anisotropic filtering are used, up to the hardware maximum. Set value to 2 for 2x anisotropic, 4 for 4x, etc. Default 1. - host_timescale. Scales the passage of time on client and server. Set to 0 or 1 for normal speed, less than 1 for slow motion, and greater than 1 for fast-forward. If greater than 0, overrides host_framerate. Default 0. - max_edicts. Sets the maximum number of entites on both the client and server. Default 1024. Acceptable values range from 256 to 8192. Set to 600 to mimic standard quake. Changes won't take effect until the next time a map is loaded. Note: if a client connects to a server, and the client's maximum is lower than the server's, the client will probably crash if it ever sees an entnum higher than its local max_edicts. Warning: you may need to increase -heapsize if you want a high max_edicts value. - r_drawworld. If 1, draw world as usual. If 0, don't draw the world. Default 1. (compare r_drawentities) - r_showbboxes. If 1, draw a wireframe bounding box around each entity. Note that these are the server-side per-edict physics bounding boxes, not the client-side per-model rendering bboxes. If 0, disable this feature. Default 0. - r_showtris. If 1, draw wireframe outlines for every triangle in the scene. Like in Quake 3, the lines will be visible even through solid geometry. If 2, draw the outlines only on visible surfaces (like r_showtris 2 in Medal of Honor.) If 0, disable wireframe overlay. Default 0. - vid_bpp. Sets the color depth of the screen in fullscreen mode. Windowed mode ignores this setting. Can be 16 or 32. Default 16. (Changes won't take effect until the next call to vid_restart.) - vid_fullscreen. If 1, fitzquake will run fullscreen. If 0, fitzquake will run in a window. Default 1. (Changes won't take effect until the next call to vid_restart.) - vid_height. Sets the vertical screen/window resolution. Default 480. In windowed mode, cannot be less than 200. (Changes won't take effect until the next call to vid_restart.) - vid_refreshrate. Sets the refresh rate of the screen in fullscreen mode. Windowed mode ignores this setting. Possible values include 60, 70, 72, 75, 85, etc. Default 60. (Changes won't take effect until the next call to vid_restart.) - vid_width. Sets the horizontal screen/window resolution. Default 640. In windowed mode, cannot be less than 320. (Changes won't take effect until the next call to vid_restart.) - vid_vsync. Set to 1 to enable vertical sync, which eliminates tearing, but caps your framerate at the monitor refreshrate. Set to 0 to disable vertical sync, which allows tearing but doesn't cap your framerate. Default 0. Note: If fitzquake detects that your driver settings have forced vsync to be disabled, it will post a message to the console saying so, and this cvar will have no effect. commands: - "cycle [ [ ...]]" to cycle a cvar through a list of values.. Note: this command will get stuck on a list that contains the same value more than once, such as "cycle blah 1 2 1 3". If you're doing anything that complex you can just use aliases. - vid_restart. Tries to set a video mode that matches the values of vid_width, vid_height, vid_fullscreen, and, if vid_fullscreen is 1, vid_bpp and vid_refreshrate. - vid_test. Like vid_restart, except that after switching to the new mode pops up a confirmation dialogue. This is useful if you are not sure what modes your monitor can handle, so you don't get stuck with a blank screen. The dialogue has a time limit so that if you don't press a key within 5 seconds, it will revert to the previous mode. changes in 0.75 --------------- - totally rewritten bsp drawing code. The new code combines the advantages of the gl_texsort 1 and gl_texsort 0 codepaths from glquake into one codepath that uses texture sorting and multitexture. In my tests, i've found that it's about the same speed as glquake in low poly scenes (like the original quake levels,) but as you get into the thousands of wpolys, it's faster and faster. - 2x overbright lighting. Lighting now looks like software quake. Just like overbright lighting on models, overbright on world polys requires GL_EXT_texture_env_combine (TNT and later, voodoo4 and later.) Without it, FitzQuake will be use two passes to render overbright world polys. So if you don't have that extension, you might disable it for performance reasons. (see cvar "gl_overbright") - colored lighting support using .lit files. - totally new water surface animation. The new method requires no surface subdivision, isn't plagued by tearing and sparklies caused by tjunctions, and looks almost identical to software quake's water, and doesn't slow down on very large sheets of water. (see cvars "r_waterquality" and "r_oldwater") - old water aninmation fixed so that it doesn't look bad when gl_subdivide_size is 32 or lower. - can load external textures (currently targa and pcx) if they match the texture name in the bsp and are located in the id1/textures, or /textures directory. At the moment no other images (skins, menu, etc.) can be replaced. - gamma correction now goes back to normal when Fitzquake loses focus. - tab completion now adds a space after the completed command/cvar if the cursor is at the end of the editline. - increased the max length of the video mode list from 30 to 80. This should alleviate the problems people with newer cards were having trying to use some 32-bit modes -- there were so many 16-bit modes that they filled up the list before all 32-bit modes could be detected. The video menu will still only list a certain number of modes due to space constraints, but you can see the complete list by using the console command "vid_describemodes" - number of listed video modes in the "video options" menu increased from 27 to 36. - added alpha control for the front sky layer. (see cvar "r_skyalpha") - clock can now display time in 24-hour or "military" time. - added an "mtex" counter to the r_speeds 2 readout. This measures the number of megabytes of texture memory used each frame to draw the scene. Note: this doesn't count textures used to draw the console, menu, or statusbar. - added optional drawing of surfaces inside sky leaves, for compiler/map testing purposes. (see cvar "r_oldskyleaf") - sky surfaces on bmodels are now visible, though drawn incorrectly. - fixed multiple textures in bsp with same name or no name get overwritten/not loaded. (example: rd1m3) - fixed some nasty texture loading bugs that were especially hounding 3dfx users (wrong texture, no texture, or unnecessarily low-res textures displayed on models and world.) These bugs would also occur in conjunction with nonzero values of gl_max_size or gl_picmip. - fixed bug where changing gl_max_size or gl_picmip in-game would screw up alias model texture coordinates. - fixed bug where if -gamma is specified at the command line, the "gamma" cvar would be ignored. - fixed model more than 2048 units above floor us unlit by static lighting. - fixed crash with con_logcenterprint when centerprint message was too long. - fixed crash when changing to a nonexistant gamedir and then trying to write a file (screenshot, etc.) Fitzquake now creates the directory as needed. - fixed "bad surface extents" error when sky or water surface is missing from bsp file. - fixed alias model shadows write to z-buffer. - fixed underwater intermission camera has warp, but no screenblend. - fixed console buffer still scrolled back after using the "clear" command. - fixed console command history (the list of previous commands) not being rewound after toggling the console. - removed cvar gl_texsort. New bsp drawing code always sorts by texture. - removed cvar gl_ztrick. The depth buffer is now cleared every frame. - removed cvar gl_keeptjunctions. Extra verts created by qbsp to eliminate tjunctions are now always kept. - removed "sliding on monsters' heads" fix, becuase I decided I don't like the idea of changing gameplay, even if the original behaviour is clearly a bug. Since this bug can be fixed in quakec also, I feel safer leaving it as it was. cvars: - gl_overbright. default 1. This variable controls overbright lighting on the world polygons. (For lighting on models, use gl_overbright_models.) If 1, overbright will be enabled and lighting will resemble software Quake. If 0, overbright will be disabled and lighting will resemble GLQuake. - r_oldskyleaf. default 0. If 0, surfaces inside sky leaves will be skipped by the renderer. If 1, they will be drawn whenever they are in your PVS, just like any other surface. - r_oldwater. default 1. If 1, use the old GLQuake method of subdividing the water surface to enable a warping animation. If 0, use the new render-to-texture method. Note: in general, r_oldwater 0 looks better and runs slower. So experiment to see if the performance hit is acceptable to you. - r_skyalpha. default 1. Sets the alpha of the front sky layer. Note that if sky alpha is less than 1.0, the sky will be drawn in two passes even if you have multitexture. - r_waterquality. default 8. Sets the quality of the water when r_oldwater is 0. Can be anywhere from 1 to 64. Lower values give better performance, higher values look better. A value of 4 will provide water that looks at least as good as glquake can get, and 32 is close enough to software quake for all but the most picky. To control the quality of water when r_oldwater is 1, use gl_subdivide_size instead. - scr_clock. default 0. If 1, game time is displayed. If 2, system time is displayed in 12-hour format. If 3, system time will be displayed in 24-hour or "military" time. Changes in 0.70 --------------- - added anaglyph stereo rendering. Note that this will cut your framerate in half, as it is rendering the scene once for each eye. You might want to turn off r_drawviewmodel as it is hard to focus on becuase it is so close. (see cvars "r_stereo", "r_stereodepth") - now uses ARB_multitexture if present, otherwise tries to use SGIS_multitexture. This should fix various blending bugs on some cards, such as weird cloud layers, all-black models, and inverted torches. (only ARB guarantees that GL_DECAL blending will work) - overbright models now uses GL_EXT_texture_env_combine if supported (TNT and later, voodoo4 and later,) which saves one or two passes on model rendering when gl_overbright_models is 1. The command line option -nocombine will disable this. - gl_overbright_models now defaults to 1 - custom palettes are now correctly loaded when you use the "game" command. - RecursiveLightPoint is now lerped for smoother lighting of slow-moving models. Check out the ogre in e3m3 for an example. - dynamic lighting (rockets, etc) has been moderately sped up. (some lightmaps were being uploaded even though they weren't actually touched by a dynamic light.) - overhauled 2d drawing to allow independent scaling of console, menu, and sbar (see cvars "scr_conwdith," "scr_menuscale," and "scr_sbarscale") command line switches "-conwidth" and "-conheight" removed. cvar "scr_stretch_menus" removed. - user control of max framerate. (see cvar "host_maxfps") - improved the accuracy of the FPS counter a bit, but it still seems suspect. - particles can now be drawn as quads or triangles. (see cvar "r_quadparticles") - opengl information (vendor, renderer, version, extensions) is no longer printed during initialization. (see command "gl_info") - rewrote texture management. Now instead of quake's memory usage going up and up forever (becuase textures were uploaded to the opengl.dll and never freed,) all textures will get flushed when you switch games, bringing you back down to where you were when fitzquake first launched. - gl_texturemode command will now accept a number (1 through 6) as well as the name (gl_linear_mipmap_nearest, etc.) - gl_describetexturemodes will list all texturemodes. - the inside of sky leaves are no longer rendered -- so when noclipping inside them, it will look the same as noclipping inside a solid wall. This does not affect gameplay. - cleaned up intermission display in singleplayer -- no more overlapping numbers - r_speeds readout modified a bit. (see cvar "r_speeds") - r_sky_quality now defaults to 12 instead of 8, since skies are now drawn in 1 pass (with multitexture at least) - gl_texsort now always defaults to 0. (it used to be forced to 1 when multitexture was unavailable) - fixed bug where models would actually darken when dlights got bright enough - fixed deathmatch, coop cvars not reset to zero when starting a new game from the single player menu - fixed r_lightmap doesn't work when gl_texsort = 0. - fixed inverted lightmaps / no textures / no dynamic light bug when multitexture is disabled and gl_texsort = 0 - fixed scrolling map title in wrong place when width does not equal conwidth - fixed a few color borking problems in 16-bit mode. The front sky layer, sprites, and pics with transparency will still look a bit off (as in glquake,) but at least the console image and statusbar background look better. Though it isn't perfect, it should once again look like what you expect from glquake in 16bit. - fixed pixel gap on both sides of the console in 1024x768 - fixed wpoly count being slightly lower when gl_texsort = 0 (the count was correct when gl_texsort = 1) - fixed lmap count always zero when gl_texsort = 1 - dlight fans are now drawn after water, so that it looks right at least when wateralpha is 1. - fixed a possible bug with older 3dfx cards (voodoo 1/2/rush) where the gamma cvar might not work (untested) - fixed old commands showing up in the console prompt after hitting 'enter' - increased MAX_HANDLES so that you should never see the "out of handles" error message. - removed cvar "chase_alpha." the transparent player model was buggy and didn't work well in a lot of conditions. - removed cvar "gl_doubleeys" (yes, that is the correct misspelling) With overbright models, eyes can be seen as easily as in software mode. - removed cvars "cl_crossx," "cl_crossy," "lcd_x," "lcd_yaw," and "gl_reporttjunctions" which didn't do anything (in glquake, at least). - removed support for GL_EXT_shared_texture_palette (special 8-bit texture format) - removed VCR code. command line switches "-record" and "-playback" no longer supported. - removed support for command line switch "-gamma" -- just use the gamma cvar, or idgamma or something. cvars: - r_stereo. default 0. If nonzero, the scene will be rendered once in red, and again in cyan, with the two views shifted slightly. If you have 3D glasses you can appreciate this (assumes that the left eye is red and the right eye is cyan.) The value of r_stereo sets the eye separation. If your glasses have red on the right eye, use a negative value to flip the views. - r_stereodepth. default 128. Sets the distance at which the two views will converge when stereo rendering is active. - scr_conwdith. default 0. Sets the virtual console width, where smaller numbers means larger text. Values larger than window width, or smaller than 320, will be clamped to that range, and all values will be rounded to a multiple of 8. If 0, the window width will be used. Note that values that divide evenly into the window width will make the text look nicest. - scr_menuscale. default 1. Sets the scale factor for menus and other centered overlays. If 1, images will be drawn at 1:1 scale. If 2, images will be double size. Menus will never be drawn smaller than 1:1, and never larger than the size of the window. Note that integer values will make the text look nicest. - scr_sbarscale. default 1. Sets the scale factor for the statusbar. If 1, images will be drawn at 1:1 scale. If 2, images will be double size. The statusbar will never be drawn smaller than 1:1, and never larger than the width of the window. Note that integer values will make the text look nicest. - host_maxfps. default 72. sets the maximum frames per second fitzquake will render (also the maximum number of server frames per second.) Clamped to the range 10 - 1000. Set to 72 to mimic standard quake. - r_quadparticles. default 1. If 1, particles are drawn as GL_QUADS instead of GL_TRIANGLES. Quads use 4 verts instead of 3, but the fillrate cost is 1/2 that of triangles. Depending on your card, either one may be faster. This cvar has no effect on the appearance of particles. - r_speeds. default 0. Values of 1 and 2 will give you increasing amounts of information. When you see two numbers separated by a slash, the first number is polys, and the second number is passes. commands: - gl_info. Displays opengl info which was previously displayed during initialization: vendor, renderer, version, and extensions - imagelist. Displays a list of loaded textures, and their dimentions. - gl_texturemode. Now accepts a number (1 through 6) as well as the name (gl_nearest, etc.) - gl_describetexturemodes. Lists all texturemodes. Changes in 0.65 --------------- - gamma cvar now supported. (see cvar "gamma") - fullbrights on models now supported. - odd-sized world textures are now bilinearly resampled (instead of glquake's nearest pixel resample) - removed all fixed-size buffers for loading textures; now the only limit is the size of your memory heap (textures will still be downsampled if they are bigger than the hardware can handle) - styled lights (torch flicker, etc.) can now be disabled (see cvar "r_flatlightstyles") - sky now uses multitexture if available. - centerprints are now optionally logged to the console (see cvar "con_logcenterprint") - number of savegame slots increased to 20 (from 12) - if a map title is longer than 22 characters, it scrolls marquee-style in the statusbar. - gl_flashblend defaults to 1 again. (consistency with glquake) - gl_ztrick defaults to 1 again. (consistency with glquake) - command line gamma now defaults to 1.0 for all cards. (consistency with glquake, plus hardware gamma is better) - now checks hardware for maximum texture size. Users of later voodoo cards should be able to see large textures now. (see cvar "gl_max_size") - now shows AM/PM when scr_clock is 2 - fixed crash when starting dedicated server - fixed crash when loading too many textures. (now it throws a sys_error and quits) - fixed fitzquake-specific crash when player goes near water in a demo playback. - scr_conalpha now actually works. - keypad enter in the console works again. - optional 2x overbrightening on models. (see cvar "gl_overbright_models") - optional quake2-style noclip. (see cvar "sv_altnoclip") - new icon cvars: - con_logcenterprint: If 1, centerprint messages will be logged to the console in sp/coop. If 2, they will also be logged in deathmatch. Default 1. - gamma: Brighten or darken the screen to compensate for differences between monitors. This is now supported by using your video card's gamma support. Just like in winquake, values less than 1 are brighter and values greater than one are darker. Default 1. Notes: I have added special code so that this will work on 3dfx cards too, but i have no way to check that it works. If fitzquake crashes, your hardware gamma may be stuck at the wrong value. You can use the "display settings" control panel fix this, or you could try lordhavoc's useful "setgamma" utility (available on this page.) Also note that if texture-brightening gamma has been requested at the command line (fitzquake.exe -gamma ), harware gamma will not be used and the gamma cvar will have no effect. - gl_max_size: Now defaults to 0. If 0, textures will be as large as the hardware allows. Positive values can be used to impose a limit smaller than the hardware's reported maximum. - gl_overbright_models: If 1, models will be rendered using 2x overbrightening and will appear at roughly the same brightness as they do in software quake, which is noticably brighter than the default fitzquake / glquake appearance. I disabled this cvar by default becuase this is still a poorly supported feature -- it currently takes 2 or 3 passes to render a model when this feature is enabled, compared to only 1 pass when it is disabled. Default 0. - r_flatlightstyles: If 1, styled lights (torch flicker, etc.) will be displayed as a steady light. If 2, the peak intensity will be used instead of the average intensity. Default 0. - sv_altnoclip: If 1, enable the alternative noclip movement which resembles quake2 and is not constrained to the horizontal plane. Set to 0 to retain quake's original noclip behavior. Default 1. Changes in 0.60 --------------- - graphics: - better sky projection, configurable for performance - menus and other overlays are now centered on the screen, and can optionally stretch to fit resolution. - all 2d graphics now obey gl_texturemode and all texturemode changes take effect immediately - underwater warping now resembles quake3's perspective munging, and obeys r_waterwarp - fixed frustum culling b0rked (giving HOM at certain FOV / vidsize / screensize combinations) - fixed lack of support for VP_PARALLEL_ORIENTED, VP_PARALLEL_UPRIGHT, and FACING_UPRIGHT sprites - fixed fullbrights not displayed on world/bmodels - fixed texture cache mismatch error - fixed pink edges on sprites, menu items, etc - fixed particle z-buffer bug (apparent when a particle is in front of a water surface) - fixed ugly resampling of non-power of two gfx, skins, sprites - fixed large models (shamblers, shub) dissapearing near edge of screen - fixed statusbar not visible when gl_clear = 1 - fixed r_fullbright change not immedate when gl_texsort = 0 - fixed alias models still dark when r_fullbright = 1 - fixed HOM when screen is partially underwater - fixed gunshot decals not showing up on some walls - sky and water warp now freeze when you pause, as well as lightning bolts - r_novis changes now take effect immediately, rather than when you cross a leaf boundary - removed mirror code - two particle styles, circle and square (cvar controlled) - console: - improved tab completion: - hitting tab once will display a list of possible matches and complete the line using the first match - hitting tab or shift-tab will cycle through the matches - autocomplete will now match against aliases as well as commands and cvars. - autocomplete will now complete later in the string (e.g. "bind mouse1 +att" + tab will complete '+attack') - autocomplete will even complete inside the string (e.g. "bind m +attack" + tab will complete 'mouse1' if the cursor is right after the 'm') - new key functionality: - tab -- autocomplete, cycle through multiple matches - shift-tab -- cycle backwards through multiple matches - ins -- toggle insertmode - del -- delete current character - backspace -- delete previous character - ctrl-v -- paste from windows clippboard - leftarrow -- move cursor one character left in line - rightarrow -- move cursor one character right in line. or, get one character from the previous command. - home -- move cursor to beginning of line - end -- move cursor to end of line - ctrl-pgup -- scroll up a screen at a time - ctrl-pgdn -- scroll down a screen at a time - ctrl-home -- scroll to top of console history - ctrl-end -- scroll to bottom of console history - carets indicate that you are scrolled back, like in quakeworld/quake2 - left arrow, right arrow, pgup, pgdn keys now auto-repeat - quadrupled the length of the console history - commands: - "game " to load a mod. - "reset " to reset a cvar to default. Note that this is the engine default, not the default.cfg value - resetall. resets all cvars. - mods. lists all child folders of quake directory which contain either a progs.dat or a pak file - maps. lists all addon levels available (i.e. all levels that are not in id1/*.pak) - mapname. outputs the short name of the current level (e.g. "e1m5") - cmdlist. alphabetized. 'cmdlist blah' will list only cmds that start with 'blah' - cvarlist. alphabetized. 'cvarlist blah' will list only cvars that start with 'blah' - "inc [amount]" to increment a cvar by amount. amount defaults to 1. - "toggle " to invert the value of a cvar (nonzero -> 0 and 0 -> 1) - god, noclip, notarget, and fly can now be explicitly set. example: "god 0" will disable god mode - viewpos. outputs (X,Y,Z) pitch yaw roll - "give a " now gives you armour. type depends on value - bindlist. lists all key bindings - "alias " now outputs the current command string - "unalias " to delete an alias - unaliasall. delete all aliases. - condump. dumps console to condump.txt - fog. set global fog. syntax is "fog ", "fog ", or "fog " See section on modding for details. Set density to 0 to disable fog. - "sky " to load a skybox. if skyname is "", this will turn off skybox rendering. - stuffcmds now parses the "cmdline" cvar rather than the args actually passed to the program. This is useful for loading mods dynamically, so you can edit the cmdline before execing quake.rc (which calls "stuffcmds") - cvars: - scr_stretch_menus. default 1. if 1, menus and other overlays are stretched to fill the screen - scr_conalpha. default 1. This is the opacity when the console is halfscreen. 0.6 will mimic glquake - scr_clock. default 0. if 1, game time is displayed. If 2, system time is displayed. - scr_showfps. default 0. if 1, FPS are displayed. - r_waterwarp recognized. default 1. if zero, no underwater warping will take place - r_drawflat recognized. default 0. if 1, polygons will be drawn as a solid color with no lightmap or texture - r_particles. default 1. 0 = no particles, 1 = circular particles, 2 = square particles - r_fastsky. default 0. if 1, sky will be rendered as solid color, for added performance - r_sky_quality. default 8. Higher number divides the sky more, for a smoother effect and slower performance. - r_clearcolor now supported. default 2. specify a palette index from 0 to 255. - gl_fullbrights. default 1. set to 0 to disable fullbrights - gl_farclip. default 8192. set to 4096 to mimic glquake. note that the skybox will be drawn somewhat closer than this value. - cl_maxpitch. default 90 (straight down.) set to 80 to mimic normal quake - cl_minpitch. default -90 (straight up.) set to -70 to mimic normal quake - cl_keypad. default 1. if 0, keypad keys will respond as in quake.exe (for example, pushing 'KP_END' will be the same as pushing '1') - gl_flashblend now defaults to 0. - gl_ztrick now defaults to 0. - gl_keeptjunctions now defaults to 1. (note, contrary to the name of this cvar, what is being kept is the extra polygon verts which *eliminate* tjunctions. This is a good thing, so i made it default to 1) - chase_alpha. default 1. lower values make the chasecam player model translucent. Buggy. - keyboard - keypad keys are now bindable: KP_NUMLOCK, KP_SLASH, KP_STAR, KP_MINUS, KP_HOME, KP_UPARROW, KP_PGUP, KP_PLUS, KP_LEFTARROW, KP_5, KP_RIGHTARROW, KP_END, KP_DOWNARROW, KP_PGDN, KP_ENTER, KP_INS, KP_DEL - command line: - running with -gamma at the command line will set the gamma. There is still no way to change this value in game. Default is 1.0 for 3dfx cards, 0.7 for all others - if unspecified, -conwidth now defaults to -width - modding extensions: - skyboxes (worldspawn and qc settable), currently only targa and pcx formats accepted - global fog (worldspawn and qc settable) - physics - fixed sliding around while standing on solid entities' bounding boxes (monsters, players, etc) - misc - default heapsize is now 32mb (was 16mb) - default zonesize is now 256k (was 48k) quakespasm-0.93.0/Misc/fs_search_order.patch0000644000000000000000000000220313153013704017511 0ustar rootrootallow plain files to override files inside a PAK file -- Sander van Dijk. Index: Quake/common.c =================================================================== --- Quake/common.c (revision 1476) +++ Quake/common.c (working copy) @@ -2036,13 +2036,6 @@ static void COM_AddGameDirectory (const else path_id = 1U; _add_path: - // add the directory to the search path - search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t)); - search->path_id = path_id; - q_strlcpy (search->filename, com_gamedir, sizeof(search->filename)); - search->next = com_searchpaths; - com_searchpaths = search; - // add any pak files in the format pak0.pak pak1.pak, ... for (i = 0; ; i++) { @@ -2074,6 +2067,13 @@ _add_path: if (!pak) break; } + // add the directory to the search path -- moved here from before the pakX.pak loop -- svdijk. + search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t)); + search->path_id = path_id; + q_strlcpy (search->filename, com_gamedir, sizeof(search->filename)); + search->next = com_searchpaths; + com_searchpaths = search; + if (!been_here && host_parms->userdir != host_parms->basedir) { been_here = true; quakespasm-0.93.0/Misc/mk_header.c0000644000000000000000000000313012765532434015441 0ustar rootroot/* gcc -Wall mk_header.c -o mk_header dumps the bytes of given input to a C header as comma separated hexadecimal values. the output header can be used in a C source like: const char bin[] = { # include "output.h" }; */ #include #include #include int main (int argc, char **argv) { FILE *f; struct stat s; unsigned char *buf, *ptr; const char *output; long i, j; if (argc != 2 && argc != 3) { printf ("Usage: mk_header [output]\n" "Default output file is \"output.h\"\n"); return 1; } if (stat(argv[1], &s) == -1 || ! S_ISREG(s.st_mode) ) { printf ("Couldn't stat %s\n", argv[1]); return 1; } if (s.st_size == 0) { printf ("%s is an empty file\n", argv[1]); return 1; } buf = (unsigned char *) malloc (s.st_size); if (buf == NULL) { printf ("Couldn't malloc %ld bytes\n", (long)s.st_size); return 1; } f = fopen (argv[1], "rb"); if (f == NULL) { free(buf); printf ("Couldn't open %s\n", argv[1]); return 1; } if (fread (buf, 1, s.st_size, f) != (size_t) s.st_size) { fclose (f); free (buf); printf ("Error reading %s\n", argv[1]); return 1; } fclose (f); output = (argc == 3) ? argv[2] : "output.h"; f = fopen (output, "wb"); if (!f) { free (buf); printf ("Couldn't open %s\n", output); return 1; } for (i = 0, j = 0, ptr = buf; i < s.st_size; ++i) { fprintf (f, "0x%02x", *ptr++); if (i == s.st_size - 1) break; fprintf (f, ","); if (++j < 16) fprintf (f, " "); else { j = 0; fprintf (f, "\n"); } } fprintf (f, "\n"); fclose (f); free (buf); return 0; } quakespasm-0.93.0/Misc/fitzquake080.txt0000644000000000000000000011217211364571402016351 0ustar rootroot ================================================================================ Fitzquake version 0.80, May 26, 2005 Filename : fitzquake080.exe Author : John Fitzgibbons Email Address : johnfitz@u.washington.edu Author's Homepage : http://www.celephais.net/ Fitzquake Homepage : http://www.celephais.net/fitzquake Fitzquake is a modified glquake based on the source code released by id Software. My primary focus is fixing a lot of the rendering bugs which made glquake inferior to the software renderer. My secondary focus is adding conveniences for mappers and general users. I am also slowly adding support for new modding or mapping features such as skyboxes, fog, and colored light. While I have made extensive changes to the code, I pretty much use the same OpenGL features that the original glquake uses. Therefore, if you can run glquake, you can probably run Fitzquake. I am not finished working on this project, so bug reports and feature requests are welcome. Acknowlegements -------------------------------------------------------------------------------- id Software (quake and quake2 code) LordHavoc (code and assistance) Bengt Jardrup (feedback, assistance, testing) additional thanks to: Aardappel, SmallPileOfGibs, FrikaC, Vondur, Topaz, Tomaz, Tonik, Radix, EvilTypeGuy, NightBringer, MH, Maddes, Fett, Craig Wills, Heffo, Riot, Gleeb, people in #flipcode, and others for their tutorials, code, testing, and assistance. Copyright / Permissions -------------------------------------------------------------------------------- Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. History ================================================================================ changes in 0.80 --------------- - map loading is about four times faster. This is due to some optimizations in the texture loading code. - video mode can now be changed in-game. You can change resolution, color depth, refresh rate, and switch between windowed and fullscreen modes. This can be accomplished easily using the video options menu, and is also available at the console using cvars. Note: if you launch fitzquake with -width, -height, -window, or -bpp in the command line, Fitzquake will use the command line settings and ignore whatever it reads from the config files at startup. However, you will still be able to change the video mode from the menu or the console as you like. Also note: to change video modes by execing a config file, the config file must include "vid_restart" after the other vid_ cvars have been set. (see cvars "vid_width," "vid_height," "vid_fullscreen," "vid_bpp," "vid_refreshrate," and commands "vid_restart," "vid_test") - added control of entity count limits (MAX_EDICTS.) The default maximum has been increased from 600 to 1024. It can be raised even higher (up to 8192 -- this is a limit set by the network protocol) using a cvar. (see cvar "max_edicts") - added control of vertical sync. (see cvar "vid_vsync") - added control of anistropic filtering. Anisotropic filtering is a method to improve texture clarity on surfaces when viewed at steep angles. (see cvar "gl_texture_anisotropy") - console buffer size can now be set. The command line option "-consize" allows you to specify the buffer size in kilobytes. For reference, fitzquake's default buffer size is 64k, and glquake's is 16k. 16k is also the smallest size fitzquake will allow you to set. Note: this buffer lives in the heap, so if you want to have a HUGE buffer, you might need to increase -heapsize also. - increased max surface extents from 256 (the software quake maximum) to 2000. For reference, the glquake maximum is 512. This should eliminate the "bad surface extents" crash when running some maps that could be played in glquake, but not fitzquake or winquake. - increased max vertices in an alias model from 1024 (the glquake maximum) to 2000 (the software quake maximum.) - increased MAX_CHANNELS from 128 to 512 and MAX_DYNAMIC_CHANNELS from 8 to 128 - screenshot filenames are now in the format "fitz0000.tga", increasing the maximum number of screenshots per folder from one hundred to ten thousand. - vid_describemodes output cleaned up; now displays a list of valid refresh rates for each mode - added a more intuitive cvar control of slow motion/fast-forward for demos and live gameplay. (see cvar "host_timescale") - "reset to defaults" option in the menu now executes the "resetall" command before loading default.cfg - game command now writes config.cfg to current gamedir before switching - +mlook is now saved to config.cfg - changed smallest allowed window size from 320x240 to 320x200 - console cursor images are now hard-coded, and the insert mode cursor is now a vertical bar instead of an underscore. This was prompted by several popular mods (OUM, Xmen TC) using nonstandard cursors, which are incompatible the fitzquake console enhancements. - gl_farclip now defaults to 16384. This should be high enough to handle even the largest open areas without unwanted clipping. The only reason you'd want to lower this number is if you see z-fighting. - added cvar r_drawworld (see below) - added cvar r_showtris (see below) - added cvar r_showbboxes (see below) - added a command to cycle a cvar through a list of values. (see command "cycle") - mapname command now works on dedicated server. - added a hack to fix those white edges on the bottom of the large box of shells. I feel dirty, but they look better, now. - changed stuffcmds behavior to allow hyphenated map names, config file names, etc. in the command line. (example: "fitzquake.exe +exec my-config.cfg +map my-map") However, you still can't load a map or config file when the first character in the filename is a hyphen. - skybox loader now reverts to scrolling sky if all 6 skybox faces failed to load. - tweaked circular particles to match the apparent size of the square particles. - fixed crash when loading mods with large player skins (like the chainsaw mod, and PerQuake.) Colormapping can now handle any size player texture (limited to the heapsize, of course.) - fixed bug where a replacement model in a mod is messed up becuase a matching .ms2 file in id1/glquake is present. The fix is simply to disable all mesh caching, which slows down model loading a little. - fixed freeze when gl_overbright is 1 and either texture_env_combine or multitexture is disabled or not supported. - fixed "numgltextures == MAX_GLTEXTURES" crash when viewing multiplayer->setup menu. - fixed "numgltextures == MAX_GLTEXTURES" crash when playing large maps (nesp09,) due to frequent model recaches. - fixed bug where entities and water were not being drawn any frame in which a model had to be recached. - fixed skybox texture showing up in the place of other textures/skins/lightmaps, when the previous loaded map had a skybox. - fixed missing polygons on edges of screen when underwater and r_waterwarp is 1. - fixed missing polygons on edges of screen when r_stereo is enabled. - fixed long mapnames that scroll are truncated to be shorter than what appears in the console. Well, sort of. What I did was increase the maximum length from 39 to 127. A mapname longer than 127 will still be truncated, but these are pretty rare. - fixed rogue's teamplay skin showing up as an all-white texture. - fixed checkerboard texture shows up sometimes when an animated texture has fullbright pixels on some frames, but not others (reactor core in junk.bsp) - fixed color 255 is not fullbright - fixed fuscia dots appearing in corners of resampled textures - fixed mapname command crashes when client is disconnected. - fixed cl_nolerp 1 screws up speed of demo playback - fixed annoying client-side step-up smoothing when in noclip mode (except on nonlocal servers.) - fixed scr_clock 2 displays hour twelve as "0" instead of "12" - fixed skip textured surfaces still drawn even after running tyrann's skip utility - fixed crash when loading a map with a sky texture where the "sky" in the texturename is not lowercase - fixed hang in e2m2 on easy skill, where you can shoot one of the buttons at the beginning and then trick-jump through the open gate. Now it prints a warning message ("SV_TouchLinks: null link") and lets you keep playing. - fixed bug when viewsize < 100, and r_oldwater is 0, where you can see the water textures overlaying part of the brown frame around the viewport. - fixed bug where imagelist and r_speeds2 would report the same megabyte counts in both 16bpp and 32bpp mode, even though texture bpp should match (and does match) framebuffer bpp. The numbers are now different and accurate for each bit depth. - removed cvars "vid_config_x," "vid_config_y," "vid_wait," "vid_nopageflip," "_vid_wait_override," "_vid_default_mode," "_vid_default_mode_win," and "vid_stretch_by_2," none of which glquake ever used. - removed cvar "vid_mode" and commands "vid_describemode" and "vid_nummodes," because of the new video mode handling. cvars: - gl_texture_anisotropy. Controls the amount of anisotropy in texture filtering. If 1 or less than 1, texture filtering is normal (isotropic.) If greater than 1, increasing degrees of anisotropic filtering are used, up to the hardware maximum. Set value to 2 for 2x anisotropic, 4 for 4x, etc. Default 1. - host_timescale. Scales the passage of time on client and server. Set to 0 or 1 for normal speed, less than 1 for slow motion, and greater than 1 for fast-forward. If greater than 0, overrides host_framerate. Default 0. - max_edicts. Sets the maximum number of entites on both the client and server. Default 1024. Acceptable values range from 256 to 8192. Set to 600 to mimic standard quake. Changes won't take effect until the next time a map is loaded. Note: if a client connects to a server, and the client's maximum is lower than the server's, the client will probably crash if it ever sees an entnum higher than its local max_edicts. Warning: you may need to increase -heapsize if you want a high max_edicts value. - r_drawworld. If 1, draw world as usual. If 0, don't draw the world. Default 1. (compare r_drawentities) - r_showbboxes. If 1, draw a wireframe bounding box around each entity. Note that these are the server-side per-edict physics bounding boxes, not the client-side per-model rendering bboxes. If 0, disable this feature. Default 0. - r_showtris. If 1, draw wireframe outlines for every triangle in the scene. Like in Quake 3, the lines will be visible even through solid geometry. If 2, draw the outlines only on visible surfaces (like r_showtris 2 in Medal of Honor.) If 0, disable wireframe overlay. Default 0. - vid_bpp. Sets the color depth of the screen in fullscreen mode. Windowed mode ignores this setting. Can be 16 or 32. Default 16. (Changes won't take effect until the next call to vid_restart.) - vid_fullscreen. If 1, fitzquake will run fullscreen. If 0, fitzquake will run in a window. Default 1. (Changes won't take effect until the next call to vid_restart.) - vid_height. Sets the vertical screen/window resolution. Default 480. In windowed mode, cannot be less than 200. (Changes won't take effect until the next call to vid_restart.) - vid_refreshrate. Sets the refresh rate of the screen in fullscreen mode. Windowed mode ignores this setting. Possible values include 60, 70, 72, 75, 85, etc. Default 60. (Changes won't take effect until the next call to vid_restart.) - vid_width. Sets the horizontal screen/window resolution. Default 640. In windowed mode, cannot be less than 320. (Changes won't take effect until the next call to vid_restart.) - vid_vsync. Set to 1 to enable vertical sync, which eliminates tearing, but caps your framerate at the monitor refreshrate. Set to 0 to disable vertical sync, which allows tearing but doesn't cap your framerate. Default 0. Note: If fitzquake detects that your driver settings have forced vsync to be disabled, it will post a message to the console saying so, and this cvar will have no effect. commands: - "cycle [ [ ...]]" to cycle a cvar through a list of values.. Note: this command will get stuck on a list that contains the same value more than once, such as "cycle blah 1 2 1 3". If you're doing anything that complex you can just use aliases. - vid_restart. Tries to set a video mode that matches the values of vid_width, vid_height, vid_fullscreen, and, if vid_fullscreen is 1, vid_bpp and vid_refreshrate. - vid_test. Like vid_restart, except that after switching to the new mode pops up a confirmation dialogue. This is useful if you are not sure what modes your monitor can handle, so you don't get stuck with a blank screen. The dialogue has a time limit so that if you don't press a key within 5 seconds, it will revert to the previous mode. changes in 0.75 --------------- - totally rewritten bsp drawing code. The new code combines the advantages of the gl_texsort 1 and gl_texsort 0 codepaths from glquake into one codepath that uses texture sorting and multitexture. In my tests, i've found that it's about the same speed as glquake in low poly scenes (like the original quake levels,) but as you get into the thousands of wpolys, it's faster and faster. - 2x overbright lighting. Lighting now looks like software quake. Just like overbright lighting on models, overbright on world polys requires GL_EXT_texture_env_combine (TNT and later, voodoo4 and later.) Without it, FitzQuake will be use two passes to render overbright world polys. So if you don't have that extension, you might disable it for performance reasons. (see cvar "gl_overbright") - colored lighting support using .lit files. - totally new water surface animation. The new method requires no surface subdivision, isn't plagued by tearing and sparklies caused by tjunctions, and looks almost identical to software quake's water, and doesn't slow down on very large sheets of water. (see cvars "r_waterquality" and "r_oldwater") - old water aninmation fixed so that it doesn't look bad when gl_subdivide_size is 32 or lower. - can load external textures (currently targa and pcx) if they match the texture name in the bsp and are located in the id1/textures, or /textures directory. At the moment no other images (skins, menu, etc.) can be replaced. - gamma correction now goes back to normal when Fitzquake loses focus. - tab completion now adds a space after the completed command/cvar if the cursor is at the end of the editline. - increased the max length of the video mode list from 30 to 80. This should alleviate the problems people with newer cards were having trying to use some 32-bit modes -- there were so many 16-bit modes that they filled up the list before all 32-bit modes could be detected. The video menu will still only list a certain number of modes due to space constraints, but you can see the complete list by using the console command "vid_describemodes" - number of listed video modes in the "video options" menu increased from 27 to 36. - added alpha control for the front sky layer. (see cvar "r_skyalpha") - clock can now display time in 24-hour or "military" time. - added an "mtex" counter to the r_speeds 2 readout. This measures the number of megabytes of texture memory used each frame to draw the scene. Note: this doesn't count textures used to draw the console, menu, or statusbar. - added optional drawing of surfaces inside sky leaves, for compiler/map testing purposes. (see cvar "r_oldskyleaf") - sky surfaces on bmodels are now visible, though drawn incorrectly. - fixed multiple textures in bsp with same name or no name get overwritten/not loaded. (example: rd1m3) - fixed some nasty texture loading bugs that were especially hounding 3dfx users (wrong texture, no texture, or unnecessarily low-res textures displayed on models and world.) These bugs would also occur in conjunction with nonzero values of gl_max_size or gl_picmip. - fixed bug where changing gl_max_size or gl_picmip in-game would screw up alias model texture coordinates. - fixed bug where if -gamma is specified at the command line, the "gamma" cvar would be ignored. - fixed model more than 2048 units above floor us unlit by static lighting. - fixed crash with con_logcenterprint when centerprint message was too long. - fixed crash when changing to a nonexistant gamedir and then trying to write a file (screenshot, etc.) Fitzquake now creates the directory as needed. - fixed "bad surface extents" error when sky or water surface is missing from bsp file. - fixed alias model shadows write to z-buffer. - fixed underwater intermission camera has warp, but no screenblend. - fixed console buffer still scrolled back after using the "clear" command. - fixed console command history (the list of previous commands) not being rewound after toggling the console. - removed cvar gl_texsort. New bsp drawing code always sorts by texture. - removed cvar gl_ztrick. The depth buffer is now cleared every frame. - removed cvar gl_keeptjunctions. Extra verts created by qbsp to eliminate tjunctions are now always kept. - removed "sliding on monsters' heads" fix, becuase I decided I don't like the idea of changing gameplay, even if the original behaviour is clearly a bug. Since this bug can be fixed in quakec also, I feel safer leaving it as it was. cvars: - gl_overbright. default 1. This variable controls overbright lighting on the world polygons. (For lighting on models, use gl_overbright_models.) If 1, overbright will be enabled and lighting will resemble software Quake. If 0, overbright will be disabled and lighting will resemble GLQuake. - r_oldskyleaf. default 0. If 0, surfaces inside sky leaves will be skipped by the renderer. If 1, they will be drawn whenever they are in your PVS, just like any other surface. - r_oldwater. default 1. If 1, use the old GLQuake method of subdividing the water surface to enable a warping animation. If 0, use the new render-to-texture method. Note: in general, r_oldwater 0 looks better and runs slower. So experiment to see if the performance hit is acceptable to you. - r_skyalpha. default 1. Sets the alpha of the front sky layer. Note that if sky alpha is less than 1.0, the sky will be drawn in two passes even if you have multitexture. - r_waterquality. default 8. Sets the quality of the water when r_oldwater is 0. Can be anywhere from 1 to 64. Lower values give better performance, higher values look better. A value of 4 will provide water that looks at least as good as glquake can get, and 32 is close enough to software quake for all but the most picky. To control the quality of water when r_oldwater is 1, use gl_subdivide_size instead. - scr_clock. default 0. If 1, game time is displayed. If 2, system time is displayed in 12-hour format. If 3, system time will be displayed in 24-hour or "military" time. Changes in 0.70 --------------- - added anaglyph stereo rendering. Note that this will cut your framerate in half, as it is rendering the scene once for each eye. You might want to turn off r_drawviewmodel as it is hard to focus on becuase it is so close. (see cvars "r_stereo", "r_stereodepth") - now uses ARB_multitexture if present, otherwise tries to use SGIS_multitexture. This should fix various blending bugs on some cards, such as weird cloud layers, all-black models, and inverted torches. (only ARB guarantees that GL_DECAL blending will work) - overbright models now uses GL_EXT_texture_env_combine if supported (TNT and later, voodoo4 and later,) which saves one or two passes on model rendering when gl_overbright_models is 1. The command line option -nocombine will disable this. - gl_overbright_models now defaults to 1 - custom palettes are now correctly loaded when you use the "game" command. - RecursiveLightPoint is now lerped for smoother lighting of slow-moving models. Check out the ogre in e3m3 for an example. - dynamic lighting (rockets, etc) has been moderately sped up. (some lightmaps were being uploaded even though they weren't actually touched by a dynamic light.) - overhauled 2d drawing to allow independent scaling of console, menu, and sbar (see cvars "scr_conwdith," "scr_menuscale," and "scr_sbarscale") command line switches "-conwidth" and "-conheight" removed. cvar "scr_stretch_menus" removed. - user control of max framerate. (see cvar "host_maxfps") - improved the accuracy of the FPS counter a bit, but it still seems suspect. - particles can now be drawn as quads or triangles. (see cvar "r_quadparticles") - opengl information (vendor, renderer, version, extensions) is no longer printed during initialization. (see command "gl_info") - rewrote texture management. Now instead of quake's memory usage going up and up forever (becuase textures were uploaded to the opengl.dll and never freed,) all textures will get flushed when you switch games, bringing you back down to where you were when fitzquake first launched. - gl_texturemode command will now accept a number (1 through 6) as well as the name (gl_linear_mipmap_nearest, etc.) - gl_describetexturemodes will list all texturemodes. - the inside of sky leaves are no longer rendered -- so when noclipping inside them, it will look the same as noclipping inside a solid wall. This does not affect gameplay. - cleaned up intermission display in singleplayer -- no more overlapping numbers - r_speeds readout modified a bit. (see cvar "r_speeds") - r_sky_quality now defaults to 12 instead of 8, since skies are now drawn in 1 pass (with multitexture at least) - gl_texsort now always defaults to 0. (it used to be forced to 1 when multitexture was unavailable) - fixed bug where models would actually darken when dlights got bright enough - fixed deathmatch, coop cvars not reset to zero when starting a new game from the single player menu - fixed r_lightmap doesn't work when gl_texsort = 0. - fixed inverted lightmaps / no textures / no dynamic light bug when multitexture is disabled and gl_texsort = 0 - fixed scrolling map title in wrong place when width does not equal conwidth - fixed a few color borking problems in 16-bit mode. The front sky layer, sprites, and pics with transparency will still look a bit off (as in glquake,) but at least the console image and statusbar background look better. Though it isn't perfect, it should once again look like what you expect from glquake in 16bit. - fixed pixel gap on both sides of the console in 1024x768 - fixed wpoly count being slightly lower when gl_texsort = 0 (the count was correct when gl_texsort = 1) - fixed lmap count always zero when gl_texsort = 1 - dlight fans are now drawn after water, so that it looks right at least when wateralpha is 1. - fixed a possible bug with older 3dfx cards (voodoo 1/2/rush) where the gamma cvar might not work (untested) - fixed old commands showing up in the console prompt after hitting 'enter' - increased MAX_HANDLES so that you should never see the "out of handles" error message. - removed cvar "chase_alpha." the transparent player model was buggy and didn't work well in a lot of conditions. - removed cvar "gl_doubleeys" (yes, that is the correct misspelling) With overbright models, eyes can be seen as easily as in software mode. - removed cvars "cl_crossx," "cl_crossy," "lcd_x," "lcd_yaw," and "gl_reporttjunctions" which didn't do anything (in glquake, at least). - removed support for GL_EXT_shared_texture_palette (special 8-bit texture format) - removed VCR code. command line switches "-record" and "-playback" no longer supported. - removed support for command line switch "-gamma" -- just use the gamma cvar, or idgamma or something. cvars: - r_stereo. default 0. If nonzero, the scene will be rendered once in red, and again in cyan, with the two views shifted slightly. If you have 3D glasses you can appreciate this (assumes that the left eye is red and the right eye is cyan.) The value of r_stereo sets the eye separation. If your glasses have red on the right eye, use a negative value to flip the views. - r_stereodepth. default 128. Sets the distance at which the two views will converge when stereo rendering is active. - scr_conwdith. default 0. Sets the virtual console width, where smaller numbers means larger text. Values larger than window width, or smaller than 320, will be clamped to that range, and all values will be rounded to a multiple of 8. If 0, the window width will be used. Note that values that divide evenly into the window width will make the text look nicest. - scr_menuscale. default 1. Sets the scale factor for menus and other centered overlays. If 1, images will be drawn at 1:1 scale. If 2, images will be double size. Menus will never be drawn smaller than 1:1, and never larger than the size of the window. Note that integer values will make the text look nicest. - scr_sbarscale. default 1. Sets the scale factor for the statusbar. If 1, images will be drawn at 1:1 scale. If 2, images will be double size. The statusbar will never be drawn smaller than 1:1, and never larger than the width of the window. Note that integer values will make the text look nicest. - host_maxfps. default 72. sets the maximum frames per second fitzquake will render (also the maximum number of server frames per second.) Clamped to the range 10 - 1000. Set to 72 to mimic standard quake. - r_quadparticles. default 1. If 1, particles are drawn as GL_QUADS instead of GL_TRIANGLES. Quads use 4 verts instead of 3, but the fillrate cost is 1/2 that of triangles. Depending on your card, either one may be faster. This cvar has no effect on the appearance of particles. - r_speeds. default 0. Values of 1 and 2 will give you increasing amounts of information. When you see two numbers separated by a slash, the first number is polys, and the second number is passes. commands: - gl_info. Displays opengl info which was previously displayed during initialization: vendor, renderer, version, and extensions - imagelist. Displays a list of loaded textures, and their dimentions. - gl_texturemode. Now accepts a number (1 through 6) as well as the name (gl_nearest, etc.) - gl_describetexturemodes. Lists all texturemodes. Changes in 0.65 --------------- - gamma cvar now supported. (see cvar "gamma") - fullbrights on models now supported. - odd-sized world textures are now bilinearly resampled (instead of glquake's nearest pixel resample) - removed all fixed-size buffers for loading textures; now the only limit is the size of your memory heap (textures will still be downsampled if they are bigger than the hardware can handle) - styled lights (torch flicker, etc.) can now be disabled (see cvar "r_flatlightstyles") - sky now uses multitexture if available. - centerprints are now optionally logged to the console (see cvar "con_logcenterprint") - number of savegame slots increased to 20 (from 12) - if a map title is longer than 22 characters, it scrolls marquee-style in the statusbar. - gl_flashblend defaults to 1 again. (consistency with glquake) - gl_ztrick defaults to 1 again. (consistency with glquake) - command line gamma now defaults to 1.0 for all cards. (consistency with glquake, plus hardware gamma is better) - now checks hardware for maximum texture size. Users of later voodoo cards should be able to see large textures now. (see cvar "gl_max_size") - now shows AM/PM when scr_clock is 2 - fixed crash when starting dedicated server - fixed crash when loading too many textures. (now it throws a sys_error and quits) - fixed fitzquake-specific crash when player goes near water in a demo playback. - scr_conalpha now actually works. - keypad enter in the console works again. - optional 2x overbrightening on models. (see cvar "gl_overbright_models") - optional quake2-style noclip. (see cvar "sv_altnoclip") - new icon cvars: - con_logcenterprint: If 1, centerprint messages will be logged to the console in sp/coop. If 2, they will also be logged in deathmatch. Default 1. - gamma: Brighten or darken the screen to compensate for differences between monitors. This is now supported by using your video card's gamma support. Just like in winquake, values less than 1 are brighter and values greater than one are darker. Default 1. Notes: I have added special code so that this will work on 3dfx cards too, but i have no way to check that it works. If fitzquake crashes, your hardware gamma may be stuck at the wrong value. You can use the "display settings" control panel fix this, or you could try lordhavoc's useful "setgamma" utility (available on this page.) Also note that if texture-brightening gamma has been requested at the command line (fitzquake.exe -gamma ), harware gamma will not be used and the gamma cvar will have no effect. - gl_max_size: Now defaults to 0. If 0, textures will be as large as the hardware allows. Positive values can be used to impose a limit smaller than the hardware's reported maximum. - gl_overbright_models: If 1, models will be rendered using 2x overbrightening and will appear at roughly the same brightness as they do in software quake, which is noticably brighter than the default fitzquake / glquake appearance. I disabled this cvar by default becuase this is still a poorly supported feature -- it currently takes 2 or 3 passes to render a model when this feature is enabled, compared to only 1 pass when it is disabled. Default 0. - r_flatlightstyles: If 1, styled lights (torch flicker, etc.) will be displayed as a steady light. If 2, the peak intensity will be used instead of the average intensity. Default 0. - sv_altnoclip: If 1, enable the alternative noclip movement which resembles quake2 and is not constrained to the horizontal plane. Set to 0 to retain quake's original noclip behavior. Default 1. Changes in 0.60 --------------- - graphics: - better sky projection, configurable for performance - menus and other overlays are now centered on the screen, and can optionally stretch to fit resolution. - all 2d graphics now obey gl_texturemode and all texturemode changes take effect immediately - underwater warping now resembles quake3's perspective munging, and obeys r_waterwarp - fixed frustum culling b0rked (giving HOM at certain FOV / vidsize / screensize combinations) - fixed lack of support for VP_PARALLEL_ORIENTED, VP_PARALLEL_UPRIGHT, and FACING_UPRIGHT sprites - fixed fullbrights not displayed on world/bmodels - fixed texture cache mismatch error - fixed pink edges on sprites, menu items, etc - fixed particle z-buffer bug (apparent when a particle is in front of a water surface) - fixed ugly resampling of non-power of two gfx, skins, sprites - fixed large models (shamblers, shub) dissapearing near edge of screen - fixed statusbar not visible when gl_clear = 1 - fixed r_fullbright change not immedate when gl_texsort = 0 - fixed alias models still dark when r_fullbright = 1 - fixed HOM when screen is partially underwater - fixed gunshot decals not showing up on some walls - sky and water warp now freeze when you pause, as well as lightning bolts - r_novis changes now take effect immediately, rather than when you cross a leaf boundary - removed mirror code - two particle styles, circle and square (cvar controlled) - console: - improved tab completion: - hitting tab once will display a list of possible matches and complete the line using the first match - hitting tab or shift-tab will cycle through the matches - autocomplete will now match against aliases as well as commands and cvars. - autocomplete will now complete later in the string (e.g. "bind mouse1 +att" + tab will complete '+attack') - autocomplete will even complete inside the string (e.g. "bind m +attack" + tab will complete 'mouse1' if the cursor is right after the 'm') - new key functionality: - tab -- autocomplete, cycle through multiple matches - shift-tab -- cycle backwards through multiple matches - ins -- toggle insertmode - del -- delete current character - backspace -- delete previous character - ctrl-v -- paste from windows clippboard - leftarrow -- move cursor one character left in line - rightarrow -- move cursor one character right in line. or, get one character from the previous command. - home -- move cursor to beginning of line - end -- move cursor to end of line - ctrl-pgup -- scroll up a screen at a time - ctrl-pgdn -- scroll down a screen at a time - ctrl-home -- scroll to top of console history - ctrl-end -- scroll to bottom of console history - carets indicate that you are scrolled back, like in quakeworld/quake2 - left arrow, right arrow, pgup, pgdn keys now auto-repeat - quadrupled the length of the console history - commands: - "game " to load a mod. - "reset " to reset a cvar to default. Note that this is the engine default, not the default.cfg value - resetall. resets all cvars. - mods. lists all child folders of quake directory which contain either a progs.dat or a pak file - maps. lists all addon levels available (i.e. all levels that are not in id1/*.pak) - mapname. outputs the short name of the current level (e.g. "e1m5") - cmdlist. alphabetized. 'cmdlist blah' will list only cmds that start with 'blah' - cvarlist. alphabetized. 'cvarlist blah' will list only cvars that start with 'blah' - "inc [amount]" to increment a cvar by amount. amount defaults to 1. - "toggle " to invert the value of a cvar (nonzero -> 0 and 0 -> 1) - god, noclip, notarget, and fly can now be explicitly set. example: "god 0" will disable god mode - viewpos. outputs (X,Y,Z) pitch yaw roll - "give a " now gives you armour. type depends on value - bindlist. lists all key bindings - "alias " now outputs the current command string - "unalias " to delete an alias - unaliasall. delete all aliases. - condump. dumps console to condump.txt - fog. set global fog. syntax is "fog ", "fog ", or "fog " See section on modding for details. Set density to 0 to disable fog. - "sky " to load a skybox. if skyname is "", this will turn off skybox rendering. - stuffcmds now parses the "cmdline" cvar rather than the args actually passed to the program. This is useful for loading mods dynamically, so you can edit the cmdline before execing quake.rc (which calls "stuffcmds") - cvars: - scr_stretch_menus. default 1. if 1, menus and other overlays are stretched to fill the screen - scr_conalpha. default 1. This is the opacity when the console is halfscreen. 0.6 will mimic glquake - scr_clock. default 0. if 1, game time is displayed. If 2, system time is displayed. - scr_showfps. default 0. if 1, FPS are displayed. - r_waterwarp recognized. default 1. if zero, no underwater warping will take place - r_drawflat recognized. default 0. if 1, polygons will be drawn as a solid color with no lightmap or texture - r_particles. default 1. 0 = no particles, 1 = circular particles, 2 = square particles - r_fastsky. default 0. if 1, sky will be rendered as solid color, for added performance - r_sky_quality. default 8. Higher number divides the sky more, for a smoother effect and slower performance. - r_clearcolor now supported. default 2. specify a palette index from 0 to 255. - gl_fullbrights. default 1. set to 0 to disable fullbrights - gl_farclip. default 8192. set to 4096 to mimic glquake. note that the skybox will be drawn somewhat closer than this value. - cl_maxpitch. default 90 (straight down.) set to 80 to mimic normal quake - cl_minpitch. default -90 (straight up.) set to -70 to mimic normal quake - cl_keypad. default 1. if 0, keypad keys will respond as in quake.exe (for example, pushing 'KP_END' will be the same as pushing '1') - gl_flashblend now defaults to 0. - gl_ztrick now defaults to 0. - gl_keeptjunctions now defaults to 1. (note, contrary to the name of this cvar, what is being kept is the extra polygon verts which *eliminate* tjunctions. This is a good thing, so i made it default to 1) - chase_alpha. default 1. lower values make the chasecam player model translucent. Buggy. - keyboard - keypad keys are now bindable: KP_NUMLOCK, KP_SLASH, KP_STAR, KP_MINUS, KP_HOME, KP_UPARROW, KP_PGUP, KP_PLUS, KP_LEFTARROW, KP_5, KP_RIGHTARROW, KP_END, KP_DOWNARROW, KP_PGDN, KP_ENTER, KP_INS, KP_DEL - command line: - running with -gamma at the command line will set the gamma. There is still no way to change this value in game. Default is 1.0 for 3dfx cards, 0.7 for all others - if unspecified, -conwidth now defaults to -width - modding extensions: - skyboxes (worldspawn and qc settable), currently only targa and pcx formats accepted - global fog (worldspawn and qc settable) - physics - fixed sliding around while standing on solid entities' bounding boxes (monsters, players, etc) - misc - default heapsize is now 32mb (was 16mb) - default zonesize is now 256k (was 48k) quakespasm-0.93.0/Misc/qs_pak/0000755000000000000000000000000013204512422014617 5ustar rootrootquakespasm-0.93.0/Misc/qs_pak/Makefile0000644000000000000000000000040612425501423016262 0ustar rootrootINPUT := gfx/conback.lmp \ maps/e1m1.ent \ maps/e1m2.ent \ maps/e1m4.ent \ maps/e2m2.ent \ maps/e2m3.ent \ maps/e2m7.ent \ default.cfg OUTPUT := quakespasm.pak $(OUTPUT): $(INPUT) ./mkpak.sh $(INPUT) > $(OUTPUT) .PHONY: clean clean: rm -f $(OUTPUT) quakespasm-0.93.0/Misc/qs_pak/maps/0000755000000000000000000000000013204512422015557 5ustar rootrootquakespasm-0.93.0/Misc/qs_pak/maps/e2m7.ent.orig0000644000000000000000000014250612403131422020005 0ustar rootroot{ "wad" "gfx/tim.wad" "classname" "worldspawn" "sounds" "7" "worldtype" "0" "message" "the Underearth" } { "angle" "90" "origin" "1136 -1100 -72" "classname" "info_player_start" } { "origin" "1184 -776 -152" "classname" "light" "light" "150" } { "classname" "light" "origin" "1704 -584 -184" "light" "150" } { "classname" "light" "origin" "1640 -688 -184" "light" "150" } { "classname" "light" "origin" "1696 -888 -192" "light" "150" } { "classname" "light" "origin" "1088 -960 -152" "light" "150" } { "classname" "light" "origin" "1248 -960 -152" "light" "150" } { "classname" "light" "origin" "1016 -768 -152" "light" "100" } { "classname" "light" "origin" "896 -920 -152" "light" "150" } { "light" "100" "origin" "1584 -208 -112" "classname" "light" } { "light" "100" "origin" "1776 -208 -112" "classname" "light" } { "light" "150" "origin" "1584 -88 -112" "classname" "light" } { "origin" "1774 58 -76" "classname" "light_torch_small_walltorch" } { "light" "100" "origin" "1584 -488 -232" "classname" "light" } { "light" "100" "origin" "1768 -480 -232" "classname" "light" } { "light" "150" "origin" "1752 -112 -176" "classname" "light" } { "light" "150" "origin" "1592 -120 -176" "classname" "light" } { "light" "150" "origin" "1592 -248 -192" "classname" "light" } { "light" "200" "origin" "1768 -240 -192" "classname" "light" } { "light" "150" "origin" "1676 -220 -188" "classname" "light" } { "light" "150" "origin" "1672 -40 -136" "classname" "light" } { "light" "150" "origin" "1676 -376 -252" "classname" "light" } { "light" "250" "origin" "1112 952 -92" "classname" "light" } { "light" "200" "origin" "1280 928 -152" "classname" "light" } { "classname" "light" "origin" "704 952 -92" "light" "250" } { "classname" "light" "origin" "824 1112 -92" "light" "200" } { "classname" "light" "origin" "952 760 -92" "light" "200" } { "classname" "light" "origin" "824 760 -92" "light" "200" } { "light" "250" "origin" "952 1112 -92" "classname" "light" } { "classname" "light" "origin" "1128 -848 288" "light" "500" } { "classname" "light" "origin" "1144 -432 288" } { "classname" "light" "origin" "864 -552 272" "light" "200" } { "classname" "light" "origin" "1392 -568 168" "light" "200" } { "classname" "light" "origin" "1416 -592 -24" "light" "150" } { "classname" "light" "origin" "888 -584 -24" "light" "150" } { "classname" "light_torch_small_walltorch" "origin" "1058 -466 -24" "light" "225" } { "origin" "1214 -466 -24" "classname" "light_torch_small_walltorch" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "1198 -66 40" "light" "300" } { "classname" "light" "origin" "1144 -204 172" "light" "150" } { "classname" "light" "origin" "1144 -292 -32" "light" "150" } { "classname" "item_spikes" "origin" "880 -592 -96" "spawnflags" "1" } { "classname" "item_health" "origin" "1068 -944 -96" } { "classname" "light" "origin" "1128 -1084 96" "light" "300" } { "classname" "light" "origin" "888 -848 248" "light" "150" } { "light" "150" "origin" "1504 -896 248" "classname" "light" } { "classname" "light" "origin" "1304 -1048 24" "light" "225" } { "classname" "light" "origin" "1520 -872 -192" "light" "150" } { "light" "100" "origin" "1384 -776 -184" "classname" "light" } { "classname" "light" "origin" "1368 -912 -184" "light" "100" } { "classname" "light" "origin" "1584 496 148" "light" "200" } { "light" "200" "origin" "1488 496 148" "classname" "light" } { "classname" "light" "origin" "1688 164 148" "light" "200" } { "light" "200" "origin" "1352 496 148" "classname" "light" } { "classname" "light" "origin" "1608 496 -36" "light" "200" } { "light" "200" "origin" "1456 496 -36" "classname" "light" } { "classname" "light" "origin" "1692 600 -12" "light" "200" } { "light" "200" "origin" "1274 618 -64" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1274 378 -64" "light" "200" } { "classname" "light" "origin" "1256 496 -40" "light" "200" } { "spawnflags" "2056" "wait" "-1" "classname" "func_door" "angle" "270" "model" "*1" } { "wait" "-1" "spawnflags" "2056" "sounds" "3" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "200" "origin" "1968 1792 -257" "classname" "light" } { "light" "250" "origin" "1880 1792 -105" "classname" "light" } { "classname" "light" "origin" "1976 1944 56" "light" "150" } { "light" "150" "origin" "1992 1624 64" "classname" "light" } { "classname" "light" "origin" "1944 1736 64" "light" "150" } { "light" "150" "origin" "1944 1832 64" "classname" "light" } { "classname" "light" "origin" "1128 496 -24" "light" "250" } { "light" "150" "origin" "1120 632 -24" "classname" "light" } { "light" "200" "origin" "928 544 -24" "classname" "light" } { "classname" "light" "origin" "640 664 -8" "light" "200" } { "light" "200" "origin" "848 672 -8" "classname" "light" } { "classname" "light" "origin" "1024 544 -188" "light" "175" } { "light" "250" "origin" "64 192 136" "classname" "light" } { "classname" "light" "origin" "528 184 136" "light" "250" } { "light" "200" "origin" "72 408 8" "classname" "light" } { "classname" "light" "origin" "80 -48 8" "light" "200" } { "light" "250" "origin" "400 384 80" "classname" "light" } { "classname" "light" "origin" "392 -16 80" "light" "250" } { "classname" "light" "origin" "312 184 -80" "light" "200" } { "light" "200" "origin" "440 184 -80" "classname" "light" } { "classname" "light" "origin" "504 368 -120" "light" "250" } { "light" "150" "origin" "632 192 -120" "classname" "light" } { "classname" "light" "origin" "504 -16 -120" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "774 446 -172" "light" "250" } { "origin" "774 -70 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "light" "250" "origin" "896 -128 152" "classname" "light" } { "classname" "light" "origin" "896 184 152" "light" "250" } { "light" "150" "origin" "656 184 216" "classname" "light" } { "classname" "light" "origin" "304 368 -152" "light" "200" } { "light" "200" "origin" "0 480 -168" "classname" "light" } { "classname" "light" "origin" "96 376 -168" "light" "200" } { "classname" "light" "origin" "16 1480 -96" "light" "200" } { "light" "200" "origin" "1280 1824 -120" "classname" "light" } { "light" "200" "origin" "504 1816 -120" "classname" "light" } { "classname" "light" "origin" "712 1808 -120" "light" "200" } { "light" "200" "origin" "1064 1808 -120" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "858 1950 -172" "light" "250" } { "origin" "658 1950 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "666 1682 -172" "light" "250" } { "origin" "858 1682 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light" "origin" "1248 1384 -32" "light" "200" } { "classname" "light" "origin" "1688 936 -136" "light" "200" } { "light" "250" "origin" "1856 1444 -52" "classname" "light" } { "light" "150" "origin" "1864 1316 -192" "classname" "light" } { "classname" "light" "origin" "1776 1212 -192" "light" "150" } { "light" "150" "origin" "1696 1076 -192" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "1770 730 -64" "light" "250" } { "classname" "light" "origin" "1760 1720 -201" "light" "250" } { "classname" "light" "origin" "1632 1520 -201" "light" "200" } { "classname" "light" "origin" "1984 1504 -201" "light" "200" } { "light" "250" "classname" "light_torch_small_walltorch" "origin" "1874 2104 -300" } { "light" "250" "origin" "1712 2104 -300" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light" "origin" "1792 2048 48" } { "classname" "light_flame_large_yellow" "origin" "1362 1778 0" } { "classname" "light" "origin" "1408 1776 -124" "light" "200" } { "classname" "light" "origin" "1520 1880 -84" "light" "175" } { "light" "175" "origin" "1416 1512 -84" "classname" "light" } { "classname" "light" "origin" "1376 1568 -164" "light" "150" } { "light" "150" "origin" "1416 1888 -164" "classname" "light" } { "classname" "light" "origin" "1544 2072 -164" "light" "150" } { "classname" "light" "origin" "1552 1968 36" "light" "250" } { "classname" "light" "origin" "1416 1968 -16" "light" "175" } { "light" "175" "origin" "1416 2176 -16" "classname" "light" } { "classname" "light" "origin" "1240 2176 -16" "light" "175" } { "light" "175" "origin" "1240 2000 -16" "classname" "light" } { "classname" "light" "origin" "1264 1576 -72" "light" "200" } { "light" "200" "origin" "992 1480 -40" "classname" "light" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "968 1632 -132" } { "light" "200" "origin" "968 1328 -132" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1474 2234 -133" "light" "200" } { "origin" "1182 2234 -133" "classname" "light_torch_small_walltorch" "light" "200" } { "classname" "light" "origin" "1936 1480 48" "light" "150" } { "light" "150" "origin" "1544 1528 64" "classname" "light" } { "classname" "light" "origin" "1472 1488 24" "light" "150" } { "classname" "light" "origin" "1400 1664 96" "light" "150" } { "light" "200" "origin" "1792 2176 -221" "classname" "light" } { "classname" "light" "origin" "1880 2288 -221" "light" "200" } { "light" "200" "origin" "2048 2288 -221" "classname" "light" } { "classname" "light" "origin" "2128 2208 -221" "light" "200" } { "light" "200" "origin" "2160 1992 -205" "classname" "light" } { "origin" "2288 1952 -29" "classname" "light" } { "light" "250" "origin" "2274 1738 -172" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2274 1682 -172" "light" "250" } { "light" "200" "origin" "2376 2184 -152" "classname" "light" } { "light" "200" "origin" "2618 1658 -169" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2618 1368 -169" "light" "200" } { "light" "200" "origin" "2176 1488 -169" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "2298 626 24" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1930 738 24" "light" "200" } { "light" "200" "origin" "2050 394 24" "classname" "light_torch_small_walltorch" } { "classname" "func_plat" "model" "*3" } { "light" "150" "origin" "2152 1784 -312" "classname" "light" } { "classname" "light" "origin" "2232 936 -4" "light" "200" } { "classname" "light" "origin" "2232 1040 48" } { "classname" "light_torch_small_walltorch" "origin" "2034 1034 -164" "light" "200" } { "classname" "light" "origin" "2304 1040 280" "light" "200" } { "light" "200" "origin" "2168 1040 280" "classname" "light" } { "origin" "2130 2452 -112" "classname" "light_flame_large_yellow" "light" "250" } { "classname" "light_flame_large_yellow" "origin" "1858 2452 -112" "light" "250" } { "classname" "light" "origin" "2132 2416 -188" "light" "150" } { "light" "150" "origin" "1860 2416 -188" "classname" "light" } { "light" "200" "origin" "2256 1968 -453" "classname" "light" } { "classname" "light" "origin" "2256 2184 -453" "light" "200" } { "light" "200" "origin" "2216 2384 -453" "classname" "light" } { "classname" "light" "origin" "1792 2400 -453" "light" "200" } { "light" "175" "origin" "1984 2400 -453" "classname" "light" } { "light" "150" "origin" "2168 1608 -9" "classname" "light" } { "classname" "light" "origin" "2368 1600 -9" "light" "150" } { "light" "150" "origin" "2240 1424 44" "classname" "light" } { "classname" "light" "origin" "2400 1424 44" "light" "150" } { "light" "150" "origin" "2560 1424 44" "classname" "light" } { "classname" "light" "origin" "2560 1560 44" "light" "175" } { "light" "150" "origin" "2232 1288 44" "classname" "light" } { "light" "175" "origin" "2384 1424 -160" "classname" "light" } { "classname" "light" "origin" "2232 1288 -160" "light" "175" } { "light" "150" "origin" "2164 932 -172" "classname" "light" } { "classname" "light" "origin" "2308 932 -172" "light" "150" } { "light" "150" "origin" "2232 776 24" "classname" "light" } { "classname" "light" "origin" "2192 664 24" "light" "150" } { "light" "150" "origin" "2016 696 24" "classname" "light" } { "classname" "light" "origin" "1912 496 24" "light" "150" } { "light" "175" "origin" "80 1616 -120" "classname" "light" } { "classname" "light" "origin" "72 1888 -120" "light" "175" } { "light" "175" "origin" "296 1616 -120" "classname" "light" } { "light" "175" "origin" "304 1888 -120" "classname" "light" } { "targetname" "t20" "angle" "90" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t21" "angle" "120" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t22" "angle" "150" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t19" "angle" "60" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t18" "angle" "30" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t17" "angle" "0" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t24" "angle" "210" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t23" "angle" "180" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t25" "angle" "240" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t26" "angle" "270" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t27" "angle" "300" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t28" "angle" "330" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "delay" ".1" "targetname" "t29" "target" "t17" "classname" "trigger_multiple" "model" "*4" } { "delay" ".1" "targetname" "t17" "target" "t18" "classname" "trigger_multiple" "model" "*5" } { "delay" ".1" "targetname" "t18" "target" "t19" "classname" "trigger_multiple" "model" "*6" } { "delay" ".1" "targetname" "t19" "target" "t20" "classname" "trigger_multiple" "model" "*7" } { "delay" ".1" "targetname" "t20" "target" "t21" "classname" "trigger_multiple" "model" "*8" } { "delay" ".1" "targetname" "t21" "target" "t22" "classname" "trigger_multiple" "model" "*9" } { "delay" ".1" "targetname" "t22" "target" "t23" "classname" "trigger_multiple" "model" "*10" } { "delay" ".1" "targetname" "t23" "target" "t24" "classname" "trigger_multiple" "model" "*11" } { "delay" ".1" "targetname" "t24" "target" "t25" "classname" "trigger_multiple" "model" "*12" } { "delay" ".1" "targetname" "t25" "target" "t26" "classname" "trigger_multiple" "model" "*13" } { "delay" ".1" "targetname" "t26" "target" "t27" "classname" "trigger_multiple" "model" "*14" } { "delay" ".1" "targetname" "t27" "target" "t28" "classname" "trigger_multiple" "model" "*15" } { "target" "t29" "wait" "1.3" "classname" "trigger_multiple" "model" "*16" } { "origin" "192 1750 -188" "classname" "light_flame_large_yellow" } { "light" "125" "origin" "214 1752 -166" "classname" "light" } { "classname" "light" "origin" "192 1774 -166" "light" "125" } { "light" "125" "origin" "170 1750 -166" "classname" "light" } { "classname" "light" "origin" "194 1726 -166" "light" "125" } { "target" "t31" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*17" } { "target" "t31" "angle" "180" "wait" "-1" "classname" "func_button" "model" "*18" } { "target" "t31" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*19" } { "target" "t31" "angle" "90" "wait" "-1" "classname" "func_button" "model" "*20" } { "wait" "-1" "targetname" "t30" "sounds" "4" "speed" "50" "angle" "-1" "classname" "func_door" "model" "*21" } { "count" "4" "targetname" "t31" "target" "t30" "classname" "trigger_counter" "model" "*22" } { "light" "150" "origin" "2424 1080 -176" "classname" "light" } { "classname" "light" "origin" "2424 992 -176" "light" "150" } { "targetname" "t40" "angle" "180" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "160" } { "targetname" "t40" "angle" "140" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "120" } { "targetname" "t40" "angle" "200" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "220" } { "targetname" "t40" "angle" "240" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t41" "target" "t40" "wait" ".5" "classname" "trigger_multiple" "model" "*23" } { "target" "t41" "classname" "trigger_multiple" "model" "*24" } { "target" "t41" "classname" "trigger_multiple" "model" "*25" } { "target" "t41" "classname" "trigger_multiple" "model" "*26" } { "target" "t41" "classname" "trigger_multiple" "model" "*27" } { "target" "t41" "classname" "trigger_multiple" "model" "*28" } { "target" "t41" "classname" "trigger_multiple" "model" "*29" } { "target" "t41" "classname" "trigger_multiple" "model" "*30" } { "light" "125" "origin" "2196 1200 -204" "classname" "light" } { "classname" "light" "origin" "2284 1200 -204" "light" "125" } { "light" "125" "origin" "2284 1112 -204" "classname" "light" } { "classname" "light" "origin" "2388 1104 -204" "light" "125" } { "light" "125" "origin" "2284 1000 -204" "classname" "light" } { "classname" "light" "origin" "2140 1008 -204" "light" "125" } { "light" "125" "origin" "2132 1096 -204" "classname" "light" } { "classname" "light" "origin" "2132 1160 -204" "light" "125" } { "light" "200" "origin" "528 1816 -392" "classname" "light" } { "classname" "light" "origin" "736 1808 -392" "light" "200" } { "light" "200" "origin" "1040 1808 -392" "classname" "light" } { "classname" "light" "origin" "744 1424 -392" "light" "200" } { "light" "200" "origin" "752 1288 -416" "classname" "light" } { "light" "200" "origin" "760 1064 -376" "classname" "light" } { "classname" "func_train" "spawnflags" "33" "targetname" "t42" "dmg" "1000" "sounds" "1" "target" "t124" "speed" "250" "model" "*31" } { "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t43" "speed" "50" "sounds" "3" "model" "*32" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t42" "sounds" "1" "model" "*33" } { "classname" "trigger_once" "target" "t43" "targetname" "t42" "delay" "2" "model" "*34" } { "classname" "light" "origin" "1936 1784 -288" "light" "125" } { "classname" "monster_ogre" "origin" "1976 1784 -328" "angle" "180" } { "classname" "func_door" "angle" "90" "wait" "-1" "sounds" "1" "model" "*35" } { "classname" "func_door" "angle" "270" "targetname" "t44" "wait" "-1" "model" "*36" } { "classname" "light" "origin" "64 264 8" "light" "125" } { "light" "125" "origin" "64 120 8" "classname" "light" } { "classname" "light" "origin" "64 192 8" "light" "100" } { "classname" "trigger_once" "target" "t44" "model" "*37" } { "classname" "light" "origin" "512 184 -8" "light" "175" } { "targetname" "t119" "classname" "func_door" "angle" "90" "wait" "-1" "speed" "40" "model" "*38" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "270" "speed" "40" "sounds" "4" "model" "*39" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "-1" "speed" "30" "message" "Go for a swim first..." "sounds" "3" "model" "*40" } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "t45" "model" "*41" } { "classname" "light" "origin" "36 184 84" "light" "75" } { "classname" "light" "origin" "752 184 -192" "light" "150" } { "classname" "light" "origin" "544 536 -152" "light" "125" } { "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "targetname" "t45" "model" "*42" } { "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "targetname" "t45" "model" "*43" } { "light" "125" "origin" "544 -152 -152" "classname" "light" } { "classname" "item_artifact_envirosuit" "origin" "1024 492 -232" } { "classname" "item_armor1" "origin" "2128 1752 -352" } { "classname" "light_flame_small_yellow" "origin" "1024 368 -4" "light" "250" } { "classname" "light" "origin" "1024 400 -64" "light" "150" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*44" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*45" } { "sounds" "3" "classname" "func_button" "angle" "270" "wait" "3" "target" "t51" "model" "*46" } { "classname" "light" "origin" "1224 712 -216" "light" "100" } { "classname" "light" "origin" "1232 768 -192" "light" "100" } { "light" "75" "origin" "1168 760 -232" "classname" "light" } { "light" "100" "origin" "1232 800 -208" "classname" "light" } { "sounds" "1" "origin" "1860 472 -8" "classname" "item_key2" "spawnflags" "2048" } { "classname" "light" "origin" "1112 1568 -40" "light" "200" } { "light" "150" "origin" "-96 1440 -160" "classname" "light" } { "classname" "light" "origin" "384 1440 -160" "light" "150" } { "light" "200" "origin" "248 1408 -96" "classname" "light" } { "light" "150" "origin" "968 1480 -128" "classname" "light" } { "light" "150" "origin" "1008 -592 -32" "classname" "light" } { "classname" "light" "origin" "1264 -592 -32" "light" "150" } { "light" "200" "origin" "880 -840 -56" "classname" "light" } { "classname" "light" "origin" "1376 -856 -56" "light" "200" } { "light" "175" "origin" "264 184 -8" "classname" "light" } { "light" "175" "origin" "1464 -864 -208" "classname" "light" } { "light" "175" "origin" "1816 160 -56" "classname" "light" } { "classname" "light" "origin" "1560 160 -56" "light" "175" } { "light" "175" "origin" "1544 1632 104" "classname" "light" } { "classname" "light" "origin" "1792 1624 128" "light" "175" } { "classname" "item_health" "origin" "1068 -980 -104" } { "spawnflags" "1" "classname" "item_shells" "origin" "1344 -1160 -96" } { "classname" "monster_ogre" "origin" "1008 -128 40" "angle" "315" "targetname" "t59" "spawnflags" "1" } { "classname" "trigger_once" "target" "t59" "model" "*47" } { "classname" "monster_ogre" "origin" "896 96 56" "angle" "270" "target" "t60" "spawnflags" "1" } { "classname" "path_corner" "origin" "896 208 40" "target" "t60" "targetname" "t61" } { "origin" "896 -16 40" "classname" "path_corner" "targetname" "t60" "target" "t61" } { "classname" "path_corner" "origin" "168 392 -56" "target" "t62" "targetname" "t63" } { "origin" "168 -16 -56" "classname" "path_corner" "targetname" "t62" "target" "t63" } { "classname" "path_corner" "origin" "88 344 -56" "targetname" "t64" "target" "t65" "spawnflags" "256" } { "origin" "88 72 -56" "classname" "path_corner" "target" "t64" "targetname" "t65" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "88 224 -40" "angle" "90" "target" "t64" "spawnflags" "257" } { "classname" "monster_hell_knight" "origin" "168 232 -40" "angle" "270" "target" "t62" "spawnflags" "1" } { "classname" "monster_zombie" "origin" "544 -120 -208" "angle" "90" "targetname" "t45" } { "classname" "monster_zombie" "origin" "544 496 -208" "angle" "270" "targetname" "t45" } { "classname" "monster_wizard" "origin" "664 428 216" "angle" "225" "spawnflags" "1" } { "angle" "135" "origin" "664 -56 216" "classname" "monster_wizard" "spawnflags" "257" } { "classname" "light" "origin" "1736 88 -40" "light" "100" } { "classname" "monster_ogre" "origin" "904 -120 56" "angle" "0" "targetname" "t66" "spawnflags" "1281" } { "classname" "trigger_once" "target" "t66" "spawnflags" "256" "model" "*48" } { "classname" "item_health" "origin" "1168 -408 -80" "spawnflags" "1" } { "classname" "item_health" "origin" "1168 -104 -16" } { "classname" "item_spikes" "origin" "928 216 32" } { "classname" "item_rockets" "origin" "632 -48 32" } { "classname" "item_health" "origin" "32 392 -64" } { "light" "200" "origin" "1688 288 148" "classname" "light" } { "classname" "light" "origin" "1688 464 148" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1814 622 -64" "light" "250" } { "classname" "light" "origin" "1768 344 -72" "light" "200" } { "classname" "trigger_monsterjump" "angle" "0" "model" "*49" } { "targetname" "t100" "classname" "monster_ogre" "origin" "1504 272 24" "angle" "0" "spawnflags" "256" } { "targetname" "t68" "target" "t67" "origin" "1352 576 -120" "classname" "path_corner" "spawnflags" "256" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "1352 416 -120" "spawnflags" "256" } { "target" "t68" "angle" "90" "origin" "1360 480 -104" "classname" "monster_demon1" "spawnflags" "257" } { "targetname" "t70" "target" "t69" "origin" "1472 496 -120" "classname" "path_corner" } { "target" "t70" "targetname" "t69" "classname" "path_corner" "origin" "1688 496 -120" } { "target" "t69" "angle" "270" "origin" "1648 544 -104" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t71" "targetname" "t72" "origin" "1984 688 -24" "classname" "path_corner" "spawnflags" "256" } { "target" "t72" "targetname" "t71" "classname" "path_corner" "origin" "1984 448 -24" "spawnflags" "256" } { "target" "t71" "angle" "270" "origin" "1992 536 -8" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t74" "targetname" "t73" "origin" "2120 680 -24" "classname" "path_corner" } { "targetname" "t74" "target" "t73" "classname" "path_corner" "origin" "2240 680 -24" } { "target" "t74" "angle" "225" "origin" "2256 736 -8" "classname" "monster_ogre" "spawnflags" "1" } { "targetname" "t78" "angle" "90" "origin" "2232 1312 -8" "classname" "monster_ogre" "spawnflags" "256" } { "target" "t76" "angle" "90" "origin" "2232 1008 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "targetname" "t76" "target" "t75" "origin" "2120 968 -216" "classname" "path_corner" } { "target" "t76" "targetname" "t75" "classname" "path_corner" "origin" "2336 968 -216" } { "classname" "light" "origin" "2384 2400 -152" "light" "200" } { "targetname" "t77" "wait" "-1" "sounds" "1" "speed" "300" "angle" "-2" "classname" "func_door" "model" "*50" } { "targetname" "t77" "angle" "180" "origin" "2512 2312 -200" "classname" "monster_demon1" "spawnflags" "256" } { "targetname" "t77" "classname" "monster_demon1" "origin" "2512 2160 -200" "angle" "180" } { "classname" "item_health" "origin" "2504 2200 -224" } { "light" "150" "origin" "2536 2312 -128" "classname" "light" } { "classname" "light" "origin" "2536 2160 -128" "light" "150" } { "target" "t77" "classname" "trigger_once" "model" "*51" } { "spawnflags" "2" "origin" "2368 2336 -224" "classname" "item_health" } { "spawnflags" "1" "origin" "2504 2240 -224" "classname" "item_spikes" } { "origin" "2408 2072 -224" "classname" "item_shells" } { "target" "t78" "classname" "trigger_once" "model" "*52" } { "targetname" "t78" "angle" "90" "origin" "2240 1272 -200" "classname" "monster_demon1" } { "light" "150" "origin" "1120 1384 -40" "classname" "light" } { "light" "200" "origin" "1200 1248 -104" "classname" "light" } { "light" "250" "origin" "1288 1032 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1256 1032 -64" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "568 1032 -4" "light" "250" } { "classname" "light" "origin" "600 1032 -64" "light" "150" } { "light" "250" "origin" "888 1272 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "888 1240 -64" "classname" "light" } { "light" "200" "origin" "608 1232 -108" "classname" "light" } { "target" "t79" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*53" } { "targetname" "t79" "message" "This door is opened near by..." "sounds" "3" "speed" "35" "angle" "-1" "wait" "-1" "classname" "func_door" "model" "*54" } { "light" "100" "origin" "580 1208 -148" "classname" "light" } { "light" "200" "origin" "1224 736 -8" "classname" "light" } { "target" "t80" "classname" "trigger_once" "model" "*55" } { "targetname" "t80" "angle" "315" "origin" "640 1088 128" "classname" "monster_wizard" } { "targetname" "t80" "classname" "monster_wizard" "origin" "616 720 128" "angle" "0" } { "targetname" "t80" "angle" "180" "origin" "1280 680 128" "classname" "monster_wizard" "spawnflags" "256" } { "target" "t82" "origin" "1248 1080 24" "classname" "monster_wizard" "spawnflags" "1" } { "targetname" "t82" "target" "t81" "origin" "1168 1064 8" "classname" "path_corner" } { "target" "t82" "targetname" "t81" "classname" "path_corner" "origin" "720 1064 8" } { "targetname" "t80" "classname" "monster_wizard" "origin" "584 1112 128" "angle" "315" "spawnflags" "256" } { "origin" "1256 944 -184" "classname" "item_health" } { "spawnflags" "1" "origin" "1116 584 -256" "classname" "item_spikes" } { "targetname" "t79" "angle" "315" "origin" "1000 1464 -168" "classname" "monster_ogre" } { "target" "t85" "targetname" "t86" "origin" "1456 1824 -192" "classname" "path_corner" "spawnflags" "256" } { "target" "t86" "targetname" "t85" "classname" "path_corner" "origin" "1456 1560 -192" "spawnflags" "256" } { "target" "t85" "angle" "270" "origin" "1456 1664 -176" "classname" "monster_ogre" "spawnflags" "256" } { "targetname" "t91" "angle" "270" "origin" "1560 1944 -320" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1600 1912 -320" "angle" "270" } { "angle" "225" "origin" "1960 1984 -320" "classname" "monster_zombie" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1904 1928 -320" "angle" "225" } { "targetname" "t91" "angle" "270" "origin" "1624 1768 -320" "classname" "monster_zombie" } { "target" "t87" "classname" "monster_zombie" "origin" "1696 1912 -320" "angle" "0" } { "target" "t89" "angle" "270" "origin" "1704 1800 -320" "classname" "monster_zombie" } { "target" "t87" "targetname" "t88" "origin" "1648 1912 -336" "classname" "path_corner" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "1848 1904 -336" } { "targetname" "t90" "target" "t89" "origin" "1648 1824 -336" "classname" "path_corner" } { "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "1768 1752 -336" } { "target" "t91" "classname" "trigger_once" "model" "*56" } { "spawnflags" "1" "origin" "1496 1808 -200" "classname" "item_health" } { "spawnflags" "1" "origin" "1184 2176 -192" "classname" "item_health" } { "classname" "item_health" "origin" "1184 2128 -192" "spawnflags" "1" } { "spawnflags" "1" "origin" "1264 1680 -192" "classname" "item_health" } { "target" "t92" "targetname" "t93" "origin" "2376 2304 -216" "classname" "path_corner" } { "target" "t93" "targetname" "t92" "classname" "path_corner" "origin" "2376 1984 -216" } { "target" "t95" "targetname" "t94" "origin" "2144 1608 -216" "classname" "path_corner" } { "target" "t94" "targetname" "t95" "classname" "path_corner" "origin" "2376 1608 -216" } { "target" "t92" "angle" "270" "origin" "2376 2176 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "135" "origin" "2416 1760 -200" "classname" "monster_hell_knight" "spawnflags" "257" } { "target" "t94" "angle" "180" "origin" "2320 1608 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "90" "origin" "2152 1840 -200" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t97" "targetname" "t96" "origin" "2096 2376 -216" "classname" "path_corner" "spawnflags" "256" } { "target" "t96" "targetname" "t97" "classname" "path_corner" "origin" "2232 2208 -216" "spawnflags" "256" } { "target" "t96" "angle" "135" "origin" "2200 2264 -200" "classname" "monster_wizard" "spawnflags" "257" } { "origin" "2104 1544 -224" "classname" "item_shells" } { "origin" "2176 1240 -32" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2256 1240 -32" } { "origin" "1928 584 -32" "classname" "item_shells" } { "spawnflags" "1" "origin" "2040 1072 -32" "classname" "item_health" } { "classname" "item_health" "origin" "2040 1024 -32" "spawnflags" "1" } { "angle" "225" "origin" "2352 1160 -200" "classname" "monster_hell_knight" "spawnflags" "256" } { "angle" "180" "origin" "1864 1192 -216" "classname" "monster_zombie" } { "classname" "monster_zombie" "origin" "1776 1192 -216" "angle" "180" } { "target" "t99" "targetname" "t98" "origin" "1688 1192 -232" "classname" "path_corner" } { "target" "t98" "targetname" "t99" "classname" "path_corner" "origin" "1688 1048 -232" } { "target" "t98" "angle" "90" "origin" "1688 1112 -216" "classname" "monster_zombie" } { "origin" "536 416 32" "classname" "item_shells" } { "target" "t100" "classname" "trigger_once" "spawnflags" "256" "model" "*57" } { "origin" "1784 408 -128" "classname" "item_health" } { "classname" "item_health" "origin" "1784 448 -128" } { "origin" "1736 728 -128" "classname" "item_rockets" } { "target" "t101" "sounds" "1" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*58" } { "light" "250" "origin" "1418 234 52" "classname" "light_torch_small_walltorch" } { "targetname" "t101" "classname" "trigger_secret" "model" "*59" } { "targetname" "t102" "angle" "180" "classname" "trigger_monsterjump" "model" "*60" } { "target" "t102" "killtarget" "t102" "classname" "trigger_once" "model" "*61" } { "origin" "1568 1968 -352" "classname" "item_health" } { "classname" "item_health" "origin" "1608 1968 -352" } { "origin" "2112 1776 -352" "classname" "item_shells" "spawnflags" "2048" } { "origin" "1920 2232 -352" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2008 2232 -352" } { "classname" "item_shells" "origin" "2176 1440 -224" } { "target" "t66" "classname" "trigger_once" "model" "*62" } { "origin" "1744 0 -128" "classname" "item_spikes" "spawnflags" "1" } { "target" "t103" "targetname" "t104" "origin" "-56 1432 -224" "classname" "path_corner" "spawnflags" "256" } { "target" "t104" "targetname" "t103" "classname" "path_corner" "origin" "312 1432 -224" "spawnflags" "256" } { "target" "t103" "origin" "40 1440 -208" "classname" "monster_ogre" "spawnflags" "257" } { "angle" "45" "origin" "56 1616 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "target" "t105" "targetname" "t106" "origin" "520 1816 -232" "classname" "path_corner" } { "target" "t106" "targetname" "t105" "classname" "path_corner" "origin" "920 1816 -232" } { "target" "t105" "origin" "624 1800 -216" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "0" "origin" "480 1824 64" "classname" "monster_wizard" "spawnflags" "257" } { "targetname" "t107" "classname" "monster_wizard" "origin" "656 1816 64" "angle" "0" "spawnflags" "1" } { "targetname" "t107" "angle" "0" "origin" "840 1816 64" "classname" "monster_wizard" "spawnflags" "257" } { "target" "t107" "classname" "trigger_once" "model" "*63" } { "origin" "1440 2200 -192" "classname" "item_shells" } { "classname" "item_shells" "origin" "1440 2160 -192" } { "spawnflags" "1" "origin" "1184 1976 -192" "classname" "item_spikes" } { "target" "t109" "targetname" "t108" "origin" "1240 2000 -184" "classname" "path_corner" } { "target" "t108" "targetname" "t109" "classname" "path_corner" "origin" "1240 1760 -184" } { "target" "t108" "angle" "90" "origin" "1240 1904 -168" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t110" "classname" "trigger_once" "spawnflags" "256" "model" "*64" } { "targetname" "t110" "angle" "45" "origin" "1216 2088 -168" "classname" "monster_ogre" "spawnflags" "1281" } { "classname" "item_health" "origin" "1496 1768 -200" } { "target" "t111" "classname" "trigger_once" "model" "*65" } { "targetname" "t111" "angle" "315" "origin" "764 -60 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "45" "origin" "764 436 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "225" "origin" "284 -56 -208" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t119" "wait" "-1" "speed" "40" "angle" "90" "classname" "func_door" "model" "*66" } { "origin" "1280 592 -128" "classname" "item_shells" } { "classname" "item_shells" "origin" "576 416 32" } { "classname" "item_spikes" "origin" "904 504 -128" "spawnflags" "1" } { "origin" "904 464 -128" "classname" "item_spikes" "spawnflags" "1" } { "classname" "item_health" "origin" "1024 912 -152" "spawnflags" "1" } { "classname" "item_spikes" "origin" "720 848 -184" "spawnflags" "1" } { "classname" "path_corner" "origin" "1224 1192 -176" "targetname" "t112" "target" "t113" } { "origin" "1216 896 -176" "classname" "path_corner" "targetname" "t113" "target" "t112" } { "classname" "monster_ogre" "origin" "1224 992 -160" "angle" "90" "target" "t112" } { "classname" "item_shells" "origin" "968 1600 -184" } { "classname" "item_artifact_super_damage" "origin" "1444 308 -104" } { "classname" "light" "origin" "992 -128 72" "light" "100" } { "classname" "item_shells" "origin" "1672 1008 -240" } { "classname" "item_health" "origin" "2264 1320 -224" } { "classname" "monster_wizard" "origin" "2120 1664 -72" "angle" "315" "spawnflags" "1" } { "classname" "item_health" "origin" "2016 392 -32" } { "classname" "monster_hell_knight" "origin" "360 1744 -208" "angle" "90" "targetname" "t114" "spawnflags" "257" } { "classname" "trigger_once" "target" "t114" "spawnflags" "256" "model" "*67" } { "classname" "item_health" "origin" "352 1464 -232" } { "spawnflags" "1" "origin" "352 1392 -232" "classname" "item_health" } { "classname" "item_armorInv" "origin" "744 1424 -448" } { "classname" "item_health" "origin" "-56 320 -232" } { "origin" "-16 320 -232" "classname" "item_health" } { "targetname" "t117" "classname" "trigger_teleport" "target" "t115" "spawnflags" "2" "model" "*68" } { "delay" ".5" "targetname" "t117" "classname" "trigger_teleport" "target" "t116" "spawnflags" "2" "model" "*69" } { "classname" "monster_wizard" "origin" "2928 1816 -152" "angle" "180" "targetname" "t117" } { "angle" "180" "origin" "2928 1768 -152" "classname" "monster_wizard" "targetname" "t117" } { "classname" "info_teleport_destination" "origin" "1824 1920 -184" "angle" "225" "targetname" "t115" } { "classname" "info_teleport_destination" "origin" "1880 1544 -184" "angle" "180" "targetname" "t116" } { "classname" "trigger_once" "target" "t117" "model" "*70" } { "classname" "monster_zombie" "origin" "764 388 -208" "angle" "0" "targetname" "t111" } { "angle" "0" "origin" "764 -12 -208" "classname" "monster_zombie" "targetname" "t111" } { "classname" "monster_zombie" "origin" "408 -56 -208" "angle" "270" "targetname" "t111" } { "classname" "light" "origin" "1200 672 -240" "light" "125" } { "classname" "item_spikes" "origin" "72 392 -64" "spawnflags" "1" } { "classname" "item_spikes" "origin" "2368 920 -224" } { "origin" "2032 976 -224" "classname" "item_spikes" } { "classname" "light" "origin" "1416 2096 -112" "light" "150" } { "light" "150" "origin" "1240 2096 -112" "classname" "light" } { "classname" "item_spikes" "origin" "464 1824 -240" } { "origin" "504 1824 -240" "classname" "item_spikes" } { "classname" "item_shells" "origin" "-96 1472 -232" "spawnflags" "1" } { "classname" "item_health" "origin" "528 -172 -232" } { "classname" "item_health" "origin" "40 -64 -64" } { "light" "225" "classname" "light_torch_small_walltorch" "origin" "122 -86 -8" } { "origin" "134 462 -8" "classname" "light_torch_small_walltorch" "light" "225" } { "light" "250" "origin" "678 446 92" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "678 -70 92" "light" "250" } { "light" "150" "origin" "120 -56 -16" "classname" "light" } { "classname" "light" "origin" "136 424 -16" "light" "150" } { "light" "150" "origin" "600 184 296" "classname" "light" } { "classname" "light" "origin" "152 184 296" "light" "150" } { "light" "150" "origin" "368 376 296" "classname" "light" } { "classname" "light" "origin" "368 0 296" "light" "150" } { "light" "150" "origin" "352 192 232" "classname" "light" } { "light" "200" "origin" "64 408 168" "classname" "light" } { "classname" "light" "origin" "56 -48 168" "light" "200" } { "light" "125" "origin" "1520 1880 24" "classname" "light" } { "origin" "272 272 -232" "classname" "item_rockets" } { "targetname" "t45" "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "model" "*71" } { "classname" "light" "origin" "416 -152 -152" "light" "125" } { "targetname" "t45" "angle" "90" "origin" "416 -120 -208" "classname" "monster_zombie" "spawnflags" "256" } { "origin" "400 524 -232" "classname" "item_health" } { "light" "125" "origin" "416 536 -152" "classname" "light" } { "targetname" "t45" "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "model" "*72" } { "targetname" "t45" "angle" "270" "origin" "416 496 -208" "classname" "monster_zombie" "spawnflags" "256" } { "target" "t120" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*73" } { "target" "t120" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*74" } { "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*75" } { "light" "125" "origin" "1792 840 -112" "classname" "light" } { "classname" "light" "origin" "1584 840 -112" "light" "125" } { "light" "125" "origin" "1792 2280 -480" "classname" "light" } { "classname" "light" "origin" "1792 2144 -480" "light" "125" } { "origin" "1776 2232 -508" "classname" "item_health" } { "classname" "item_health" "origin" "1776 2192 -508" } { "light" "150" "origin" "1016 1696 -336" "classname" "light" } { "light" "125" "origin" "1080 1696 -280" "classname" "light" } { "light" "125" "origin" "1152 1696 -200" "classname" "light" } { "light" "150" "origin" "1096 1696 -352" "classname" "light" } { "classname" "trigger_secret" "model" "*76" } { "target" "t116" "classname" "trigger_teleport" "model" "*77" } { "spawnflags" "768" "angle" "315" "origin" "1400 1856 -176" "classname" "monster_ogre" } { "target" "t121" "targetname" "t122" "spawnflags" "768" "origin" "1824 2280 -344" "classname" "path_corner" } { "target" "t122" "targetname" "t121" "spawnflags" "768" "classname" "path_corner" "origin" "2080 2280 -344" } { "target" "t121" "spawnflags" "769" "angle" "180" "origin" "2128 2272 -328" "classname" "monster_demon1" } { "spawnflags" "2816" "origin" "1824 2072 -352" "classname" "item_shells" } { "spawnflags" "769" "angle" "90" "origin" "1792 2176 -144" "classname" "monster_wizard" } { "spawnflags" "769" "angle" "180" "origin" "2600 1640 -200" "classname" "monster_hell_knight" } { "spawnflags" "2816" "origin" "2096 1136 -32" "classname" "item_shells" } { "classname" "monster_wizard" "origin" "1872 1432 -72" "angle" "90" "spawnflags" "769" } { "classname" "monster_hell_knight" "origin" "2400 1032 -8" "angle" "180" "spawnflags" "769" } { "classname" "monster_demon1" "origin" "1000 496 -104" "angle" "0" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1240 2088 -168" "angle" "45" "spawnflags" "769" "targetname" "t110" } { "classname" "monster_hell_knight" "origin" "360 1936 -208" "angle" "270" "spawnflags" "769" "targetname" "t114" } { "classname" "item_shells" "origin" "16 1768 -232" "spawnflags" "2560" } { "spawnflags" "2560" "origin" "344 1576 -232" "classname" "item_shells" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*78" } { "classname" "light" "origin" "1136 -1144 264" "light" "250" } { "light" "250" "origin" "1264 -552 44" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "1008 -552 44" "light" "250" } { "classname" "monster_demon1" "origin" "888 -120 56" "angle" "0" "spawnflags" "769" "targetname" "t66" } { "classname" "monster_hell_knight" "origin" "616 184 56" "angle" "0" "spawnflags" "768" } { "classname" "item_spikes" "origin" "880 216 32" "spawnflags" "2816" } { "classname" "monster_hell_knight" "origin" "1944 728 -8" "angle" "0" "spawnflags" "769" } { "classname" "light" "origin" "-48 1184 -156" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 1024 -120" "classname" "light" } { "classname" "light" "origin" "48 800 -120" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 600 -120" "classname" "light" } { "classname" "monster_hell_knight" "origin" "128 1088 -208" "angle" "225" "spawnflags" "1" } { "angle" "315" "origin" "-104 760 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-56 592 -232" } { "classname" "item_health" "origin" "-56 920 -232" } { "classname" "monster_demon1" "origin" "0 544 -208" "angle" "90" "target" "t123" "spawnflags" "769" } { "mangle" "20 315 0" "origin" "1568 2040 -88" "classname" "info_intermission" } { "classname" "item_shells" "origin" "1528 1968 -352" "spawnflags" "3584" } { "origin" "-88 1376 -232" "classname" "item_health" "spawnflags" "3585" } { "classname" "item_artifact_envirosuit" "origin" "1216 1696 -168" "spawnflags" "3584" } { "classname" "monster_demon1" "origin" "144 372 -208" "angle" "180" "spawnflags" "768" "targetname" "t123" } { "classname" "monster_demon1" "origin" "0 528 -208" "angle" "90" "spawnflags" "1025" } { "classname" "info_player_deathmatch" "origin" "1128 -840 -80" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "656 184 -208" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-24 1440 -208" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "1240 1816 -168" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1032 1568 -168" "angle" "0" } { "classname" "func_wall" "spawnflags" "1792" "model" "*79" } { "classname" "weapon_rocketlauncher" "origin" "184 1440 -232" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1424 608 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "2272 680 -8" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "2240 1472 -200" "angle" "270" } { "classname" "weapon_supernailgun" "origin" "2236 1184 -32" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "2424 1824 -200" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1792 2072 -328" "angle" "270" } { "classname" "weapon_supershotgun" "origin" "1488 1584 -200" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "88 184 -64" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1224 840 -168" "angle" "90" } { "classname" "weapon_nailgun" "origin" "1024 872 -152" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "136 1760 -232" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "1976 2288 -352" "spawnflags" "1792" } { "classname" "weapon_lightning" "origin" "2128 1792 -352" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1608 728 -128" "spawnflags" "1792" } { "spawnflags" "1792" "classname" "item_cells" "origin" "72 1032 -232" } { "classname" "item_cells" "origin" "1008 1328 -192" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1304 -1160 -96" "spawnflags" "1792" } { "origin" "2272 1440 -224" "classname" "item_shells" "spawnflags" "2816" } { "origin" "224 1736 -232" "classname" "item_health" } { "classname" "info_intermission" "origin" "1328 -1168 192" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1248 680 8" "mangle" "20 130 0" } { "classname" "info_intermission" "origin" "1280 1824 -104" "mangle" "10 180 0" } { "classname" "light" "origin" "-304 888 -80" "light" "150" } { "light" "150" "origin" "-304 712 -80" "classname" "light" } { "classname" "light" "origin" "-224 872 -8" "light" "125" } { "classname" "light" "origin" "-224 728 -8" "light" "125" } { "light" "150" "origin" "-178 706 -156" "classname" "light_torch_small_walltorch" } { "classname" "light" "origin" "-320 838 -138" "light" "100" } { "light" "100" "origin" "-320 774 -138" "classname" "light" } { "classname" "info_player_coop" "origin" "1192 -1088 -72" "angle" "90" } { "angle" "90" "origin" "1080 -1088 -72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1008 -1112 -72" "angle" "90" } { "angle" "90" "origin" "1264 -1112 -72" "classname" "info_player_coop" } { "classname" "item_armor1" "origin" "784 1816 -232" } { "classname" "item_spikes" "origin" "-56 1472 -232" "spawnflags" "1" } { "classname" "item_rockets" "origin" "400 -172 -232" } { "classname" "func_wall" "spawnflags" "1792" "model" "*80" } { "spawnflags" "1792" "classname" "func_wall" "model" "*81" } { "classname" "path_corner" "origin" "1954 1770 -96" "targetname" "t124" "target" "t125" } { "classname" "path_corner" "origin" "1954 1770 -320" "targetname" "t125" "target" "t124" } { "classname" "weapon_grenadelauncher" "origin" "1688 720 -128" "spawnflags" "2048" } { "wait" "-1" "angle" "-2" "classname" "func_door" "targetname" "t126" "lip" "-8" "model" "*82" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "model" "*83" } { "classname" "light" "origin" "-224 832 -152" "light" "125" } { "classname" "trigger_counter" "target" "t126" "targetname" "t127" "spawnflags" "1" "count" "7" "model" "*84" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*85" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*86" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*87" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*88" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*89" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*90" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*91" } { "classname" "ambient_swamp1" "origin" "1338 -854 -104" } { "classname" "ambient_swamp2" "origin" "938 -854 -104" } { "classname" "ambient_drip" "origin" "1138 -854 -176" } { "classname" "ambient_drip" "origin" "1650 -862 -192" } { "classname" "ambient_drip" "origin" "1674 -438 -192" } { "classname" "ambient_drip" "origin" "1682 2 -48" } { "classname" "ambient_swamp1" "origin" "1674 1986 -280" } { "classname" "ambient_swamp2" "origin" "1826 2378 -280" } { "classname" "ambient_swamp1" "origin" "2258 2058 -280" } { "classname" "ambient_drip" "origin" "746 1370 -376" } { "classname" "ambient_drip" "origin" "762 906 -240" } { "origin" "1034 722 -240" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "554 1818 -352" } { "origin" "1002 1810 -352" "classname" "ambient_drip" } { "speed" "35" "classname" "func_door" "wait" "-1" "angle" "-2" "sounds" "1" "targetname" "t101" "model" "*92" } { "classname" "light" "origin" "1440 296 -80" "light" "125" } { "origin" "1792 792 -240" "classname" "item_spikes" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m2.ent.orig0000644000000000000000000012033212403131422017770 0ustar rootroot{ "message" "Castle of the Damned" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" "sounds" "8" } { "angle" "270" "origin" "1496 1664 296" "classname" "info_player_start" } { "origin" "1432 672 336" "classname" "light" "light" "250" } { "light" "200" "origin" "1496 888 272" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "932 640 340" } { "classname" "light_torch_small_walltorch" "origin" "1104 812 340" } { "classname" "light" "origin" "1104 640 544" "light" "300" } { "light" "175" "origin" "1216 536 353" "classname" "light" } { "light" "250" "origin" "1816 328 448" "classname" "light" } { "light" "200" "origin" "1632 472 208" "classname" "light" } { "light" "200" "origin" "1792 -392 240" "classname" "light" } { "light" "200" "origin" "1452 -124 508" "classname" "light" } { "light" "150" "origin" "1196 -124 508" "classname" "light" } { "light" "150" "origin" "1044 -124 508" "classname" "light" } { "light" "200" "origin" "756 -124 508" "classname" "light" } { "light" "250" "origin" "744 336 145" "classname" "light" } { "light" "200" "origin" "1176 -912 672" "classname" "light" } { "origin" "1328 -544 552" "classname" "light" } { "classname" "light" "origin" "1528 -912 640" "light" "200" } { "light" "200" "classname" "light" "origin" "880 -648 672" } { "origin" "240 -264 392" "classname" "light" "light" "250" } { "classname" "light" "origin" "-352 -504 464" "light" "200" } { "origin" "-448 -608 804" "classname" "light" "light" "450" } { "light" "250" "origin" "776 -912 472" "classname" "light" } { "light" "300" "origin" "1630 -806 428" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "1528 -912 464" "classname" "light" } { "classname" "light" "origin" "1180 -484 560" } { "classname" "light" "origin" "1184 -612 560" } { "classname" "light" "origin" "1016 -368 472" "light" "100" } { "classname" "light" "origin" "1016 -464 472" "light" "100" } { "classname" "light" "origin" "1020 -560 472" "light" "100" } { "classname" "light" "origin" "1208 -776 472" "light" "100" } { "classname" "light" "origin" "1288 -776 472" "light" "100" } { "classname" "light" "origin" "1360 -776 472" "light" "100" } { "light" "200" "origin" "1792 120 376" "classname" "light" } { "origin" "1538 182 356" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "1640 80 360" "classname" "light" } { "light" "200" "origin" "1928 80 360" "classname" "light" } { "light" "250" "origin" "1792 296 208" "classname" "light" } { "light" "150" "origin" "1800 40 160" "classname" "light" } { "light" "200" "origin" "1776 -392 160" "classname" "light" } { "light" "200" "origin" "1304 -392 152" "classname" "light" } { "light" "250" "origin" "1632 112 136" "classname" "light" } { "light" "250" "origin" "1432 312 136" "classname" "light" } { "light" "200" "origin" "1136 -656 160" "classname" "light" } { "light" "200" "origin" "1136 -416 160" "classname" "light" } { "light" "250" "origin" "1448 -552 160" "classname" "light" } { "light" "200" "origin" "1920 440 136" "classname" "light" } { "light" "200" "origin" "968 88 177" "classname" "light" } { "light" "300" "origin" "1088 312 129" "classname" "light" } { "light" "150" "origin" "1376 168 129" "classname" "light" } { "light" "250" "origin" "112 -384 392" "classname" "light" } { "origin" "300 -1004 508" "classname" "light" } { "origin" "296 -812 505" "classname" "light" } { "origin" "300 -1204 505" "classname" "light" } { "light" "150" "origin" "470 -1006 468" "classname" "light_torch_small_walltorch" } { "light" "250" "origin" "984 -1216 496" "classname" "light" } { "light" "250" "origin" "888 -1128 552" "classname" "light" } { "light" "200" "origin" "800 -1216 592" "classname" "light" } { "light" "200" "origin" "664 -1216 592" "classname" "light" } { "light" "200" "origin" "584 -1136 592" "classname" "light" } { "light" "200" "origin" "584 -968 592" "classname" "light" } { "light" "250" "origin" "584 -744 592" "classname" "light" } { "light" "200" "origin" "528 -1144 464" "classname" "light" } { "light" "200" "origin" "528 -856 464" "classname" "light" } { "light" "200" "classname" "light" "origin" "1496 1544 440" } { "origin" "1384 1392 440" "classname" "light" "light" "250" } { "origin" "1496 1104 520" "classname" "light" } { "origin" "1608 1400 440" "classname" "light" "light" "250" } { "light" "250" "origin" "1240 1712 360" "classname" "light" } { "light" "250" "origin" "1744 1696 360" "classname" "light" } { "classname" "light" "origin" "1384 1136 440" "light" "250" } { "classname" "light" "origin" "1608 1144 440" "light" "250" } { "classname" "path_corner" "origin" "1168 736 296" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "992 744 296" "targetname" "t6" "target" "t7" } { "classname" "path_corner" "origin" "1000 544 296" "targetname" "t7" "target" "t34" } { "classname" "item_health" "origin" "960 704 288" } { "classname" "item_shells" "origin" "952 512 288" } { "classname" "path_corner" "origin" "1344 -128 304" "targetname" "t9" "target" "t8" } { "classname" "path_corner" "origin" "898 -128 304" "targetname" "t8" "target" "t9" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1018 -126 320" "angle" "0" "target" "t8" } { "classname" "item_health" "origin" "1344 -224 296" "spawnflags" "1" } { "classname" "item_health" "origin" "1400 -224 296" "spawnflags" "1" } { "origin" "1528 192 296" "classname" "item_shells" } { "classname" "path_corner" "origin" "1496 1040 184" "targetname" "t22" "target" "t23" } { "classname" "path_corner" "origin" "1496 840 248" "targetname" "t23" "target" "t33" } { "spawnflags" "1" "classname" "item_shells" "origin" "1056 -648 288" } { "classname" "item_health" "origin" "1184 -736 288" } { "spawnflags" "257" "classname" "monster_army" "origin" "1646 -698 360" "angle" "180" "targetname" "t89" } { "classname" "path_corner" "origin" "1400 640 272" "targetname" "t30" "target" "t79" } { "classname" "path_corner" "origin" "1496 752 232" "targetname" "t33" "target" "t77" } { "classname" "path_corner" "origin" "1192 560 296" "targetname" "t34" "target" "t80" } { "classname" "item_shells" "origin" "1616 1280 176" } { "classname" "item_health" "origin" "1056 -840 416" "spawnflags" "1" } { "classname" "item_health" "origin" "1104 -840 416" "spawnflags" "1" } { "spawnflags" "1" "classname" "monster_army" "origin" "262 -458 320" "angle" "0" "target" "t96" } { "spawnflags" "1024" "classname" "item_health" "origin" "136 -296 296" } { "classname" "path_corner" "origin" "-536 -704 472" "targetname" "t42" "target" "t41" } { "classname" "path_corner" "origin" "-576 -416 472" "targetname" "t41" "target" "t42" } { "classname" "monster_knight" "origin" "-578 -654 480" "target" "t41" "spawnflags" "1" } { "classname" "item_shells" "origin" "-368 -752 456" } { "classname" "item_health" "origin" "-16 -520 360" "spawnflags" "1" } { "classname" "item_health" "origin" "-16 -576 360" "spawnflags" "1" } { "classname" "light" "origin" "1848 -568 320" "light" "200" } { "classname" "light" "origin" "1760 -560 408" "light" "200" } { "classname" "light" "origin" "1624 -560 352" "light" "150" } { "targetname" "t43" "angle" "270" "origin" "800 368 312" "classname" "info_teleport_destination" } { "origin" "752 168 296" "classname" "item_health" } { "target" "t43" "classname" "trigger_teleport" "model" "*1" } { "origin" "1712 -568 256" "classname" "item_health" } { "classname" "monster_ogre" "origin" "1494 1134 208" "angle" "270" "target" "t22" } { "light" "300" "origin" "1856 1288 384" "classname" "light" } { "classname" "light" "origin" "1136 1288 384" } { "light" "200" "origin" "1920 328 380" "classname" "light" } { "target" "t122" "spawnflags" "2048" "sounds" "1" "classname" "item_key1" "origin" "880 -300 464" } { "light" "300" "origin" "648 -384 430" "classname" "light_flame_small_yellow" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1104 -224 406" } { "light" "250" "origin" "1456 -128 406" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "988 532 353" "light" "175" } { "light" "125" "origin" "1100 648 328" "classname" "light" } { "origin" "1616 936 310" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "1360 936 310" } { "origin" "1792 504 390" "classname" "light_flame_small_yellow" "light" "300" } { "origin" "1972 -252 332" "classname" "info_null" "targetname" "t47" } { "light" "800" "origin" "1992 -252 336" "classname" "light" "target" "t47" } { "classname" "info_null" "origin" "1948 -292 332" "targetname" "t48" } { "light" "800" "classname" "light" "origin" "1948 -312 336" "target" "t48" } { "origin" "880 -328 562" "classname" "light_flame_small_yellow" "light" "300" } { "classname" "light" "origin" "1056 -1288 504" } { "origin" "1184 -1288 504" "classname" "light" } { "classname" "light" "origin" "1312 -1288 504" } { "origin" "1440 -1288 504" "classname" "light" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t50" "model" "*2" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t50" "model" "*3" } { "classname" "trigger_once" "target" "t50" "model" "*4" } { "classname" "light" "origin" "1368 -1016 504" "light" "200" } { "light" "200" "origin" "1120 -1024 504" "classname" "light" } { "classname" "light" "origin" "1248 -1184 464" "light" "175" } { "classname" "light" "origin" "776 -480 480" "light" "225" } { "classname" "light" "origin" "1904 -144 168" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1706 -206 316" "light" "300" } { "light" "300" "origin" "2134 -34 316" "classname" "light_torch_small_walltorch" } { "origin" "1152 -296 422" "classname" "light_flame_small_yellow" "light" "250" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1152 -760 422" } { "origin" "1528 -556 478" "classname" "light_flame_small_yellow" "light" "250" } { "targetname" "t52" "origin" "1532 -552 328" "classname" "info_null" } { "origin" "1340 -544 384" "classname" "item_armor2" } { "sounds" "1" "targetname" "t53" "lip" "64" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*5" } { "sounds" "1" "targetname" "t53" "classname" "func_door" "angle" "-1" "wait" "-1" "lip" "64" "model" "*6" } { "targetname" "t53" "target" "t54" "classname" "trigger_teleport" "spawnflags" "2" "model" "*7" } { "targetname" "t54" "angle" "180" "origin" "1408 -688 449" "classname" "info_teleport_destination" } { "targetname" "t53" "target" "t57" "classname" "trigger_teleport" "spawnflags" "2" "model" "*8" } { "targetname" "t57" "angle" "180" "origin" "1408 -400 361" "classname" "info_teleport_destination" } { "spawnflags" "768" "targetname" "t53" "angle" "180" "origin" "1912 -856 217" "classname" "monster_wizard" } { "spawnflags" "768" "targetname" "t53" "classname" "monster_wizard" "origin" "1912 -936 217" "angle" "180" } { "targetname" "t50" "angle" "90" "origin" "1320 -1112 441" "classname" "monster_knight" } { "spawnflags" "256" "targetname" "t50" "angle" "0" "origin" "1056 -1144 441" "classname" "monster_knight" } { "sounds" "1" "targetname" "t61" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*9" } { "sounds" "3" "lip" "64" "spawnflags" "1" "targetname" "t58" "angle" "270" "wait" "-1" "classname" "func_door" "model" "*10" } { "sounds" "1" "wait" "-1" "angle" "270" "target" "t58" "classname" "func_button" "model" "*11" } { "target" "t61" "classname" "trigger_once" "model" "*12" } { "light" "225" "origin" "984 -480 480" "classname" "light" } { "light" "175" "origin" "880 -368 176" "classname" "light" } { "classname" "light" "origin" "880 -592 240" "light" "175" } { "light" "200" "origin" "880 -488 184" "classname" "light" } { "light" "150" "origin" "880 -304 472" "classname" "light" } { "classname" "light" "origin" "-96 308 864" "light" "850" } { "origin" "-32 -440 624" "classname" "light" } { "sounds" "1" "targetname" "t73" "wait" "-1" "lip" "196" "angle" "-1" "classname" "func_door" "model" "*13" } { "light" "300" "origin" "104 144 688" "classname" "light" } { "classname" "light" "origin" "-264 144 688" "light" "300" } { "sounds" "1" "targetname" "t73" "wait" "-1" "classname" "func_door" "angle" "-1" "lip" "196" "model" "*14" } { "classname" "light_flame_small_yellow" "origin" "-24 -232 414" "light" "250" } { "lip" "-2" "sounds" "3" "speed" "350" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*15" } { "target" "t63" "targetname" "t62" "origin" "-12 312 264" "classname" "path_corner" } { "target" "t64" "targetname" "t63" "origin" "-12 312 356" "classname" "path_corner" } { "wait" "-1" "target" "t66" "targetname" "t64" "classname" "path_corner" "origin" "-13 440 355" } { "sounds" "1" "targetname" "t71" "wait" "-1" "target" "t65" "angle" "-2" "classname" "func_button" "model" "*16" } { "target" "t64" "targetname" "t66" "origin" "-13 440 355" "classname" "path_corner" } { "light" "200" "origin" "-96 440 376" "classname" "light" } { "light" "150" "origin" "8 456 376" "classname" "light" } { "targetname" "t70" "target" "t67" "classname" "path_corner" "origin" "-220 312 264" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "-220 312 356" } { "wait" "-1" "target" "t69" "targetname" "t68" "origin" "-221 440 355" "classname" "path_corner" } { "target" "t68" "targetname" "t69" "classname" "path_corner" "origin" "-221 440 355" } { "classname" "light" "origin" "-200 456 376" "light" "150" } { "targetname" "t65" "target" "t62" "classname" "func_train" "speed" "50" "sounds" "1" "model" "*17" } { "light" "250" "origin" "-96 632 406" "classname" "light_flame_small_yellow" } { "targetname" "t72" "origin" "-96 288 304" "classname" "info_null" } { "light" "450" "target" "t72" "origin" "-96 288 368" "classname" "light" } { "target" "t70" "targetname" "t65" "speed" "50" "classname" "func_train" "sounds" "1" "model" "*18" } { "lip" "-2" "sounds" "0" "speed" "350" "classname" "func_door" "wait" "-1" "angle" "0" "model" "*19" } { "targetname" "t65" "delay" "4.7" "target" "t73" "classname" "trigger_once" "model" "*20" } { "targetname" "t73" "angle" "270" "origin" "-96 552 320" "classname" "monster_demon1" "spawnflags" "1024" } { "targetname" "t74" "angle" "90" "origin" "132 -192 476" "classname" "info_teleport_destination" } { "targetname" "t75" "classname" "info_teleport_destination" "origin" "-328 -196 476" "angle" "90" } { "target" "t75" "classname" "trigger_teleport" "spawnflags" "1" "model" "*21" } { "target" "t74" "classname" "trigger_teleport" "spawnflags" "1" "model" "*22" } { "light" "200" "origin" "-418 306 356" "classname" "light" } { "classname" "light" "origin" "260 308 356" "light" "200" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*23" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "0" "classname" "func_door" "model" "*24" } { "sounds" "0" "wait" "-1" "angle" "0" "targetname" "t73" "classname" "func_door" "model" "*25" } { "sounds" "0" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*26" } { "sounds" "3" "wait" "-1" "angle" "-2" "targetname" "t73" "classname" "func_door" "model" "*27" } { "classname" "light" "origin" "-96 24 360" "light" "100" } { "light" "100" "origin" "-96 -40 360" "classname" "light" } { "classname" "light" "origin" "-160 -568 624" } { "origin" "-160 -440 624" "classname" "light" } { "classname" "light" "origin" "-32 -568 624" } { "classname" "light" "origin" "-96 -88 484" "light" "150" } { "classname" "light" "origin" "-440 -408 804" "light" "450" } { "classname" "light" "origin" "600 -128 352" "light" "200" } { "classname" "light" "origin" "576 -608 504" "light" "250" } { "classname" "light" "origin" "384 -504 392" "light" "250" } { "classname" "light" "origin" "1264 240 295" "light" "250" } { "light" "250" "origin" "944 240 295" "classname" "light" } { "classname" "path_corner" "origin" "1480 704 264" "targetname" "t77" "target" "t78" } { "classname" "path_corner" "origin" "1448 656 264" "targetname" "t78" "target" "t30" } { "classname" "path_corner" "origin" "1264 640 304" "targetname" "t80" "target" "t5" } { "classname" "path_corner" "origin" "1328 640 304" "targetname" "t79" "target" "t80" } { "light" "200" "origin" "1488 -392 216" "classname" "light" } { "classname" "path_corner" "origin" "816 80 304" "targetname" "t83" "target" "t82" "spawnflags" "256" } { "origin" "816 312 304" "classname" "path_corner" "targetname" "t82" "target" "t83" "spawnflags" "256" } { "classname" "monster_army" "origin" "806 206 320" "angle" "90" "target" "t82" "spawnflags" "256" } { "classname" "trigger_once" "target" "t84" "model" "*28" } { "classname" "monster_ogre" "origin" "1790 -146 312" "angle" "90" "targetname" "t84" } { "classname" "path_corner" "origin" "1088 -672 296" "target" "t85" "targetname" "t88" } { "origin" "1088 -376 296" "classname" "path_corner" "targetname" "t85" "target" "t86" } { "classname" "path_corner" "origin" "1088 -376 296" "targetname" "t87" "target" "t88" } { "origin" "1448 -376 296" "classname" "path_corner" "targetname" "t86" "target" "t87" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1086 -498 312" "angle" "270" "target" "t88" } { "spawnflags" "256" "classname" "trigger_once" "target" "t89" "model" "*29" } { "classname" "item_health" "origin" "352 -752 408" "spawnflags" "1025" } { "spawnflags" "1025" "origin" "352 -792 408" "classname" "item_health" } { "classname" "item_health" "origin" "352 -832 408" "spawnflags" "1" } { "classname" "path_corner" "origin" "408 -776 416" "targetname" "t94" "target" "t95" } { "origin" "400 -1088 416" "classname" "path_corner" "targetname" "t95" "target" "t94" } { "classname" "path_corner" "origin" "584 -1096 416" "targetname" "t92" "target" "t93" } { "origin" "584 -792 416" "classname" "path_corner" "targetname" "t93" "target" "t92" } { "classname" "monster_army" "origin" "390 -970 432" "angle" "0" "target" "t94" } { "classname" "monster_army" "origin" "566 -970 432" "angle" "270" "target" "t92" } { "classname" "path_corner" "origin" "208 -304 304" "targetname" "t97" "target" "t96" } { "classname" "path_corner" "origin" "208 -464 304" "targetname" "t96" "target" "t97" } { "spawnflags" "1280" "classname" "path_corner" "origin" "-344 160 304" "targetname" "t100" "target" "t99" } { "spawnflags" "1280" "origin" "168 152 304" "classname" "path_corner" "targetname" "t99" "target" "t100" } { "spawnflags" "1280" "classname" "monster_ogre" "origin" "240 152 320" "angle" "180" "target" "t99" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "-392 80 320" "angle" "0" "targetname" "t101" } { "spawnflags" "768" "classname" "trigger_once" "target" "t101" "model" "*30" } { "classname" "item_health" "origin" "40 -16 464" } { "origin" "80 -48 464" "classname" "item_health" } { "origin" "520 -72 296" "classname" "item_shells" } { "spawnflags" "1" "origin" "-424 -216 296" "classname" "item_shells" } { "spawnflags" "769" "angle" "270" "origin" "880 -400 568" "classname" "monster_wizard" } { "light" "200" "origin" "432 176 152" "classname" "light" } { "light" "150" "origin" "432 -56 256" "classname" "light" } { "origin" "264 -96 300" "classname" "item_health" } { "classname" "item_health" "origin" "264 -140 300" } { "spawnflags" "1" "origin" "1184 1568 240" "classname" "item_health" } { "classname" "item_health" "origin" "1184 1616 240" "spawnflags" "1" } { "light" "150" "origin" "1496 1112 108" "classname" "light" } { "light" "200" "origin" "1120 1152 96" "classname" "light" } { "light" "200" "origin" "1080 692 184" "classname" "light" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "832 1184 294" } { "origin" "464 536 358" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "600 704 334" } { "light" "150" "origin" "1736 1096 110" "classname" "light" } { "light" "100" "origin" "832 1056 134" "classname" "light" } { "light" "150" "origin" "784 704 294" "classname" "light" } { "origin" "856 592 182" "classname" "item_health" } { "classname" "item_health" "origin" "824 552 182" } { "classname" "info_player_deathmatch" "origin" "-416 -144 320" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "168 -480 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1496 1328 200" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "1936 -136 312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "936 -1216 432" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "792 -992 440" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1080 -720 312" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "408 -752 432" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "792 -208 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "784 808 206" "angle" "225" } { "sounds" "3" "wait" "3" "angle" "90" "classname" "func_door" "model" "*31" } { "sounds" "0" "wait" "3" "angle" "270" "classname" "func_door" "model" "*32" } { "spawnflags" "1" "origin" "680 832 182" "classname" "item_shells" } { "origin" "1392 240 300" "classname" "weapon_supershotgun" } { "spawnflags" "769" "angle" "270" "origin" "954 -754 444" "classname" "monster_ogre" } { "spawnflags" "1" "origin" "520 -1280 408" "classname" "item_shells" } { "light" "200" "origin" "-612 -500 548" "classname" "light" } { "classname" "func_door" "angle" "90" "targetname" "t110" "wait" "-1" "model" "*33" } { "sounds" "3" "classname" "func_door" "angle" "270" "wait" "-1" "model" "*34" } { "classname" "trigger_once" "target" "t110" "model" "*35" } { "classname" "trigger_changelevel" "map" "e1m3" "model" "*36" } { "spawnflags" "1792" "origin" "680 728 184" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1496 1256 176" "classname" "weapon_nailgun" } { "angle" "180" "spawnflags" "1792" "origin" "-96 -496 360" "classname" "weapon_supernailgun" } { "spawnflags" "1794" "origin" "-112 -8 464" "classname" "item_health" } { "spawnflags" "1793" "origin" "-112 -568 360" "classname" "item_spikes" } { "spawnflags" "1792" "origin" "1616 1424 176" "classname" "item_spikes" } { "spawnflags" "1792" "classname" "item_spikes" "origin" "1656 1424 176" } { "spawnflags" "1792" "origin" "1696 1424 176" "classname" "item_spikes" } { "spawnflags" "768" "target" "t34" "angle" "315" "origin" "1070 646 312" "classname" "monster_ogre" } { "spawnflags" "768" "targetname" "t84" "angle" "90" "origin" "1624 88 376" "classname" "monster_wizard" } { "spawnflags" "768" "angle" "90" "targetname" "t84" "origin" "1866 -378 312" "classname" "monster_ogre" } { "angle" "45" "origin" "1088 -1096 440" "classname" "monster_knight" "targetname" "t50" } { "spawnflags" "768" "classname" "monster_knight" "origin" "1400 -1144 440" "angle" "90" "targetname" "t50" } { "spawnflags" "256" "target" "t111" "targetname" "t112" "origin" "896 -1216 416" "classname" "path_corner" } { "spawnflags" "256" "target" "t112" "targetname" "t111" "classname" "path_corner" "origin" "704 -1216 416" } { "spawnflags" "257" "target" "t111" "angle" "180" "origin" "758 -1218 432" "classname" "monster_army" } { "spawnflags" "768" "target" "t114" "targetname" "t113" "origin" "-96 -520 368" "classname" "path_corner" } { "spawnflags" "768" "target" "t113" "targetname" "t114" "origin" "-96 -152 304" "classname" "path_corner" } { "targetname" "t116" "spawnflags" "769" "target" "t113" "angle" "270" "origin" "-98 -194 320" "classname" "monster_ogre" } { "spawnflags" "1536" "origin" "1936 -96 289" "classname" "item_health" } { "spawnflags" "1025" "origin" "1040 -1200 417" "classname" "item_health" } { "spawnflags" "769" "target" "t117" "angle" "315" "origin" "-560 -312 592" "classname" "monster_wizard" } { "spawnflags" "768" "target" "t118" "targetname" "t117" "origin" "-528 -344 576" "classname" "path_corner" } { "spawnflags" "768" "target" "t117" "targetname" "t118" "origin" "-352 -656 576" "classname" "path_corner" } { "classname" "light" "origin" "1360 976 224" "light" "150" } { "light" "150" "origin" "1616 976 224" "classname" "light" } { "classname" "light" "origin" "1208 1296 368" "light" "250" } { "origin" "1784 1288 368" "classname" "light" "light" "250" } { "classname" "light" "origin" "1496 1664 336" "light" "250" } { "classname" "light" "origin" "1752 1176 112" "light" "150" } { "light" "200" "origin" "1776 976 112" "classname" "light" } { "classname" "light" "origin" "1216 976 112" "light" "200" } { "light" "150" "origin" "1224 1176 112" "classname" "light" } { "classname" "light" "origin" "1496 1432 520" "light" "250" } { "classname" "light" "origin" "1496 1304 264" "light" "200" } { "classname" "light" "origin" "1496 1432 288" "light" "200" } { "classname" "light" "origin" "1608 1120 88" "light" "150" } { "light" "150" "origin" "1384 1120 88" "classname" "light" } { "classname" "light" "origin" "1496 864 368" "light" "150" } { "light" "175" "origin" "980 764 353" "classname" "light" } { "classname" "light" "origin" "1228 764 353" "light" "175" } { "classname" "light" "origin" "1104 464 353" "light" "200" } { "classname" "light" "origin" "1104 -40 423" "light" "200" } { "light" "150" "origin" "1416 -128 367" "classname" "light" } { "classname" "light" "origin" "1104 -184 367" "light" "200" } { "classname" "light" "origin" "1184 56 423" "light" "150" } { "light" "150" "origin" "1024 56 423" "classname" "light" } { "classname" "light" "origin" "1272 -64 399" "light" "150" } { "light" "150" "origin" "888 -64 399" "classname" "light" } { "classname" "light" "origin" "1104 152 129" "light" "300" } { "classname" "light" "origin" "976 392 129" "light" "200" } { "classname" "light" "origin" "1104 656 120" } { "classname" "light" "origin" "896 712 144" "light" "200" } { "classname" "light" "origin" "640 704 280" "light" "200" } { "classname" "light" "origin" "464 496 296" "light" "150" } { "classname" "light" "origin" "888 1152 96" "light" "200" } { "classname" "light" "origin" "840 880 240" "light" "200" } { "classname" "light" "origin" "848 584 240" "light" "150" } { "classname" "light" "origin" "784 160 144" "light" "200" } { "classname" "light" "origin" "440 336 144" "light" "150" } { "light" "150" "origin" "584 336 144" "classname" "light" } { "classname" "light" "origin" "432 24 136" "light" "150" } { "classname" "light" "origin" "656 328 224" "light" "200" } { "classname" "light" "origin" "432 -128 312" "light" "200" } { "classname" "light" "origin" "600 -384 360" "light" "200" } { "origin" "520 -128 406" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light" "origin" "424 -320 352" "light" "200" } { "classname" "light" "origin" "664 -1216 472" "light" "150" } { "classname" "light" "origin" "336 -1208 504" "light" "150" } { "light" "150" "origin" "336 -1008 504" "classname" "light" } { "classname" "light" "origin" "336 -816 504" "light" "150" } { "classname" "light" "origin" "880 -1000 496" "light" "200" } { "classname" "light" "origin" "880 -792 496" "light" "200" } { "classname" "light" "origin" "880 -376 304" "light" "200" } { "classname" "light" "origin" "1048 -912 480" "light" "225" } { "classname" "light" "origin" "1120 -1192 468" "light" "150" } { "light" "150" "origin" "1376 -1192 468" "classname" "light" } { "classname" "light" "origin" "1472 -912 464" "light" "175" } { "classname" "light" "origin" "880 -304 480" "light" "100" } { "classname" "light" "origin" "880 -680 480" "light" "175" } { "classname" "light" "origin" "1600 -704 484" "light" "150" } { "classname" "light" "origin" "1504 -704 348" "light" "175" } { "light" "175" "origin" "1336 -704 348" "classname" "light" } { "classname" "light" "origin" "1152 -640 332" "light" "200" } { "classname" "light" "origin" "1096 -552 348" "light" "150" } { "light" "200" "origin" "1160 -456 332" "classname" "light" } { "light" "150" "origin" "1216 -384 348" "classname" "light" } { "classname" "light" "origin" "1344 -384 348" "light" "150" } { "classname" "light" "origin" "1544 392 156" "light" "225" } { "light" "225" "origin" "1848 248 156" "classname" "light" } { "classname" "light" "origin" "1936 136 156" "light" "225" } { "light" "200" "origin" "2096 -80 156" "classname" "light" } { "light" "200" "origin" "2048 -408 156" "classname" "light" } { "classname" "light" "origin" "1456 -392 444" "light" "225" } { "classname" "light" "origin" "1640 -384 352" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "2134 -474 316" "light" "250" } { "light" "200" "origin" "168 216 496" "classname" "light" } { "classname" "light" "origin" "-328 208 496" "light" "200" } { "light" "200" "origin" "-96 360 432" "classname" "light" } { "classname" "light" "origin" "-96 144 432" "light" "200" } { "light" "200" "origin" "-376 32 432" "classname" "light" } { "light" "200" "origin" "208 -72 432" "classname" "light" } { "light" "150" "origin" "-96 72 360" "classname" "light" } { "light" "150" "origin" "-64 -232 368" "classname" "light" } { "light" "150" "origin" "-96 -320 560" "classname" "light" } { "light" "250" "origin" "-96 -496 448" "classname" "light" } { "light" "150" "origin" "-416 -104 392" "classname" "light" } { "light" "150" "origin" "-344 -152 528" "classname" "light" } { "classname" "light" "origin" "160 -152 528" "light" "150" } { "light" "150" "origin" "-96 8 528" "classname" "light" } { "light" "200" "origin" "-560 -504 688" "classname" "light" } { "classname" "light" "origin" "-440 -368 688" "light" "200" } { "light" "200" "origin" "-440 -656 688" "classname" "light" } { "classname" "light" "origin" "-336 -504 688" "light" "200" } { "classname" "light" "origin" "2084 -208 336" "light" "100" } { "light" "100" "origin" "2012 -252 332" "classname" "light" } { "classname" "light" "origin" "1948 -328 332" "light" "100" } { "light" "150" "origin" "1892 -452 332" "classname" "light" } { "sounds" "1" "targetname" "t120" "wait" "-1" "angle" "-2" "classname" "func_door" "lip" "4" "model" "*37" } { "target" "t120" "classname" "trigger_once" "model" "*38" } { "light" "100" "origin" "2076 -312 336" "classname" "light" } { "sounds" "3" "spawnflags" "2064" "angle" "0" "wait" "-1" "classname" "func_door" "model" "*39" } { "spawnflags" "2064" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*40" } { "light" "100" "origin" "332 -264 356" "classname" "light" } { "classname" "light" "origin" "144 -264 356" "light" "100" } { "light" "100" "origin" "1104 572 316" "classname" "light" } { "target" "t121" "wait" ".8" "classname" "trigger_multiple" "model" "*41" } { "targetname" "t121" "angle" "180" "origin" "2120 -256 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "targetname" "t121" "angle" "90" "origin" "1944 -456 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "light" "150" "origin" "1312 -856 472" "classname" "light" } { "classname" "light" "origin" "1184 -856 472" "light" "175" } { "classname" "light" "origin" "1560 -568 224" "light" "200" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "50" "sounds" "1" "targetname" "t123" "lip" "6" "model" "*42" } { "classname" "trigger_once" "target" "t123" "model" "*43" } { "classname" "light" "origin" "1496 -552 330" "light" "700" "target" "t52" } { "classname" "light" "origin" "1288 80 140" "light" "250" } { "classname" "light" "origin" "1288 400 80" "light" "200" } { "classname" "light" "origin" "1328 -664 160" "light" "200" } { "classname" "item_armor1" "origin" "784 56 304" } { "classname" "light" "origin" "1544 464 352" "light" "75" } { "classname" "func_plat" "model" "*44" } { "classname" "light" "origin" "1496 1192 280" "light" "200" } { "classname" "light" "origin" "1608 1192 136" "light" "100" } { "light" "100" "origin" "1384 1184 136" "classname" "light" } { "light" "100" "origin" "1608 1048 136" "classname" "light" } { "classname" "light" "origin" "1384 1048 136" "light" "100" } { "classname" "light" "origin" "1200 1148 92" "light" "150" } { "light" "150" "origin" "876 -184 367" "classname" "light" } { "classname" "light" "origin" "768 -128 384" "light" "200" } { "classname" "light" "origin" "1104 388 552" "light" "250" } { "light" "200" "origin" "1392 240 384" "classname" "light" } { "classname" "light" "origin" "1392 80 368" "light" "200" } { "classname" "light" "origin" "1272 400 367" "light" "150" } { "light" "150" "origin" "920 400 367" "classname" "light" } { "classname" "light" "origin" "816 208 368" "light" "200" } { "classname" "light" "origin" "800 24 368" "light" "200" } { "classname" "light" "origin" "800 376 385" "light" "150" } { "light" "150" "origin" "1400 400 385" "classname" "light" } { "classname" "path_corner" "origin" "1104 336 300" "target" "t126" "targetname" "t127" } { "origin" "1104 24 300" "classname" "path_corner" "targetname" "t126" "target" "t127" } { "classname" "monster_army" "origin" "1104 424 316" "angle" "270" "target" "t127" } { "targetname" "t128" "origin" "1392 240 308" "classname" "info_null" } { "light" "300" "target" "t128" "origin" "1392 240 376" "classname" "light" } { "targetname" "t129" "angle" "0" "origin" "552 -128 320" "classname" "monster_army" } { "target" "t129" "classname" "trigger_once" "model" "*45" } { "sounds" "1" "classname" "trigger_secret" "model" "*46" } { "sounds" "1" "classname" "trigger_secret" "model" "*47" } { "classname" "light" "origin" "1104 24 536" "light" "350" } { "spawnflags" "1" "classname" "func_door_secret" "angle" "270" "model" "*48" } { "light" "200" "origin" "1680 1552 320" "classname" "light" } { "classname" "light" "origin" "1312 1552 320" "light" "200" } { "classname" "item_spikes" "origin" "1480 1104 68" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1760 -568 256" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1232 -1200 416" } { "message" "This door is opened elsewhere..." "classname" "func_door" "sounds" "3" "angle" "180" "wait" "-1" "targetname" "t122" "speed" "35" "spawnflags" "2048" "model" "*49" } { "classname" "func_door" "angle" "0" "wait" "-1" "speed" "30" "spawnflags" "2048" "model" "*50" } { "classname" "light" "origin" "1496 1600 296" "light" "150 " } { "light" "150" "origin" "1568 1664 296" "classname" "light" } { "classname" "light" "origin" "1424 1664 296" "light" "150" } { "classname" "light" "origin" "1328 1424 296" "light" "200" } { "light" "250" "origin" "1696 1416 296" "classname" "light" } { "classname" "monster_army" "origin" "1592 1296 200" "angle" "270" } { "spawnflags" "768" "classname" "monster_demon1" "origin" "-96 576 320" "angle" "270" "targetname" "t73" "target" "t143" } { "classname" "path_corner" "origin" "1392 416 304" "targetname" "t131" "target" "t130" "spawnflags" "768" } { "origin" "1392 296 304" "classname" "path_corner" "targetname" "t130" "target" "t131" "spawnflags" "768" } { "classname" "monster_army" "origin" "1392 352 320" "angle" "270" "target" "t130" "spawnflags" "768" } { "target" "t132" "targetname" "t133" "origin" "296 -328 304" "classname" "path_corner" } { "target" "t133" "targetname" "t132" "classname" "path_corner" "origin" "472 -416 304" } { "spawnflags" "1" "target" "t132" "angle" "90" "origin" "472 -456 320" "classname" "monster_army" } { "spawnflags" "1" "targetname" "t89" "angle" "135" "origin" "1712 -784 376" "classname" "monster_army" } { "target" "t135" "spawnflags" "256" "targetname" "t134" "origin" "400 -1128 416" "classname" "path_corner" } { "target" "t134" "spawnflags" "256" "targetname" "t135" "classname" "path_corner" "origin" "400 -1248 416" } { "target" "t134" "spawnflags" "257" "angle" "90" "origin" "408 -1208 432" "classname" "monster_army" } { "targetname" "t101" "angle" "90" "origin" "-288 -24 488" "classname" "monster_army" "spawnflags" "768" } { "spawnflags" "768" "targetname" "t101" "classname" "monster_army" "origin" "136 -128 488" "angle" "90" } { "spawnflags" "1792" "origin" "-264 -24 464" "classname" "item_rockets" } { "spawnflags" "2048" "origin" "-240 -8 464" "classname" "item_spikes" } { "classname" "monster_ogre" "origin" "-304 -304 488" "angle" "225" "spawnflags" "769" } { "classname" "func_wall" "spawnflags" "768" "model" "*51" } { "classname" "trap_spikeshooter" "origin" "2048 -48 332" "angle" "270" "spawnflags" "769" "targetname" "t121" } { "origin" "2048 -476 332" "classname" "info_null" "targetname" "t136" } { "style" "32" "origin" "2048 -456 336" "classname" "light" "light" "800" "spawnflags" "1" "target" "t136" "targetname" "t137" } { "style" "32" "classname" "trigger_once" "spawnflags" "768" "target" "t137" "model" "*52" } { "classname" "monster_wizard" "origin" "672 328 384" "angle" "180" "spawnflags" "768" "targetname" "t138" } { "classname" "trigger_once" "target" "t138" "model" "*53" } { "style" "32" "classname" "light" "origin" "2004 -52 332" "light" "100" "spawnflags" "1" "targetname" "t137" } { "classname" "item_shells" "origin" "1416 224 300" "spawnflags" "768" } { "classname" "path_corner" "origin" "-344 136 304" "targetname" "t139" "target" "t140" "spawnflags" "768" } { "origin" "168 128 304" "classname" "path_corner" "target" "t139" "targetname" "t140" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "-400 168 320" "spawnflags" "768" "target" "t139" } { "classname" "trap_spikeshooter" "origin" "2120 -256 332" "angle" "180" "spawnflags" "769" "targetname" "t121" } { "classname" "trap_spikeshooter" "origin" "1944 -456 332" "targetname" "t121" "angle" "90" "spawnflags" "769" } { "classname" "item_spikes" "origin" "-336 -80 470" "spawnflags" "768" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t142" "spawnflags" "2" "model" "*54" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t141" "spawnflags" "2" "model" "*55" } { "classname" "monster_demon1" "origin" "32 840 359" "angle" "270" "targetname" "t143" "spawnflags" "768" } { "angle" "270" "origin" "-192 840 359" "classname" "monster_demon1" "targetname" "t143" "spawnflags" "768" } { "classname" "info_teleport_destination" "origin" "80 216 303" "angle" "270" "targetname" "t141" } { "angle" "270" "origin" "-264 224 303" "classname" "info_teleport_destination" "targetname" "t142" } { "wait" "-1" "target" "t53" "health" "1" "classname" "func_button" "model" "*56" } { "spawnflags" "768" "angle" "270" "origin" "1408 1296 200" "classname" "monster_army" } { "classname" "item_shells" "origin" "772 -856 420" "spawnflags" "768" } { "spawnflags" "1792" "origin" "1248 -1128 420" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "864 -312 440" "classname" "item_rockets" } { "classname" "trigger_once" "message" "Pass through the arch to exit..." "model" "*57" } { "mangle" "20 300 0" "classname" "info_intermission" "origin" "-224 424 512" } { "mangle" "20 45 0" "origin" "1048 -744 488" "classname" "info_intermission" } { "mangle" "20 270 0" "origin" "1104 424 528" "classname" "info_intermission" } { "mangle" "20 45 0" "origin" "1240 984 416" "classname" "info_intermission" } { "sounds" "1" "speed" "20" "classname" "func_button" "angle" "0" "wait" "-1" "target" "t144" "model" "*58" } { "classname" "light" "origin" "400 -1392 480" "light" "150" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "20" "sounds" "1" "targetname" "t144" "model" "*59" } { "classname" "trigger_secret" "model" "*60" } { "classname" "item_artifact_super_damage" "origin" "400 -1360 432" } { "classname" "item_spikes" "origin" "808 -632 192" "spawnflags" "2049" } { "classname" "item_health" "origin" "924 -632 192" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "880 -616 192" "spawnflags" "1792" } { "classname" "ambient_drip" "origin" "842 978 344" } { "classname" "ambient_drip" "origin" "546 330 400" } { "classname" "info_player_coop" "origin" "1608 1664 264" "angle" "270" } { "angle" "270" "origin" "1392 1664 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1496 1560 264" "angle" "270" } { "spawnflags" "256" "angle" "270" "origin" "232 -176 320" "classname" "monster_ogre" } { "spawnflags" "1280" "angle" "225" "origin" "-368 -312 480" "classname" "monster_knight" } { "spawnflags" "1792" "classname" "func_wall" "model" "*61" } { "origin" "1810 274 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1802 -102 200" } { "origin" "2050 -214 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "2002 -390 200" } { "origin" "1738 -398 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1346 -398 200" } { "origin" "1138 -542 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "882 -494 200" } { "classname" "ambient_swamp1" "origin" "1722 1090 176" } { "origin" "1242 1090 176" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "1106 642 192" } { "origin" "1346 242 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "866 210 192" } { "classname" "ambient_swamp1" "origin" "1802 90 192" } { "origin" "1546 -398 192" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "2042 -310 192" } { "origin" "1178 -398 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp2" "origin" "1202 -678 192" } quakespasm-0.93.0/Misc/qs_pak/maps/e2m7.ent0000644000000000000000000014260112403131422017042 0ustar rootroot{ "wad" "gfx/tim.wad" "classname" "worldspawn" "sounds" "7" "worldtype" "0" "message" "the Underearth" } { "angle" "90" "origin" "1136 -1100 -72" "classname" "info_player_start" } { "origin" "1184 -776 -152" "classname" "light" "light" "150" } { "classname" "light" "origin" "1704 -584 -184" "light" "150" } { "classname" "light" "origin" "1640 -688 -184" "light" "150" } { "classname" "light" "origin" "1696 -888 -192" "light" "150" } { "classname" "light" "origin" "1088 -960 -152" "light" "150" } { "classname" "light" "origin" "1248 -960 -152" "light" "150" } { "classname" "light" "origin" "1016 -768 -152" "light" "100" } { "classname" "light" "origin" "896 -920 -152" "light" "150" } { "light" "100" "origin" "1584 -208 -112" "classname" "light" } { "light" "100" "origin" "1776 -208 -112" "classname" "light" } { "light" "150" "origin" "1584 -88 -112" "classname" "light" } { "origin" "1774 58 -76" "classname" "light_torch_small_walltorch" } { "light" "100" "origin" "1584 -488 -232" "classname" "light" } { "light" "100" "origin" "1768 -480 -232" "classname" "light" } { "light" "150" "origin" "1752 -112 -176" "classname" "light" } { "light" "150" "origin" "1592 -120 -176" "classname" "light" } { "light" "150" "origin" "1592 -248 -192" "classname" "light" } { "light" "200" "origin" "1768 -240 -192" "classname" "light" } { "light" "150" "origin" "1676 -220 -188" "classname" "light" } { "light" "150" "origin" "1672 -40 -136" "classname" "light" } { "light" "150" "origin" "1676 -376 -252" "classname" "light" } { "light" "250" "origin" "1112 952 -92" "classname" "light" } { "light" "200" "origin" "1280 928 -152" "classname" "light" } { "classname" "light" "origin" "704 952 -92" "light" "250" } { "classname" "light" "origin" "824 1112 -92" "light" "200" } { "classname" "light" "origin" "952 760 -92" "light" "200" } { "classname" "light" "origin" "824 760 -92" "light" "200" } { "light" "250" "origin" "952 1112 -92" "classname" "light" } { "classname" "light" "origin" "1128 -848 288" "light" "500" } { "classname" "light" "origin" "1144 -432 288" } { "classname" "light" "origin" "864 -552 272" "light" "200" } { "classname" "light" "origin" "1392 -568 168" "light" "200" } { "classname" "light" "origin" "1416 -592 -24" "light" "150" } { "classname" "light" "origin" "888 -584 -24" "light" "150" } { "classname" "light_torch_small_walltorch" "origin" "1058 -466 -24" "light" "225" } { "origin" "1214 -466 -24" "classname" "light_torch_small_walltorch" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "1198 -66 40" "light" "300" } { "classname" "light" "origin" "1144 -204 172" "light" "150" } { "classname" "light" "origin" "1144 -292 -32" "light" "150" } { "classname" "item_spikes" "origin" "880 -592 -96" "spawnflags" "1" } { "classname" "item_health" "origin" "1068 -944 -96" } { "classname" "light" "origin" "1128 -1084 96" "light" "300" } { "classname" "light" "origin" "888 -848 248" "light" "150" } { "light" "150" "origin" "1504 -896 248" "classname" "light" } { "classname" "light" "origin" "1304 -1048 24" "light" "225" } { "classname" "light" "origin" "1520 -872 -192" "light" "150" } { "light" "100" "origin" "1384 -776 -184" "classname" "light" } { "classname" "light" "origin" "1368 -912 -184" "light" "100" } { "classname" "light" "origin" "1584 496 148" "light" "200" } { "light" "200" "origin" "1488 496 148" "classname" "light" } { "classname" "light" "origin" "1688 164 148" "light" "200" } { "light" "200" "origin" "1352 496 148" "classname" "light" } { "classname" "light" "origin" "1608 496 -36" "light" "200" } { "light" "200" "origin" "1456 496 -36" "classname" "light" } { "classname" "light" "origin" "1692 600 -12" "light" "200" } { "light" "200" "origin" "1274 618 -64" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1274 378 -64" "light" "200" } { "classname" "light" "origin" "1256 496 -40" "light" "200" } { "spawnflags" "2056" "wait" "-1" "classname" "func_door" "angle" "270" "model" "*1" } { "wait" "-1" "spawnflags" "2056" "sounds" "3" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "200" "origin" "1968 1792 -257" "classname" "light" } { "light" "250" "origin" "1880 1792 -105" "classname" "light" } { "classname" "light" "origin" "1976 1944 56" "light" "150" } { "light" "150" "origin" "1992 1624 64" "classname" "light" } { "classname" "light" "origin" "1944 1736 64" "light" "150" } { "light" "150" "origin" "1944 1832 64" "classname" "light" } { "classname" "light" "origin" "1128 496 -24" "light" "250" } { "light" "150" "origin" "1120 632 -24" "classname" "light" } { "light" "200" "origin" "928 544 -24" "classname" "light" } { "classname" "light" "origin" "640 664 -8" "light" "200" } { "light" "200" "origin" "848 672 -8" "classname" "light" } { "classname" "light" "origin" "1024 544 -188" "light" "175" } { "light" "250" "origin" "64 192 136" "classname" "light" } { "classname" "light" "origin" "528 184 136" "light" "250" } { "light" "200" "origin" "72 408 8" "classname" "light" } { "classname" "light" "origin" "80 -48 8" "light" "200" } { "light" "250" "origin" "400 384 80" "classname" "light" } { "classname" "light" "origin" "392 -16 80" "light" "250" } { "classname" "light" "origin" "312 184 -80" "light" "200" } { "light" "200" "origin" "440 184 -80" "classname" "light" } { "classname" "light" "origin" "504 368 -120" "light" "250" } { "light" "150" "origin" "632 192 -120" "classname" "light" } { "classname" "light" "origin" "504 -16 -120" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "774 446 -172" "light" "250" } { "origin" "774 -70 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "light" "250" "origin" "896 -128 152" "classname" "light" } { "classname" "light" "origin" "896 184 152" "light" "250" } { "light" "150" "origin" "656 184 216" "classname" "light" } { "classname" "light" "origin" "304 368 -152" "light" "200" } { "light" "200" "origin" "0 480 -168" "classname" "light" } { "classname" "light" "origin" "96 376 -168" "light" "200" } { "classname" "light" "origin" "16 1480 -96" "light" "200" } { "light" "200" "origin" "1280 1824 -120" "classname" "light" } { "light" "200" "origin" "504 1816 -120" "classname" "light" } { "classname" "light" "origin" "712 1808 -120" "light" "200" } { "light" "200" "origin" "1064 1808 -120" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "858 1950 -172" "light" "250" } { "origin" "658 1950 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "666 1682 -172" "light" "250" } { "origin" "858 1682 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light" "origin" "1248 1384 -32" "light" "200" } { "classname" "light" "origin" "1688 936 -136" "light" "200" } { "light" "250" "origin" "1856 1444 -52" "classname" "light" } { "light" "150" "origin" "1864 1316 -192" "classname" "light" } { "classname" "light" "origin" "1776 1212 -192" "light" "150" } { "light" "150" "origin" "1696 1076 -192" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "1770 730 -64" "light" "250" } { "classname" "light" "origin" "1760 1720 -201" "light" "250" } { "classname" "light" "origin" "1632 1520 -201" "light" "200" } { "classname" "light" "origin" "1984 1504 -201" "light" "200" } { "light" "250" "classname" "light_torch_small_walltorch" "origin" "1874 2104 -300" } { "light" "250" "origin" "1712 2104 -300" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light" "origin" "1792 2048 48" } { "classname" "light_flame_large_yellow" "origin" "1362 1778 0" } { "classname" "light" "origin" "1408 1776 -124" "light" "200" } { "classname" "light" "origin" "1520 1880 -84" "light" "175" } { "light" "175" "origin" "1416 1512 -84" "classname" "light" } { "classname" "light" "origin" "1376 1568 -164" "light" "150" } { "light" "150" "origin" "1416 1888 -164" "classname" "light" } { "classname" "light" "origin" "1544 2072 -164" "light" "150" } { "classname" "light" "origin" "1552 1968 36" "light" "250" } { "classname" "light" "origin" "1416 1968 -16" "light" "175" } { "light" "175" "origin" "1416 2176 -16" "classname" "light" } { "classname" "light" "origin" "1240 2176 -16" "light" "175" } { "light" "175" "origin" "1240 2000 -16" "classname" "light" } { "classname" "light" "origin" "1264 1576 -72" "light" "200" } { "light" "200" "origin" "992 1480 -40" "classname" "light" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "968 1632 -132" } { "light" "200" "origin" "968 1328 -132" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1474 2234 -133" "light" "200" } { "origin" "1182 2234 -133" "classname" "light_torch_small_walltorch" "light" "200" } { "classname" "light" "origin" "1936 1480 48" "light" "150" } { "light" "150" "origin" "1544 1528 64" "classname" "light" } { "classname" "light" "origin" "1472 1488 24" "light" "150" } { "classname" "light" "origin" "1400 1664 96" "light" "150" } { "light" "200" "origin" "1792 2176 -221" "classname" "light" } { "classname" "light" "origin" "1880 2288 -221" "light" "200" } { "light" "200" "origin" "2048 2288 -221" "classname" "light" } { "classname" "light" "origin" "2128 2208 -221" "light" "200" } { "light" "200" "origin" "2160 1992 -205" "classname" "light" } { "origin" "2288 1952 -29" "classname" "light" } { "light" "250" "origin" "2274 1738 -172" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2274 1682 -172" "light" "250" } { "light" "200" "origin" "2376 2184 -152" "classname" "light" } { "light" "200" "origin" "2618 1658 -169" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2618 1368 -169" "light" "200" } { "light" "200" "origin" "2176 1488 -169" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "2298 626 24" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1930 738 24" "light" "200" } { "light" "200" "origin" "2050 394 24" "classname" "light_torch_small_walltorch" } { "classname" "func_plat" "model" "*3" } { "light" "150" "origin" "2152 1784 -312" "classname" "light" } { "classname" "light" "origin" "2232 936 -4" "light" "200" } { "classname" "light" "origin" "2232 1040 48" } { "classname" "light_torch_small_walltorch" "origin" "2034 1034 -164" "light" "200" } { "classname" "light" "origin" "2304 1040 280" "light" "200" } { "light" "200" "origin" "2168 1040 280" "classname" "light" } { "origin" "2130 2452 -112" "classname" "light_flame_large_yellow" "light" "250" } { "classname" "light_flame_large_yellow" "origin" "1858 2452 -112" "light" "250" } { "classname" "light" "origin" "2132 2416 -188" "light" "150" } { "light" "150" "origin" "1860 2416 -188" "classname" "light" } { "light" "200" "origin" "2256 1968 -453" "classname" "light" } { "classname" "light" "origin" "2256 2184 -453" "light" "200" } { "light" "200" "origin" "2216 2384 -453" "classname" "light" } { "classname" "light" "origin" "1792 2400 -453" "light" "200" } { "light" "175" "origin" "1984 2400 -453" "classname" "light" } { "light" "150" "origin" "2168 1608 -9" "classname" "light" } { "classname" "light" "origin" "2368 1600 -9" "light" "150" } { "light" "150" "origin" "2240 1424 44" "classname" "light" } { "classname" "light" "origin" "2400 1424 44" "light" "150" } { "light" "150" "origin" "2560 1424 44" "classname" "light" } { "classname" "light" "origin" "2560 1560 44" "light" "175" } { "light" "150" "origin" "2232 1288 44" "classname" "light" } { "light" "175" "origin" "2384 1424 -160" "classname" "light" } { "classname" "light" "origin" "2232 1288 -160" "light" "175" } { "light" "150" "origin" "2164 932 -172" "classname" "light" } { "classname" "light" "origin" "2308 932 -172" "light" "150" } { "light" "150" "origin" "2232 776 24" "classname" "light" } { "classname" "light" "origin" "2192 664 24" "light" "150" } { "light" "150" "origin" "2016 696 24" "classname" "light" } { "classname" "light" "origin" "1912 496 24" "light" "150" } { "light" "175" "origin" "80 1616 -120" "classname" "light" } { "classname" "light" "origin" "72 1888 -120" "light" "175" } { "light" "175" "origin" "296 1616 -120" "classname" "light" } { "light" "175" "origin" "304 1888 -120" "classname" "light" } { "targetname" "t20" "angle" "90" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t21" "angle" "120" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t22" "angle" "150" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t19" "angle" "60" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t18" "angle" "30" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t17" "angle" "0" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t24" "angle" "210" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t23" "angle" "180" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t25" "angle" "240" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t26" "angle" "270" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t27" "angle" "300" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t28" "angle" "330" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "delay" ".1" "targetname" "t29" "target" "t17" "classname" "trigger_multiple" "model" "*4" } { "delay" ".1" "targetname" "t17" "target" "t18" "classname" "trigger_multiple" "model" "*5" } { "delay" ".1" "targetname" "t18" "target" "t19" "classname" "trigger_multiple" "model" "*6" } { "delay" ".1" "targetname" "t19" "target" "t20" "classname" "trigger_multiple" "model" "*7" } { "delay" ".1" "targetname" "t20" "target" "t21" "classname" "trigger_multiple" "model" "*8" } { "delay" ".1" "targetname" "t21" "target" "t22" "classname" "trigger_multiple" "model" "*9" } { "delay" ".1" "targetname" "t22" "target" "t23" "classname" "trigger_multiple" "model" "*10" } { "delay" ".1" "targetname" "t23" "target" "t24" "classname" "trigger_multiple" "model" "*11" } { "delay" ".1" "targetname" "t24" "target" "t25" "classname" "trigger_multiple" "model" "*12" } { "delay" ".1" "targetname" "t25" "target" "t26" "classname" "trigger_multiple" "model" "*13" } { "delay" ".1" "targetname" "t26" "target" "t27" "classname" "trigger_multiple" "model" "*14" } { "delay" ".1" "targetname" "t27" "target" "t28" "classname" "trigger_multiple" "model" "*15" } { "target" "t29" "wait" "1.3" "classname" "trigger_multiple" "model" "*16" } { "origin" "192 1750 -188" "classname" "light_flame_large_yellow" } { "light" "125" "origin" "214 1752 -166" "classname" "light" } { "classname" "light" "origin" "192 1774 -166" "light" "125" } { "light" "125" "origin" "170 1750 -166" "classname" "light" } { "classname" "light" "origin" "194 1726 -166" "light" "125" } { "target" "t31" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*17" } { "target" "t31" "angle" "180" "wait" "-1" "classname" "func_button" "model" "*18" } { "target" "t31" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*19" } { "target" "t31" "angle" "90" "wait" "-1" "classname" "func_button" "model" "*20" } { "wait" "-1" "targetname" "t30" "sounds" "4" "speed" "50" "angle" "-1" "classname" "func_door" "model" "*21" } { "count" "4" "targetname" "t31" "target" "t30" "classname" "trigger_counter" "model" "*22" } { "light" "150" "origin" "2424 1080 -176" "classname" "light" } { "classname" "light" "origin" "2424 992 -176" "light" "150" } { "targetname" "t40" "angle" "180" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "160" } { "targetname" "t40" "angle" "140" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "120" } { "targetname" "t40" "angle" "200" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "220" } { "targetname" "t40" "angle" "240" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t41" "target" "t40" "wait" ".5" "classname" "trigger_multiple" "model" "*23" } { "target" "t41" "classname" "trigger_multiple" "model" "*24" } { "target" "t41" "classname" "trigger_multiple" "model" "*25" } { "target" "t41" "classname" "trigger_multiple" "model" "*26" } { "target" "t41" "classname" "trigger_multiple" "model" "*27" } { "target" "t41" "classname" "trigger_multiple" "model" "*28" } { "target" "t41" "classname" "trigger_multiple" "model" "*29" } { "target" "t41" "classname" "trigger_multiple" "model" "*30" } { "light" "125" "origin" "2196 1200 -204" "classname" "light" } { "classname" "light" "origin" "2284 1200 -204" "light" "125" } { "light" "125" "origin" "2284 1112 -204" "classname" "light" } { "classname" "light" "origin" "2388 1104 -204" "light" "125" } { "light" "125" "origin" "2284 1000 -204" "classname" "light" } { "classname" "light" "origin" "2140 1008 -204" "light" "125" } { "light" "125" "origin" "2132 1096 -204" "classname" "light" } { "classname" "light" "origin" "2132 1160 -204" "light" "125" } { "light" "200" "origin" "528 1816 -392" "classname" "light" } { "classname" "light" "origin" "736 1808 -392" "light" "200" } { "light" "200" "origin" "1040 1808 -392" "classname" "light" } { "classname" "light" "origin" "744 1424 -392" "light" "200" } { "light" "200" "origin" "752 1288 -416" "classname" "light" } { "light" "200" "origin" "760 1064 -376" "classname" "light" } { "classname" "func_train" "spawnflags" "33" "targetname" "t42" "dmg" "1000" "sounds" "1" "target" "t124" "speed" "250" "model" "*31" } { "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t43" "speed" "50" "sounds" "3" "model" "*32" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t42" "sounds" "1" "model" "*33" } { "classname" "trigger_once" "target" "t43" "targetname" "t42" "delay" "2" "model" "*34" } { "classname" "light" "origin" "1936 1784 -288" "light" "125" } { "classname" "monster_ogre" "origin" "1976 1784 -328" "angle" "180" } { "classname" "func_door" "angle" "90" "wait" "-1" "sounds" "1" "model" "*35" } { "classname" "func_door" "angle" "270" "targetname" "t44" "wait" "-1" "model" "*36" } { "classname" "light" "origin" "64 264 8" "light" "125" } { "light" "125" "origin" "64 120 8" "classname" "light" } { "classname" "light" "origin" "64 192 8" "light" "100" } { "classname" "trigger_once" "target" "t44" "model" "*37" } { "classname" "light" "origin" "512 184 -8" "light" "175" } { "targetname" "t119" "classname" "func_door" "angle" "90" "wait" "-1" "speed" "40" "model" "*38" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "270" "speed" "40" "sounds" "4" "model" "*39" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "-1" "speed" "30" "message" "Go for a swim first..." "sounds" "3" "model" "*40" "origin" "-1 0 0" // svdijk -- added to prevent z-fighting } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "t45" "model" "*41" } { "classname" "light" "origin" "36 184 84" "light" "75" } { "classname" "light" "origin" "752 184 -192" "light" "150" } { "classname" "light" "origin" "544 536 -152" "light" "125" } { "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "targetname" "t45" "model" "*42" } { "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "targetname" "t45" "model" "*43" } { "light" "125" "origin" "544 -152 -152" "classname" "light" } { "classname" "item_artifact_envirosuit" "origin" "1024 492 -232" } { "classname" "item_armor1" "origin" "2128 1752 -352" } { "classname" "light_flame_small_yellow" "origin" "1024 368 -4" "light" "250" } { "classname" "light" "origin" "1024 400 -64" "light" "150" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*44" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*45" } { "sounds" "3" "classname" "func_button" "angle" "270" "wait" "3" "target" "t51" "model" "*46" } { "classname" "light" "origin" "1224 712 -216" "light" "100" } { "classname" "light" "origin" "1232 768 -192" "light" "100" } { "light" "75" "origin" "1168 760 -232" "classname" "light" } { "light" "100" "origin" "1232 800 -208" "classname" "light" } { "sounds" "1" "origin" "1860 472 -8" "classname" "item_key2" "spawnflags" "2048" } { "classname" "light" "origin" "1112 1568 -40" "light" "200" } { "light" "150" "origin" "-96 1440 -160" "classname" "light" } { "classname" "light" "origin" "384 1440 -160" "light" "150" } { "light" "200" "origin" "248 1408 -96" "classname" "light" } { "light" "150" "origin" "968 1480 -128" "classname" "light" } { "light" "150" "origin" "1008 -592 -32" "classname" "light" } { "classname" "light" "origin" "1264 -592 -32" "light" "150" } { "light" "200" "origin" "880 -840 -56" "classname" "light" } { "classname" "light" "origin" "1376 -856 -56" "light" "200" } { "light" "175" "origin" "264 184 -8" "classname" "light" } { "light" "175" "origin" "1464 -864 -208" "classname" "light" } { "light" "175" "origin" "1816 160 -56" "classname" "light" } { "classname" "light" "origin" "1560 160 -56" "light" "175" } { "light" "175" "origin" "1544 1632 104" "classname" "light" } { "classname" "light" "origin" "1792 1624 128" "light" "175" } { "classname" "item_health" "origin" "1068 -980 -104" } { "spawnflags" "1" "classname" "item_shells" "origin" "1344 -1160 -96" } { "classname" "monster_ogre" "origin" "1008 -128 40" "angle" "315" "targetname" "t59" "spawnflags" "1" } { "classname" "trigger_once" "target" "t59" "model" "*47" } { "classname" "monster_ogre" "origin" "896 96 56" "angle" "270" "target" "t60" "spawnflags" "1" } { "classname" "path_corner" "origin" "896 208 40" "target" "t60" "targetname" "t61" } { "origin" "896 -16 40" "classname" "path_corner" "targetname" "t60" "target" "t61" } { "classname" "path_corner" "origin" "168 392 -56" "target" "t62" "targetname" "t63" } { "origin" "168 -16 -56" "classname" "path_corner" "targetname" "t62" "target" "t63" } { "classname" "path_corner" "origin" "88 344 -56" "targetname" "t64" "target" "t65" "spawnflags" "256" } { "origin" "88 72 -56" "classname" "path_corner" "target" "t64" "targetname" "t65" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "88 224 -40" "angle" "90" "target" "t64" "spawnflags" "257" } { "classname" "monster_hell_knight" "origin" "168 232 -40" "angle" "270" "target" "t62" "spawnflags" "1" } { "classname" "monster_zombie" "origin" "544 -120 -208" "angle" "90" "targetname" "t45" } { "classname" "monster_zombie" "origin" "544 496 -208" "angle" "270" "targetname" "t45" } { "classname" "monster_wizard" "origin" "664 428 216" "angle" "225" "spawnflags" "1" } { "angle" "135" "origin" "664 -56 216" "classname" "monster_wizard" "spawnflags" "257" } { "classname" "light" "origin" "1736 88 -40" "light" "100" } { "classname" "monster_ogre" "origin" "904 -120 56" "angle" "0" "targetname" "t66" "spawnflags" "1281" } { "classname" "trigger_once" "target" "t66" "spawnflags" "256" "model" "*48" } { "classname" "item_health" "origin" "1168 -408 -80" "spawnflags" "1" } { "classname" "item_health" "origin" "1168 -104 -16" } { "classname" "item_spikes" "origin" "928 216 32" } { "classname" "item_rockets" "origin" "632 -48 32" } { "classname" "item_health" "origin" "32 392 -64" } { "light" "200" "origin" "1688 288 148" "classname" "light" } { "classname" "light" "origin" "1688 464 148" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1814 622 -64" "light" "250" } { "classname" "light" "origin" "1768 344 -72" "light" "200" } { "classname" "trigger_monsterjump" "angle" "0" "model" "*49" } { "targetname" "t100" "classname" "monster_ogre" "origin" "1504 272 24" "angle" "0" "spawnflags" "256" } { "targetname" "t68" "target" "t67" "origin" "1352 576 -120" "classname" "path_corner" "spawnflags" "256" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "1352 416 -120" "spawnflags" "256" } { "target" "t68" "angle" "90" "origin" "1360 480 -104" "classname" "monster_demon1" "spawnflags" "257" } { "targetname" "t70" "target" "t69" "origin" "1472 496 -120" "classname" "path_corner" } { "target" "t70" "targetname" "t69" "classname" "path_corner" "origin" "1688 496 -120" } { "target" "t69" "angle" "270" "origin" "1648 544 -104" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t71" "targetname" "t72" "origin" "1984 688 -24" "classname" "path_corner" "spawnflags" "256" } { "target" "t72" "targetname" "t71" "classname" "path_corner" "origin" "1984 448 -24" "spawnflags" "256" } { "target" "t71" "angle" "270" "origin" "1992 536 -8" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t74" "targetname" "t73" "origin" "2120 680 -24" "classname" "path_corner" } { "targetname" "t74" "target" "t73" "classname" "path_corner" "origin" "2240 680 -24" } { "target" "t74" "angle" "225" "origin" "2256 736 -8" "classname" "monster_ogre" "spawnflags" "1" } { "targetname" "t78" "angle" "90" "origin" "2232 1312 -8" "classname" "monster_ogre" "spawnflags" "256" } { "target" "t76" "angle" "90" "origin" "2232 1008 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "targetname" "t76" "target" "t75" "origin" "2120 968 -216" "classname" "path_corner" } { "target" "t76" "targetname" "t75" "classname" "path_corner" "origin" "2336 968 -216" } { "classname" "light" "origin" "2384 2400 -152" "light" "200" } { "targetname" "t77" "wait" "-1" "sounds" "1" "speed" "300" "angle" "-2" "classname" "func_door" "model" "*50" } { "targetname" "t77" "angle" "180" "origin" "2512 2312 -200" "classname" "monster_demon1" "spawnflags" "256" } { "targetname" "t77" "classname" "monster_demon1" "origin" "2512 2160 -200" "angle" "180" } { "classname" "item_health" "origin" "2504 2200 -224" } { "light" "150" "origin" "2536 2312 -128" "classname" "light" } { "classname" "light" "origin" "2536 2160 -128" "light" "150" } { "target" "t77" "classname" "trigger_once" "model" "*51" } { "spawnflags" "2" "origin" "2368 2336 -224" "classname" "item_health" } { "spawnflags" "1" "origin" "2504 2240 -224" "classname" "item_spikes" } { "origin" "2408 2072 -224" "classname" "item_shells" } { "target" "t78" "classname" "trigger_once" "model" "*52" } { "targetname" "t78" "angle" "90" "origin" "2240 1272 -200" "classname" "monster_demon1" } { "light" "150" "origin" "1120 1384 -40" "classname" "light" } { "light" "200" "origin" "1200 1248 -104" "classname" "light" } { "light" "250" "origin" "1288 1032 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1256 1032 -64" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "568 1032 -4" "light" "250" } { "classname" "light" "origin" "600 1032 -64" "light" "150" } { "light" "250" "origin" "888 1272 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "888 1240 -64" "classname" "light" } { "light" "200" "origin" "608 1232 -108" "classname" "light" } { "target" "t79" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*53" } { "targetname" "t79" "message" "This door is opened near by..." "sounds" "3" "speed" "35" "angle" "-1" "wait" "-1" "classname" "func_door" "model" "*54" } { "light" "100" "origin" "580 1208 -148" "classname" "light" } { "light" "200" "origin" "1224 736 -8" "classname" "light" } { "target" "t80" "classname" "trigger_once" "model" "*55" } { "targetname" "t80" "angle" "315" "origin" "640 1088 128" "classname" "monster_wizard" } { "targetname" "t80" "classname" "monster_wizard" "origin" "616 720 128" "angle" "0" } { "targetname" "t80" "angle" "180" "origin" "1280 680 128" "classname" "monster_wizard" "spawnflags" "256" } { "target" "t82" "origin" "1248 1080 24" "classname" "monster_wizard" "spawnflags" "1" } { "targetname" "t82" "target" "t81" "origin" "1168 1064 8" "classname" "path_corner" } { "target" "t82" "targetname" "t81" "classname" "path_corner" "origin" "720 1064 8" } { "targetname" "t80" "classname" "monster_wizard" "origin" "584 1112 128" "angle" "315" "spawnflags" "256" } { "origin" "1256 944 -184" "classname" "item_health" } { "spawnflags" "1" "origin" "1116 584 -256" "classname" "item_spikes" } { "targetname" "t79" "angle" "315" "origin" "1000 1464 -168" "classname" "monster_ogre" } { "target" "t85" "targetname" "t86" "origin" "1456 1824 -192" "classname" "path_corner" "spawnflags" "256" } { "target" "t86" "targetname" "t85" "classname" "path_corner" "origin" "1456 1560 -192" "spawnflags" "256" } { "target" "t85" "angle" "270" "origin" "1456 1664 -176" "classname" "monster_ogre" "spawnflags" "256" } { "targetname" "t91" "angle" "270" "origin" "1560 1944 -320" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1600 1912 -320" "angle" "270" } { "angle" "225" "origin" "1960 1984 -320" "classname" "monster_zombie" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1904 1928 -320" "angle" "225" } { "targetname" "t91" "angle" "270" "origin" "1624 1768 -320" "classname" "monster_zombie" } { "target" "t87" "classname" "monster_zombie" "origin" "1696 1912 -320" "angle" "0" } { "target" "t89" "angle" "270" "origin" "1704 1800 -320" "classname" "monster_zombie" } { "target" "t87" "targetname" "t88" "origin" "1648 1912 -336" "classname" "path_corner" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "1848 1904 -336" } { "targetname" "t90" "target" "t89" "origin" "1648 1824 -336" "classname" "path_corner" } { "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "1768 1752 -336" } { "target" "t91" "classname" "trigger_once" "model" "*56" } { "spawnflags" "1" "origin" "1496 1808 -200" "classname" "item_health" } { "spawnflags" "1" "origin" "1184 2176 -192" "classname" "item_health" } { "classname" "item_health" "origin" "1184 2128 -192" "spawnflags" "1" } { "spawnflags" "1" "origin" "1264 1680 -192" "classname" "item_health" } { "target" "t92" "targetname" "t93" "origin" "2376 2304 -216" "classname" "path_corner" } { "target" "t93" "targetname" "t92" "classname" "path_corner" "origin" "2376 1984 -216" } { "target" "t95" "targetname" "t94" "origin" "2144 1608 -216" "classname" "path_corner" } { "target" "t94" "targetname" "t95" "classname" "path_corner" "origin" "2376 1608 -216" } { "target" "t92" "angle" "270" "origin" "2376 2176 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "135" "origin" "2416 1760 -200" "classname" "monster_hell_knight" "spawnflags" "257" } { "target" "t94" "angle" "180" "origin" "2320 1608 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "90" "origin" "2152 1840 -200" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t97" "targetname" "t96" "origin" "2096 2376 -216" "classname" "path_corner" "spawnflags" "256" } { "target" "t96" "targetname" "t97" "classname" "path_corner" "origin" "2232 2208 -216" "spawnflags" "256" } { "target" "t96" "angle" "135" "origin" "2200 2264 -200" "classname" "monster_wizard" "spawnflags" "257" } { "origin" "2104 1544 -224" "classname" "item_shells" } { "origin" "2176 1240 -32" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2256 1240 -32" } { "origin" "1928 584 -32" "classname" "item_shells" } { "spawnflags" "1" "origin" "2040 1072 -32" "classname" "item_health" } { "classname" "item_health" "origin" "2040 1024 -32" "spawnflags" "1" } { "angle" "225" "origin" "2352 1160 -200" "classname" "monster_hell_knight" "spawnflags" "256" } { "angle" "180" "origin" "1864 1192 -216" "classname" "monster_zombie" } { "classname" "monster_zombie" "origin" "1776 1192 -216" "angle" "180" } { "target" "t99" "targetname" "t98" "origin" "1688 1192 -232" "classname" "path_corner" } { "target" "t98" "targetname" "t99" "classname" "path_corner" "origin" "1688 1048 -232" } { "target" "t98" "angle" "90" "origin" "1688 1112 -216" "classname" "monster_zombie" } { "origin" "536 416 32" "classname" "item_shells" } { "target" "t100" "classname" "trigger_once" "spawnflags" "256" "model" "*57" } { "origin" "1784 408 -128" "classname" "item_health" } { "classname" "item_health" "origin" "1784 448 -128" } { "origin" "1736 728 -128" "classname" "item_rockets" } { "target" "t101" "sounds" "1" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*58" } { "light" "250" "origin" "1418 234 52" "classname" "light_torch_small_walltorch" } { "targetname" "t101" "classname" "trigger_secret" "model" "*59" } { "targetname" "t102" "angle" "180" "classname" "trigger_monsterjump" "model" "*60" } { "target" "t102" "killtarget" "t102" "classname" "trigger_once" "model" "*61" } { "origin" "1568 1968 -352" "classname" "item_health" } { "classname" "item_health" "origin" "1608 1968 -352" } { "origin" "2112 1776 -352" "classname" "item_shells" "spawnflags" "2048" } { "origin" "1920 2232 -352" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2008 2232 -352" } { "classname" "item_shells" "origin" "2176 1440 -224" } { "target" "t66" "classname" "trigger_once" "model" "*62" } { "origin" "1744 0 -128" "classname" "item_spikes" "spawnflags" "1" } { "target" "t103" "targetname" "t104" "origin" "-56 1432 -224" "classname" "path_corner" "spawnflags" "256" } { "target" "t104" "targetname" "t103" "classname" "path_corner" "origin" "312 1432 -224" "spawnflags" "256" } { "target" "t103" "origin" "40 1440 -208" "classname" "monster_ogre" "spawnflags" "257" } { "angle" "45" "origin" "56 1616 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "target" "t105" "targetname" "t106" "origin" "520 1816 -232" "classname" "path_corner" } { "target" "t106" "targetname" "t105" "classname" "path_corner" "origin" "920 1816 -232" } { "target" "t105" "origin" "624 1800 -216" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "0" "origin" "480 1824 64" "classname" "monster_wizard" "spawnflags" "257" } { "targetname" "t107" "classname" "monster_wizard" "origin" "656 1816 64" "angle" "0" "spawnflags" "1" } { "targetname" "t107" "angle" "0" "origin" "840 1816 64" "classname" "monster_wizard" "spawnflags" "257" } { "target" "t107" "classname" "trigger_once" "model" "*63" } { "origin" "1440 2200 -192" "classname" "item_shells" } { "classname" "item_shells" "origin" "1440 2160 -192" } { "spawnflags" "1" "origin" "1184 1976 -192" "classname" "item_spikes" } { "target" "t109" "targetname" "t108" "origin" "1240 2000 -184" "classname" "path_corner" } { "target" "t108" "targetname" "t109" "classname" "path_corner" "origin" "1240 1760 -184" } { "target" "t108" "angle" "90" "origin" "1240 1904 -168" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t110" "classname" "trigger_once" "spawnflags" "256" "model" "*64" } { "targetname" "t110" "angle" "45" "origin" "1216 2088 -168" "classname" "monster_ogre" "spawnflags" "1281" } { "classname" "item_health" "origin" "1496 1768 -200" } { "target" "t111" "classname" "trigger_once" "model" "*65" } { "targetname" "t111" "angle" "315" "origin" "764 -60 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "45" "origin" "764 436 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "225" "origin" "284 -56 -208" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t119" "wait" "-1" "speed" "40" "angle" "90" "classname" "func_door" "model" "*66" } { "origin" "1280 592 -128" "classname" "item_shells" } { "classname" "item_shells" "origin" "576 416 32" } { "classname" "item_spikes" "origin" "904 504 -128" "spawnflags" "1" } { "origin" "904 464 -128" "classname" "item_spikes" "spawnflags" "1" } { "classname" "item_health" "origin" "1024 912 -152" "spawnflags" "1" } { "classname" "item_spikes" "origin" "720 848 -184" "spawnflags" "1" } { "classname" "path_corner" "origin" "1224 1192 -176" "targetname" "t112" "target" "t113" } { "origin" "1216 896 -176" "classname" "path_corner" "targetname" "t113" "target" "t112" } { "classname" "monster_ogre" "origin" "1224 992 -160" "angle" "90" "target" "t112" } { "classname" "item_shells" "origin" "968 1600 -184" } { "classname" "item_artifact_super_damage" "origin" "1444 308 -104" } { "classname" "light" "origin" "992 -128 72" "light" "100" } { "classname" "item_shells" "origin" "1672 1008 -240" } { "classname" "item_health" "origin" "2264 1320 -224" } { "classname" "monster_wizard" "origin" "2120 1664 -72" "angle" "315" "spawnflags" "1" } { "classname" "item_health" "origin" "2016 392 -32" } { "classname" "monster_hell_knight" "origin" "360 1744 -208" "angle" "90" "targetname" "t114" "spawnflags" "257" } { "classname" "trigger_once" "target" "t114" "spawnflags" "256" "model" "*67" } { "classname" "item_health" "origin" "352 1464 -232" } { "spawnflags" "1" "origin" "352 1392 -232" "classname" "item_health" } { "classname" "item_armorInv" "origin" "744 1424 -448" } { "classname" "item_health" "origin" "-56 320 -232" } { "origin" "-16 320 -232" "classname" "item_health" } { "targetname" "t117" "classname" "trigger_teleport" "target" "t115" "spawnflags" "2" "model" "*68" } { "delay" ".5" "targetname" "t117" "classname" "trigger_teleport" "target" "t116" "spawnflags" "2" "model" "*69" } { "classname" "monster_wizard" "origin" "2928 1816 -152" "angle" "180" "targetname" "t117" } { "angle" "180" "origin" "2928 1768 -152" "classname" "monster_wizard" "targetname" "t117" } { "classname" "info_teleport_destination" "origin" "1824 1920 -184" "angle" "225" "targetname" "t115" } { "classname" "info_teleport_destination" "origin" "1880 1544 -184" "angle" "180" "targetname" "t116" } { "classname" "trigger_once" "target" "t117" "model" "*70" } { "classname" "monster_zombie" "origin" "764 388 -208" "angle" "0" "targetname" "t111" } { "angle" "0" "origin" "764 -12 -208" "classname" "monster_zombie" "targetname" "t111" } { "classname" "monster_zombie" "origin" "408 -56 -208" "angle" "270" "targetname" "t111" } { "classname" "light" "origin" "1200 672 -240" "light" "125" } { "classname" "item_spikes" "origin" "72 392 -64" "spawnflags" "1" } { "classname" "item_spikes" "origin" "2368 920 -224" } { "origin" "2032 976 -224" "classname" "item_spikes" } { "classname" "light" "origin" "1416 2096 -112" "light" "150" } { "light" "150" "origin" "1240 2096 -112" "classname" "light" } { "classname" "item_spikes" "origin" "464 1824 -240" } { "origin" "504 1824 -240" "classname" "item_spikes" } { "classname" "item_shells" "origin" "-96 1472 -232" "spawnflags" "1" } { "classname" "item_health" "origin" "528 -172 -232" } { "classname" "item_health" "origin" "40 -64 -64" } { "light" "225" "classname" "light_torch_small_walltorch" "origin" "122 -86 -8" } { "origin" "134 462 -8" "classname" "light_torch_small_walltorch" "light" "225" } { "light" "250" "origin" "678 446 92" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "678 -70 92" "light" "250" } { "light" "150" "origin" "120 -56 -16" "classname" "light" } { "classname" "light" "origin" "136 424 -16" "light" "150" } { "light" "150" "origin" "600 184 296" "classname" "light" } { "classname" "light" "origin" "152 184 296" "light" "150" } { "light" "150" "origin" "368 376 296" "classname" "light" } { "classname" "light" "origin" "368 0 296" "light" "150" } { "light" "150" "origin" "352 192 232" "classname" "light" } { "light" "200" "origin" "64 408 168" "classname" "light" } { "classname" "light" "origin" "56 -48 168" "light" "200" } { "light" "125" "origin" "1520 1880 24" "classname" "light" } { "origin" "272 272 -232" "classname" "item_rockets" } { "targetname" "t45" "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "model" "*71" } { "classname" "light" "origin" "416 -152 -152" "light" "125" } { "targetname" "t45" "angle" "90" "origin" "416 -120 -208" "classname" "monster_zombie" "spawnflags" "256" } { "origin" "400 524 -232" "classname" "item_health" } { "light" "125" "origin" "416 536 -152" "classname" "light" } { "targetname" "t45" "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "model" "*72" } { "targetname" "t45" "angle" "270" "origin" "416 496 -208" "classname" "monster_zombie" "spawnflags" "256" } { "target" "t120" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*73" } { "target" "t120" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*74" } { "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*75" } { "light" "125" "origin" "1792 840 -112" "classname" "light" } { "classname" "light" "origin" "1584 840 -112" "light" "125" } { "light" "125" "origin" "1792 2280 -480" "classname" "light" } { "classname" "light" "origin" "1792 2144 -480" "light" "125" } { "origin" "1776 2232 -508" "classname" "item_health" } { "classname" "item_health" "origin" "1776 2192 -508" } { "light" "150" "origin" "1016 1696 -336" "classname" "light" } { "light" "125" "origin" "1080 1696 -280" "classname" "light" } { "light" "125" "origin" "1152 1696 -200" "classname" "light" } { "light" "150" "origin" "1096 1696 -352" "classname" "light" } { "classname" "trigger_secret" "model" "*76" } { "target" "t116" "classname" "trigger_teleport" "model" "*77" } { "spawnflags" "768" "angle" "315" "origin" "1400 1856 -176" "classname" "monster_ogre" } { "target" "t121" "targetname" "t122" "spawnflags" "768" "origin" "1824 2280 -344" "classname" "path_corner" } { "target" "t122" "targetname" "t121" "spawnflags" "768" "classname" "path_corner" "origin" "2080 2280 -344" } { "target" "t121" "spawnflags" "769" "angle" "180" "origin" "2128 2272 -328" "classname" "monster_demon1" } { "spawnflags" "2816" "origin" "1824 2072 -352" "classname" "item_shells" } { "spawnflags" "769" "angle" "90" "origin" "1792 2176 -144" "classname" "monster_wizard" } { "spawnflags" "769" "angle" "180" "origin" "2600 1640 -200" "classname" "monster_hell_knight" } { "spawnflags" "2816" "origin" "2096 1136 -32" "classname" "item_shells" } { "classname" "monster_wizard" "origin" "1872 1432 -72" "angle" "90" "spawnflags" "769" } { "classname" "monster_hell_knight" "origin" "2400 1032 -8" "angle" "180" "spawnflags" "769" } { "classname" "monster_demon1" "origin" "1000 496 -104" "angle" "0" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1240 2088 -168" "angle" "45" "spawnflags" "769" "targetname" "t110" } { "classname" "monster_hell_knight" "origin" "360 1936 -208" "angle" "270" "spawnflags" "769" "targetname" "t114" } { "classname" "item_shells" "origin" "16 1768 -232" "spawnflags" "2560" } { "spawnflags" "2560" "origin" "344 1576 -232" "classname" "item_shells" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*78" } { "classname" "light" "origin" "1136 -1144 264" "light" "250" } { "light" "250" "origin" "1264 -552 44" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "1008 -552 44" "light" "250" } { "classname" "monster_demon1" "origin" "888 -120 56" "angle" "0" "spawnflags" "769" "targetname" "t66" } { "classname" "monster_hell_knight" "origin" "616 184 56" "angle" "0" "spawnflags" "768" } { "classname" "item_spikes" "origin" "880 216 32" "spawnflags" "2816" } { "classname" "monster_hell_knight" "origin" "1944 728 -8" "angle" "0" "spawnflags" "769" } { "classname" "light" "origin" "-48 1184 -156" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 1024 -120" "classname" "light" } { "classname" "light" "origin" "48 800 -120" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 600 -120" "classname" "light" } { "classname" "monster_hell_knight" "origin" "128 1088 -208" "angle" "225" "spawnflags" "1" } { "angle" "315" "origin" "-104 760 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-56 592 -232" } { "classname" "item_health" "origin" "-56 920 -232" } { "classname" "monster_demon1" "origin" "0 544 -208" "angle" "90" "target" "t123" "spawnflags" "769" } { "mangle" "20 315 0" "origin" "1568 2040 -88" "classname" "info_intermission" } { "classname" "item_shells" "origin" "1528 1968 -352" "spawnflags" "3584" } { "origin" "-88 1376 -232" "classname" "item_health" "spawnflags" "3585" } { "classname" "item_artifact_envirosuit" "origin" "1216 1696 -168" "spawnflags" "3584" } { "classname" "monster_demon1" "origin" "144 372 -208" "angle" "180" "spawnflags" "768" "targetname" "t123" } { "classname" "monster_demon1" "origin" "0 528 -208" "angle" "90" "spawnflags" "1025" } { "classname" "info_player_deathmatch" "origin" "1128 -840 -80" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "656 184 -208" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-24 1440 -208" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "1240 1816 -168" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1032 1568 -168" "angle" "0" } { "classname" "func_wall" "spawnflags" "1792" "model" "*79" } { "classname" "weapon_rocketlauncher" "origin" "184 1440 -232" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1424 608 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "2272 680 -8" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "2240 1472 -200" "angle" "270" } { "classname" "weapon_supernailgun" "origin" "2236 1184 -32" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "2424 1824 -200" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1792 2072 -328" "angle" "270" } { "classname" "weapon_supershotgun" "origin" "1488 1584 -200" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "88 184 -64" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1224 840 -168" "angle" "90" } { "classname" "weapon_nailgun" "origin" "1024 872 -152" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "136 1760 -232" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "1976 2288 -352" "spawnflags" "1792" } { "classname" "weapon_lightning" "origin" "2128 1792 -352" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1608 728 -128" "spawnflags" "1792" } { "spawnflags" "1792" "classname" "item_cells" "origin" "72 1032 -232" } { "classname" "item_cells" "origin" "1008 1328 -192" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1304 -1160 -96" "spawnflags" "1792" } { "origin" "2272 1440 -224" "classname" "item_shells" "spawnflags" "2816" } { "origin" "224 1736 -232" "classname" "item_health" } { "classname" "info_intermission" "origin" "1328 -1168 192" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1248 680 8" "mangle" "20 130 0" } { "classname" "info_intermission" "origin" "1280 1824 -104" "mangle" "10 180 0" } { "classname" "light" "origin" "-304 888 -80" "light" "150" } { "light" "150" "origin" "-304 712 -80" "classname" "light" } { "classname" "light" "origin" "-224 872 -8" "light" "125" } { "classname" "light" "origin" "-224 728 -8" "light" "125" } { "light" "150" "origin" "-178 706 -156" "classname" "light_torch_small_walltorch" } { "classname" "light" "origin" "-320 838 -138" "light" "100" } { "light" "100" "origin" "-320 774 -138" "classname" "light" } { "classname" "info_player_coop" "origin" "1192 -1088 -72" "angle" "90" } { "angle" "90" "origin" "1080 -1088 -72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1008 -1112 -72" "angle" "90" } { "angle" "90" "origin" "1264 -1112 -72" "classname" "info_player_coop" } { "classname" "item_armor1" "origin" "784 1816 -232" } { "classname" "item_spikes" "origin" "-56 1472 -232" "spawnflags" "1" } { "classname" "item_rockets" "origin" "400 -172 -232" } { "classname" "func_wall" "spawnflags" "1792" "model" "*80" } { "spawnflags" "1792" "classname" "func_wall" "model" "*81" } { "classname" "path_corner" "origin" "1954 1770 -96" "targetname" "t124" "target" "t125" } { "classname" "path_corner" "origin" "1954 1770 -320" "targetname" "t125" "target" "t124" } { "classname" "weapon_grenadelauncher" "origin" "1688 720 -128" "spawnflags" "2048" } { "wait" "-1" "angle" "-2" "classname" "func_door" "targetname" "t126" "lip" "-8" "model" "*82" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "model" "*83" } { "classname" "light" "origin" "-224 832 -152" "light" "125" } { "classname" "trigger_counter" "target" "t126" "targetname" "t127" "spawnflags" "1" "count" "7" "model" "*84" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*85" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*86" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*87" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*88" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*89" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*90" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*91" } { "classname" "ambient_swamp1" "origin" "1338 -854 -104" } { "classname" "ambient_swamp2" "origin" "938 -854 -104" } { "classname" "ambient_drip" "origin" "1138 -854 -176" } { "classname" "ambient_drip" "origin" "1650 -862 -192" } { "classname" "ambient_drip" "origin" "1674 -438 -192" } { "classname" "ambient_drip" "origin" "1682 2 -48" } { "classname" "ambient_swamp1" "origin" "1674 1986 -280" } { "classname" "ambient_swamp2" "origin" "1826 2378 -280" } { "classname" "ambient_swamp1" "origin" "2258 2058 -280" } { "classname" "ambient_drip" "origin" "746 1370 -376" } { "classname" "ambient_drip" "origin" "762 906 -240" } { "origin" "1034 722 -240" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "554 1818 -352" } { "origin" "1002 1810 -352" "classname" "ambient_drip" } { "speed" "35" "classname" "func_door" "wait" "-1" "angle" "-2" "sounds" "1" "targetname" "t101" "model" "*92" } { "classname" "light" "origin" "1440 296 -80" "light" "125" } { "origin" "1792 792 -240" "classname" "item_spikes" } quakespasm-0.93.0/Misc/qs_pak/maps/e2m3.ent.orig0000644000000000000000000011344612403131422020002 0ustar rootroot{ "classname" "worldspawn" "wad" "gfx/jr_med.wad" "worldtype" "0" "sounds" "9" "message" "the Crypt of Decay" } { "classname" "light" "origin" "192 -648 128" } { "classname" "info_player_start" "origin" "688 -1600 -312" "angle" "180" } { "classname" "light_flame_large_yellow" "origin" "650 -438 4" } { "origin" "386 -438 4" "classname" "light_flame_large_yellow" } { "origin" "66 -886 4" "classname" "light_flame_large_yellow" } { "origin" "322 -886 4" "classname" "light_flame_large_yellow" } { "light" "250" "origin" "192 -1408 288" "classname" "light" } { "light" "250" "classname" "light" "origin" "192 -1088 288" } { "light" "250" "origin" "112 -1248 272" "classname" "light" } { "light" "250" "classname" "light" "origin" "272 -1248 272" } { "light" "200" "origin" "192 -1056 32" "classname" "light" } { "light" "150" "origin" "192 -1248 24" "classname" "light" } { "origin" "194 -1462 108" "classname" "light_flame_large_yellow" } { "origin" "194 -1030 164" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "192 -1440 32" "classname" "light" } { "sounds" "2" "classname" "func_plat" "spawnflags" "1" "model" "*1" } { "origin" "226 -1670 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "88 -1552 -184" "classname" "light" } { "origin" "-22 -1374 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "328 -1256 -184" "classname" "light" } { "light" "150" "classname" "light" "origin" "56 -1256 -184" } { "classname" "light" "origin" "248 -1480 -184" "light" "150" } { "light" "250" "origin" "552 -1608 -72" "classname" "light" } { "light" "150" "origin" "432 -1656 -224" "classname" "light" } { "light" "150" "origin" "432 -1496 -224" "classname" "light" } { "light" "100" "origin" "192 -1248 -40" "classname" "light" } { "origin" "10 -438 4" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-254 -438 4" } { "light" "150" "origin" "192 -704 -136" "classname" "light" } { "light" "250" "origin" "192 -512 -136" "classname" "light" } { "classname" "light" "origin" "416 -512 -136" "light" "150" } { "light" "150" "origin" "-32 -512 -136" "classname" "light" } { "classname" "light" "origin" "-208 -512 -136" "light" "150" } { "light" "150" "origin" "592 -512 -136" "classname" "light" } { "classname" "light" "origin" "192 -840 -136" "light" "150" } { "light" "150" "origin" "-352 -672 -168" "classname" "light" } { "classname" "light" "origin" "-320 -832 -168" "light" "150" } { "light" "150" "origin" "-320 -512 -168" "classname" "light" } { "classname" "light" "origin" "696 -512 -168" "light" "150" } { "light" "150" "origin" "736 -672 -168" "classname" "light" } { "classname" "light" "origin" "704 -832 -168" "light" "150" } { "light" "150" "origin" "512 -864 -168" "classname" "light" } { "classname" "light" "origin" "-128 -864 -168" "light" "150" } { "light" "200" "origin" "-128 -320 8" "classname" "light" } { "classname" "light" "origin" "512 -320 8" "light" "200" } { "origin" "384 -24 32" "classname" "light" } { "classname" "light" "origin" "0 -24 32" } { "light" "200" "origin" "416 -192 -8" "classname" "light" } { "classname" "light" "origin" "-32 -192 -8" "light" "200" } { "light" "200" "origin" "840 48 72" "classname" "light" } { "light" "150" "origin" "576 -24 -56" "classname" "light" } { "light" "200" "origin" "624 -24 72" "classname" "light" } { "origin" "1002 354 -60" "classname" "light_flame_large_yellow" } { "light" "100" "origin" "1000 352 -128" "classname" "light" } { "classname" "light" "origin" "736 8 72" "light" "200" } { "light" "200" "origin" "936 88 72" "classname" "light" } { "light" "150" "origin" "688 -8 -32" "classname" "light" } { "classname" "light" "origin" "784 24 -104" "light" "150" } { "light" "150" "origin" "888 72 -32" "classname" "light" } { "light" "200" "origin" "872 208 -56" "classname" "light" } { "classname" "light" "origin" "872 400 -56" "light" "200" } { "light" "200" "origin" "872 592 -56" "classname" "light" } { "classname" "light" "origin" "744 568 88" "light" "150" } { "light" "150" "origin" "744 648 88" "classname" "light" } { "classname" "light" "origin" "704 608 -80" "light" "150" } { "origin" "866 730 -60" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "864 728 -128" "light" "100" } { "sounds" "3" "wait" "-1" "targetname" "t8" "spawnflags" "2049" "angle" "0" "classname" "func_door" "model" "*2" } { "spawnflags" "2048" "angle" "90" "target" "t8" "classname" "func_button" "wait" "-1" "model" "*3" } { "origin" "520 608 -64" "classname" "light" } { "light" "400" "origin" "192 608 -24" "classname" "light" } { "sounds" "1" "wait" "-1" "angle" "270" "spawnflags" "2058" "classname" "func_door_secret" "targetname" "t9" "model" "*4" } { "light" "150" "origin" "1064 640 -112" "classname" "light" } { "targetname" "t9" "angle" "180" "origin" "1024 640 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1120 672 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1088 600 -152" "classname" "monster_zombie" } { "origin" "976 336 -176" "classname" "item_health" } { "light" "150" "origin" "192 608 -104" "classname" "light" } { "origin" "192 288 -64" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "66 106 4" "light" "200" } { "light" "200" "origin" "-30 106 4" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "504 120 -248" "light" "200" } { "light" "200" "origin" "704 224 -248" "classname" "light" } { "classname" "light" "origin" "704 472 -248" "light" "200" } { "light" "200" "origin" "304 112 -248" "classname" "light" } { "classname" "light" "origin" "80 112 -248" "light" "200" } { "light" "200" "origin" "720 608 -248" "classname" "light" } { "spawnflags" "2048" "sounds" "1" "wait" "-1" "targetname" "t3" "classname" "func_door" "angle" "90" "model" "*5" } { "spawnflags" "2048" "wait" "-1" "angle" "270" "classname" "func_door" "message" "This door opens nearby..." "model" "*6" } { "spawnflags" "2048" "target" "t3" "wait" "-1" "classname" "func_button" "angle" "180" "model" "*7" } { "light" "250" "origin" "-448 184 0" "classname" "light" } { "light" "150" "origin" "-552 280 -224" "classname" "light" } { "classname" "light" "origin" "-392 280 -224" "light" "150" } { "light" "150" "origin" "-416 -32 104" "classname" "light" } { "classname" "light" "origin" "-320 -32 104" "light" "150" } { "light" "150" "origin" "-224 -32 104" "classname" "light" } { "light" "250" "origin" "-352 -32 -32" "classname" "light" } { "light" "200" "origin" "-1160 88 -248" "classname" "light" } { "light" "200" "origin" "-1048 88 -32" "classname" "light" } { "light" "200" "classname" "light" "origin" "-1160 224 -32" } { "origin" "-742 658 -44" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-926 658 -44" } { "light" "200" "origin" "-736 632 -96" "classname" "light" } { "classname" "light" "origin" "-928 632 -96" "light" "200" } { "light" "150" "origin" "-600 104 -248" "classname" "light" } { "classname" "light" "origin" "-696 424 -248" "light" "150" } { "light" "150" "origin" "-1152 408 -248" "classname" "light" } { "classname" "light" "origin" "-944 432 -248" "light" "150" } { "light" "150" "origin" "-856 64 -248" "classname" "light" } { "classname" "light" "origin" "-160 152 -248" "light" "150" } { "light" "150" "origin" "-160 448 -248" "classname" "light" } { "classname" "light" "origin" "-328 464 -248" "light" "150" } { "light" "150" "origin" "-256 176 -248" "classname" "light" } { "origin" "-574 410 -172" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-470 410 -172" } { "target" "t4" "classname" "trigger_teleport" "model" "*8" } { "light" "200" "style" "2" "origin" "-760 576 -216" "classname" "light" } { "targetname" "t4" "angle" "180" "origin" "120 -32 -112" "classname" "info_teleport_destination" } { "light" "200" "origin" "-264 384 112" "classname" "light" } { "classname" "light" "origin" "-264 288 112" "light" "200" } { "light" "200" "origin" "-264 192 112" "classname" "light" } { "classname" "light" "origin" "-264 480 112" "light" "200" } { "light" "250" "origin" "-264 304 -56" "classname" "light" } { "classname" "light" "origin" "-520 424 0" "light" "250" } { "light" "200" "origin" "-1120 608 72" "classname" "light" } { "classname" "light" "origin" "-1024 584 72" "light" "200" } { "light" "200" "origin" "-928 584 72" "classname" "light" } { "classname" "light" "origin" "-832 584 72" "light" "200" } { "light" "200" "origin" "-544 584 72" "classname" "light" } { "classname" "light" "origin" "-640 584 72" "light" "200" } { "light" "150" "origin" "-480 768 56" "classname" "light" } { "classname" "light" "origin" "-384 768 56" "light" "150" } { "classname" "light" "origin" "-712 120 -32" "light" "200" } { "sounds" "3" "wait" "-1" "targetname" "t5" "spawnflags" "2049" "angle" "180" "classname" "func_door" "model" "*9" } { "spawnflags" "2048" "angle" "270" "target" "t5" "wait" "-1" "classname" "func_button" "model" "*10" } { "style" "32" "targetname" "t5" "light" "200" "origin" "-352 552 -56" "classname" "light" } { "light" "150" "origin" "-432 768 -56" "classname" "light" } { "light" "150" "origin" "-520 680 -56" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "targetname" "t5" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*11" } { "sounds" "3" "targetname" "t5" "spawnflags" "2049" "wait" "-1" "angle" "90" "classname" "func_door" "model" "*12" } { "origin" "-72 848 -56" "classname" "light" } { "origin" "-120 600 -8" "classname" "light" } { "classname" "light" "origin" "192 904 -8" } { "light" "200" "origin" "192 888 -248" "classname" "light" } { "classname" "light" "origin" "-104 600 -248" "light" "200" } { "light" "200" "origin" "376 984 -120" "classname" "light" } { "classname" "light" "origin" "504 760 -120" "light" "200" } { "light" "200" "origin" "-32 608 200" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*13" } { "origin" "-16 1456 16" "classname" "light" } { "light" "200" "origin" "384 1248 -56" "classname" "light" } { "classname" "light" "origin" "384 1440 -56" "light" "200" } { "light" "150" "origin" "256 1440 -56" "classname" "light" } { "classname" "light" "origin" "192 1248 -56" "light" "200" } { "light" "200" "origin" "384 1344 -88" "classname" "light" } { "classname" "light" "origin" "192 1152 -88" "light" "200" } { "classname" "light" "origin" "8 1456 -120" "light" "200" } { "spawnflags" "2048" "sounds" "1" "classname" "item_key2" "origin" "-16 1456 -152" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*14" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*15" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "spawnflags" "2048" "model" "*16" } { "classname" "trigger_once" "target" "t6" "model" "*17" } { "classname" "light" "origin" "-192 1456 -136" "light" "80" } { "classname" "light" "origin" "-16 1280 -136" "light" "80" } { "spawnflags" "768" "classname" "monster_hell_knight" "origin" "-16 1280 -168" "angle" "90" "targetname" "t6" } { "spawnflags" "256" "angle" "270" "origin" "-16 1632 -168" "classname" "monster_hell_knight" "targetname" "t6" } { "classname" "monster_hell_knight" "origin" "-192 1456 -168" "angle" "0" "targetname" "t6" } { "classname" "light" "origin" "152 1440 -56" "light" "150" } { "classname" "item_shells" "origin" "-104 1512 -192" "spawnflags" "1" } { "wait" "-1" "classname" "func_door" "angle" "0" "spawnflags" "2056" "model" "*18" } { "wait" "-1" "classname" "func_door" "angle" "180" "spawnflags" "2056" "model" "*19" } { "classname" "light" "origin" "-1120 832 -48" "light" "250" } { "classname" "light" "origin" "-1120 976 -24" "light" "250" } { "classname" "light" "origin" "-1240 1296 216" "light" "200" } { "light" "200" "origin" "-1240 1416 216" "classname" "light" } { "classname" "light" "origin" "-1240 1176 216" "light" "200" } { "classname" "light" "origin" "-712 1416 216" "light" "200" } { "light" "200" "origin" "-712 1296 216" "classname" "light" } { "classname" "light" "origin" "-712 1176 216" "light" "200" } { "light" "200" "origin" "-856 1296 216" "classname" "light" } { "classname" "light" "origin" "-1096 1296 216" "light" "200" } { "light" "200" "origin" "-976 1296 216" "classname" "light" } { "classname" "light_flame_small_white" "origin" "-1318 1514 -8" } { "origin" "-1318 1514 64" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-1318 1514 144" } { "origin" "-634 1078 -8" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-634 1078 64" } { "origin" "-634 1078 144" "classname" "light_flame_small_white" } { "classname" "func_plat" "spawnflags" "1" "model" "*20" } { "classname" "light" "origin" "-320 1536 184" "light" "250" } { "classname" "light" "origin" "-376 1312 120" "light" "250" } { "classname" "light" "origin" "-16 1536 184" "light" "200" } { "light" "200" "origin" "-560 1312 56" "classname" "light" } { "classname" "light" "origin" "24 1120 216" "light" "150" } { "classname" "light" "origin" "192 608 176" "light" "200" } { "wait" "-1" "classname" "func_door" "angle" "90" "spawnflags" "2049" "targetname" "t7" "sounds" "1" "model" "*21" } { "light" "150" "origin" "400 1104 224" "classname" "light" } { "light" "150" "origin" "-200 1056 224" "classname" "light" } { "light" "200" "origin" "192 960 224" "classname" "light" } { "light" "200" "origin" "192 784 224" "classname" "light" } { "classname" "light" "origin" "672 264 184" "light" "200" } { "light" "200" "origin" "384 184 184" "classname" "light" } { "classname" "light" "origin" "512 256 8" "light" "200" } { "classname" "light" "origin" "8 184 200" "light" "200" } { "classname" "light" "origin" "584 744 200" "light" "200" } { "classname" "light" "origin" "0 432 200" "light" "200" } { "classname" "trigger_once" "targetname" "t8" "target" "t9" "delay" "10" "model" "*22" } { "classname" "light" "origin" "192 -72 216" "light" "250" } { "classname" "light" "origin" "360 -168 360" "light" "200" } { "classname" "light" "origin" "296 -168 360" "light" "200" } { "origin" "472 -168 368" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "408 -168 360" } { "classname" "light" "origin" "384 -168 248" "light" "200" } { "classname" "light" "origin" "936 -304 328" "light" "200" } { "light" "200" "origin" "1000 -232 360" "classname" "light" } { "classname" "light" "origin" "864 -232 360" "light" "200" } { "light" "200" "origin" "864 -368 360" "classname" "light" } { "classname" "light" "origin" "1000 -368 360" "light" "200" } { "classname" "light" "origin" "736 -248 280" "light" "200" } { "light" "200" "origin" "552 -216 280" "classname" "light" } { "classname" "light" "origin" "16 144 -32" "light" "200" } { "classname" "light" "origin" "0 432 -136" "light" "200" } { "classname" "light" "origin" "192 384 184" "light" "200" } { "light" "200" "origin" "192 192 184" "classname" "light" } { "spawnflags" "2048" "classname" "func_button" "wait" "-1" "target" "t7" "model" "*23" } { "style" "33" "targetname" "t7" "classname" "light" "origin" "176 1152 200" "target" "t10" } { "classname" "info_null" "origin" "292 1152 180" "targetname" "t10" } { "classname" "light" "origin" "192 1224 200" "light" "150" } { "light" "150" "origin" "192 1376 200" "classname" "light" } { "classname" "light" "origin" "192 1536 200" "light" "200" } { "light" "150" "origin" "192 1080 200" "classname" "light" } { "classname" "weapon_nailgun" "origin" "184 -1520 -272" } { "classname" "func_button" "target" "t11" "angle" "-1" "targetname" "t12" "lip" "4" "wait" "0.1" "speed" "300" "health" "1" "model" "*24" } { "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t11" "wait" "10" "model" "*25" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "8" "targetname" "t11" "model" "*26" } { "classname" "light" "origin" "1248 -288 312" "light" "150" } { "light" "200" "origin" "1176 -400 352" "classname" "light" } { "classname" "item_health" "origin" "1336 -536 256" "spawnflags" "2" } { "classname" "light" "origin" "1320 -488 352" "light" "200" } { "classname" "trigger_multiple" "target" "t11" "wait" "10" "model" "*27" } { "classname" "item_armor1" "origin" "192 -592 -64" } { "classname" "item_shells" "origin" "176 592 -160" "spawnflags" "1" } { "classname" "weapon_nailgun" "origin" "-80 1456 -192" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "56 1144 128" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "-736 608 -280" "spawnflags" "1792" } { "classname" "item_spikes" "origin" "1120 -384 256" "spawnflags" "1" } { "classname" "item_rockets" "origin" "840 -432 192" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-456 312 -80" } { "classname" "monster_zombie" "origin" "-1152 96 -88" "spawnflags" "256" "target" "t36" } { "origin" "-1120 32 -88" "classname" "monster_zombie" "spawnflags" "768" "target" "t36" } { "classname" "monster_zombie" "origin" "-1192 168 -88" "target" "t36" } { "classname" "monster_shambler" "origin" "-1120 1104 -56" "angle" "270" } { "classname" "monster_hell_knight" "origin" "-336 1312 8" "angle" "180" } { "classname" "monster_ogre" "origin" "-698 1446 -56" "angle" "270" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "-888 1128 8" "angle" "90" "spawnflags" "256" } { "classname" "item_health" "origin" "-336 784 -128" } { "origin" "-408 608 -128" "classname" "item_health" } { "classname" "path_corner" "origin" "-1096 584 -120" "targetname" "t13" "target" "t14" } { "origin" "-496 584 -120" "classname" "path_corner" "targetname" "t14" "target" "t13" } { "classname" "monster_demon1" "origin" "-712 576 -104" "angle" "180" "target" "t13" } { "classname" "path_corner" "origin" "-528 472 -96" "targetname" "t15" "target" "t16" } { "origin" "-368 -32 -96" "classname" "path_corner" "target" "t15" "targetname" "t16" } { "classname" "monster_ogre" "origin" "-466 262 -56" "target" "t16" "spawnflags" "256" } { "target" "t22" "targetname" "t21" "origin" "56 -184 -120" "classname" "path_corner" } { "target" "t21" "targetname" "t20" "classname" "path_corner" "origin" "-128 -224 -120" } { "target" "t20" "targetname" "t19" "origin" "-128 -504 -56" "classname" "path_corner" } { "target" "t19" "targetname" "t18" "classname" "path_corner" "origin" "512 -504 -56" } { "target" "t18" "targetname" "t17" "origin" "512 -224 -120" "classname" "path_corner" } { "targetname" "t26" "target" "t17" "classname" "path_corner" "origin" "328 -184 -120" } { "target" "t23" "targetname" "t22" "classname" "path_corner" "origin" "-128 -200 -120" } { "target" "t24" "targetname" "t23" "classname" "path_corner" "origin" "-128 -552 -56" } { "target" "t25" "targetname" "t24" "origin" "512 -552 -56" "classname" "path_corner" } { "target" "t26" "targetname" "t25" "classname" "path_corner" "origin" "512 -200 -120" } { "spawnflags" "256" "target" "t21" "origin" "0 -184 -104" "classname" "monster_hell_knight" } { "spawnflags" "1" "origin" "376 -160 -104" "classname" "monster_hell_knight" } { "spawnflags" "768" "angle" "270" "origin" "190 -706 -40" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "90" "origin" "192 -1408 24" "classname" "monster_hell_knight" } { "origin" "40 -1424 0" "classname" "item_shells" "spawnflags" "1" } { "spawnflags" "1" "origin" "304 -1096 0" "classname" "item_health" } { "origin" "328 -1280 -272" "classname" "item_health" } { "origin" "40 -1256 -272" "classname" "item_spikes" } { "target" "t28" "targetname" "t27" "origin" "864 176 -168" "classname" "path_corner" } { "target" "t27" "targetname" "t28" "classname" "path_corner" "origin" "864 616 -168" } { "target" "t27" "origin" "862 446 -152" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "180" "origin" "526 -26 -104" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "0" "origin" "-72 -24 -104" "classname" "monster_hell_knight" } { "spawnflags" "256" "origin" "-274 -34 -104" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "270" "origin" "-512 760 -104" "classname" "monster_hell_knight" } { "spawnflags" "1793" "origin" "336 1104 128" "classname" "item_rockets" } { "angle" "45" "spawnflags" "256" "origin" "104 256 -136" "classname" "monster_zombie" } { "spawnflags" "769" "angle" "90" "origin" "192 488 -136" "classname" "monster_hell_knight" } { "wait" "1" "speed" "250" "lip" "16" "spawnflags" "5" "targetname" "t29" "dmg" "20" "angle" "180" "classname" "func_door" "model" "*28" } { "wait" "1" "targetname" "t29" "speed" "250" "dmg" "20" "lip" "16" "spawnflags" "5" "classname" "func_door" "sounds" "1" "model" "*29" } { "wait" "2" "target" "t29" "classname" "trigger_multiple" "model" "*30" } { "origin" "506 1762 -124" "classname" "light_torch_small_walltorch" "style" "1" } { "classname" "light_torch_small_walltorch" "origin" "274 2010 -124" } { "light" "200" "origin" "152 1824 -40" "classname" "light" } { "classname" "light" "origin" "288 1824 -40" "light" "200" } { "light" "200" "origin" "288 1696 -40" "classname" "light" } { "classname" "light" "origin" "152 1704 -40" "light" "200" } { "light" "220" "origin" "0 1704 -40" "classname" "light" } { "angle" "180" "classname" "func_door_secret" "targetname" "t35" "spawnflags" "16" "sounds" "1" "model" "*31" } { "spawnflags" "2" "origin" "-16 1816 -192" "classname" "item_health" } { "origin" "432 1672 -320" "classname" "light" "light" "220" } { "spawnflags" "1792" "origin" "-16 1752 -192" "classname" "item_rockets" } { "angle" "270" "origin" "-192 1580 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "236 1536 168" "angle" "180" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "0 1580 168" "angle" "270" "targetname" "t32" } { "angle" "90" "origin" "-96 1492 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "148 1376 168" "angle" "0" "targetname" "t33" } { "angle" "180" "origin" "236 1256 168" "classname" "trap_spikeshooter" "targetname" "t33" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "192 1580 168" "angle" "270" "targetname" "t33" } { "spawnflags" "257" "angle" "90" "origin" "192 -16 153" "classname" "monster_hell_knight" } { "spawnflags" "257" "angle" "180" "origin" "864 -248 217" "classname" "monster_shambler" } { "angle" "180" "origin" "464 -184 185" "classname" "monster_hell_knight" } { "classname" "monster_hell_knight" "origin" "192 -176 153" "angle" "90" "spawnflags" "1" "target" "t31" } { "spawnflags" "769" "angle" "90" "origin" "190 1166 153" "classname" "monster_ogre" } { "spawnflags" "2048" "origin" "48 1456 -192" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "origin" "-96 1376 -192" "classname" "item_rockets" } { "angle" "270" "spawnflags" "768" "classname" "monster_ogre" "origin" "862 662 -152" } { "light" "120" "origin" "192 -352 -264" "classname" "light" } { "spawnflags" "768" "origin" "326 -1490 -248" "classname" "monster_ogre" "angle" "180" "target" "t42" } { "origin" "192 552 128" "classname" "item_health" } { "spawnflags" "1024" "classname" "item_health" "origin" "176 672 128" } { "spawnflags" "1025" "origin" "184 1312 128" "classname" "item_health" } { "classname" "item_health" "origin" "-16 1480 128" "spawnflags" "1025" } { "origin" "672 -328 176" "classname" "item_health" } { "classname" "item_health" "origin" "776 -192 176" } { "origin" "784 312 -176" "classname" "item_shells" } { "spawnflags" "256" "angle" "315" "origin" "-26 1094 152" "classname" "monster_ogre" } { "classname" "monster_ogre" "origin" "406 1094 152" "angle" "225" "spawnflags" "768" } { "spawnflags" "1" "origin" "-200 1128 128" "classname" "item_rockets" } { "origin" "-550 -478 212" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "-550 -166 212" } { "origin" "-214 -326 252" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-120 -176 192" "classname" "light" } { "classname" "light" "origin" "56 -176 192" "light" "150" } { "origin" "-816 1488 -80" "classname" "item_health" } { "classname" "item_health" "origin" "-752 1488 -80" } { "spawnflags" "1536" "origin" "-688 1488 -80" "classname" "item_health" } { "spawnflags" "1" "origin" "176 1264 -160" "classname" "item_shells" } { "classname" "func_door" "angle" "270" "targetname" "t31" "wait" "-1" "spawnflags" "2048" "model" "*32" } { "wait" "-1" "sounds" "1" "speed" "300" "classname" "func_door" "angle" "90" "model" "*33" } { "speed" "300" "classname" "func_door" "angle" "270" "wait" "-1" "model" "*34" } { "classname" "item_health" "origin" "400 1464 -160" "spawnflags" "1024" } { "classname" "item_health" "origin" "-1136 528 -128" } { "classname" "monster_ogre" "origin" "-370 -218 88" "targetname" "t31" "spawnflags" "1" } { "classname" "monster_hell_knight" "origin" "-64 -176 152" "angle" "0" "spawnflags" "768" "targetname" "t31" } { "classname" "item_health" "origin" "-488 -480 64" } { "classname" "light" "origin" "680 -1600 -160" } { "classname" "item_spikes" "origin" "984 -192 192" "spawnflags" "1" } { "classname" "monster_ogre" "origin" "-490 -410 88" "angle" "45" } { "classname" "trigger_multiple" "target" "t32" "wait" "1" "spawnflags" "1024" "targetname" "t44" "model" "*35" } { "classname" "trigger_multiple" "target" "t33" "wait" "1" "spawnflags" "1024" "model" "*36" } { "classname" "item_rockets" "origin" "-96 272 -384" } { "classname" "weapon_supernailgun" "origin" "-1200 208 -112" "spawnflags" "1792" } { "classname" "func_door_secret" "spawnflags" "2051" "targetname" "t36" "angle" "90" "model" "*37" } { "classname" "light" "origin" "-1288 640 -80" "light" "160" } { "light" "160" "origin" "-1288 128 -80" "classname" "light" } { "classname" "light" "origin" "-1288 264 -80" "light" "160" } { "light" "160" "origin" "-1288 392 -80" "classname" "light" } { "classname" "light" "origin" "-1288 520 -80" "light" "160" } { "classname" "func_door_secret" "targetname" "t36" "angle" "270" "spawnflags" "2049" "model" "*38" } { "classname" "item_armor2" "origin" "1128 600 -176" "spawnflags" "1024" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "11" "targetname" "t34" "model" "*39" } { "classname" "light_torch_small_walltorch" "origin" "-246 -1310 -204" "light" "250" } { "classname" "trigger_once" "target" "t34" "model" "*40" } { "classname" "item_spikes" "origin" "-240 -1288 -312" "spawnflags" "1" } { "spawnflags" "1" "origin" "-240 -1368 -312" "classname" "item_spikes" } { "sounds" "3" "classname" "func_door" "angle" "-2" "spawnflags" "3585" "wait" "90" "targetname" "t6" "model" "*41" } { "sounds" "3" "classname" "func_door" "targetname" "t6" "spawnflags" "3585" "angle" "-2" "wait" "90" "model" "*42" } { "sounds" "3" "classname" "func_door" "angle" "0" "spawnflags" "1537" "targetname" "t6" "wait" "90" "model" "*43" } { "classname" "light_torch_small_walltorch" "origin" "-42 1642 -108" "light" "200" } { "classname" "item_health" "origin" "-72 560 -384" } { "origin" "-152 560 -384" "classname" "item_health" } { "targetname" "t31" "spawnflags" "513" "angle" "90" "origin" "848 -376 216" "classname" "monster_hell_knight" } { "targetname" "t31" "spawnflags" "768" "origin" "-512 -248 88" "classname" "monster_shambler" } { "spawnflags" "768" "angle" "180" "origin" "382 1246 -136" "classname" "monster_ogre" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "190 1438 -136" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "640 -1664 -312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-400 -240 88" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-216 1088 152" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-512 792 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "888 624 -152" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "-96 1456 -168" "angle" "0" } { "spawnflags" "2048" "classname" "func_wall" "model" "*44" } { "spawnflags" "2048" "classname" "func_wall" "model" "*45" } { "classname" "func_plat" "height" "192" "sounds" "2" "model" "*46" } { "classname" "item_health" "origin" "-1184 72 -352" } { "classname" "light" "origin" "-952 1408 80" "light" "200" } { "classname" "light" "origin" "-1120 1176 48" "light" "250" } { "classname" "light" "origin" "-104 288 -344" "light" "200" } { "classname" "light" "origin" "-512 376 -304" "light" "200" } { "classname" "trigger_multiple" "target" "t35" "model" "*47" } { "classname" "light" "origin" "192 -376 -16" "light" "150" } { "classname" "light" "origin" "192 -328 176" "light" "150" } { "classname" "trigger_multiple" "target" "t32" "spawnflags" "768" "wait" "0.5" "model" "*48" } { "classname" "trigger_multiple" "spawnflags" "768" "wait" "0.5" "target" "t33" "targetname" "t44" "model" "*49" } { "origin" "-1176 112 -112" "classname" "item_rockets" } { "origin" "-736 544 -280" "classname" "item_armorInv" } { "classname" "func_button" "angle" "180" "target" "t36" "model" "*50" } { "classname" "info_null" "origin" "-1332 1116 -36" "targetname" "t37" } { "classname" "light" "origin" "-1296 1120 -32" "target" "t37" "angle" "60" } { "spawnflags" "2048" "classname" "func_wall" "model" "*51" } { "spawnflags" "2048" "classname" "func_wall" "model" "*52" } { "classname" "light" "origin" "192 -152 -344" "light" "160" } { "classname" "light" "origin" "192 32 -344" "light" "160" } { "classname" "light" "origin" "32 168 -344" "light" "140" } { "light" "160" "origin" "-16 408 -344" "classname" "light" } { "classname" "light" "origin" "192 368 -344" "light" "140" } { "light" "140" "origin" "448 368 -344" "classname" "light" } { "classname" "light" "origin" "528 592 -344" "light" "200" } { "light" "200" "origin" "408 808 -344" "classname" "light" } { "classname" "light" "origin" "192 832 -344" "light" "200" } { "light" "200" "origin" "-40 728 -344" "classname" "light" } { "classname" "light" "origin" "-168 632 -344" "light" "200" } { "light" "200" "origin" "-160 976 -344" "classname" "light" } { "classname" "light" "origin" "592 456 -344" "light" "200" } { "classname" "light" "origin" "192 352 -232" "light" "200" } { "light" "200" "origin" "32 264 -232" "classname" "light" } { "classname" "light" "origin" "-184 432 -336" "light" "160" } { "light" "160" "origin" "-192 144 -336" "classname" "light" } { "classname" "light" "origin" "-776 280 -312" "light" "200" } { "light" "200" "origin" "-864 432 -312" "classname" "light" } { "classname" "light" "origin" "-1096 432 -312" "light" "200" } { "light" "200" "origin" "-1168 272 -312" "classname" "light" } { "classname" "light" "origin" "-944 64 -312" "light" "200" } { "light" "200" "origin" "-664 136 -312" "classname" "light" } { "light" "200" "origin" "-1112 592 -96" "classname" "light" } { "classname" "light" "origin" "-1040 1256 -32" "light" "150" } { "light" "200" "origin" "-880 1128 80" "classname" "light" } { "classname" "light" "origin" "-712 1400 -16" "light" "200" } { "classname" "light" "origin" "104 -600 -224" "light" "200" } { "light" "200" "origin" "280 -600 -224" "classname" "light" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*53" } { "light" "160" "origin" "-312 808 -72" "classname" "light" } { "light" "200" "origin" "192 1776 -312" "classname" "light" } { "classname" "light" "origin" "-392 568 -56" "light" "160" } { "classname" "light" "origin" "-1224 1504 8" "light" "170" } { "light" "170" "origin" "-1304 1392 8" "classname" "light" } { "classname" "light" "origin" "-640 1168 -8" "light" "170" } { "light" "160" "origin" "352 -1248 56" "classname" "light" } { "classname" "light" "origin" "40 -1248 56" "light" "160" } { "light" "160" "origin" "192 -1216 -176" "classname" "light" } { "classname" "light" "origin" "24 -1376 -232" "light" "160" } { "light" "160" "origin" "224 -1624 -232" "classname" "light" } { "classname" "light" "origin" "368 -1392 -232" "light" "160" } { "classname" "light" "origin" "8 -464 -40" "light" "140" } { "light" "140" "origin" "384 -464 -40" "classname" "light" } { "classname" "light" "origin" "-544 800 -56" "light" "140" } { "classname" "trigger_secret" "model" "*54" } { "classname" "trigger_secret" "model" "*55" } { "light" "200" "origin" "760 1856 -40" "classname" "light" } { "classname" "light" "origin" "760 1664 -40" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "538 1762 -124" "style" "1" "light" "200" } { "style" "1" "classname" "light_torch_small_walltorch" "origin" "850 1930 -124" "light" "200" } { "origin" "850 1618 -124" "classname" "light_torch_small_walltorch" "style" "1" "light" "200" } { "classname" "light" "origin" "912 1856 -40" "light" "200" } { "light" "200" "origin" "912 1664 -40" "classname" "light" } { "light" "200" "origin" "1064 1776 -172" "classname" "light" } { "light" "200" "origin" "1080 1856 -40" "classname" "light" } { "classname" "light" "origin" "1080 1664 -40" "light" "200" } { "classname" "light" "origin" "1176 1776 -172" "light" "200" } { "light" "170" "origin" "672 1768 -296" "classname" "light" } { "target" "t38" "classname" "trigger_teleport" "model" "*56" } { "targetname" "t38" "origin" "1144 1776 -88" "classname" "info_teleport_destination" } { "map" "e2m7" "classname" "trigger_changelevel" "model" "*57" } { "light" "160" "origin" "840 1768 -200" "classname" "light" } { "light" "140" "origin" "408 608 -344" "classname" "light" } { "classname" "item_spikes" "origin" "-16 240 -160" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "-1256 1448 -80" } { "origin" "432 1160 152" "classname" "item_artifact_super_damage" } { "message" "The portal lies beyond..." "targetname" "t40" "wait" "-1" "speed" "20" "sounds" "4" "angle" "-2" "classname" "func_door" "model" "*58" } { "origin" "432 1672 -368" "classname" "item_armor2" } { "target" "t39" "sounds" "1" "wait" "-1" "classname" "func_button" "model" "*59" } { "message" "The underwater barrier is lowered..." "target" "t40" "targetname" "t39" "spawnflags" "1" "classname" "trigger_once" "model" "*60" } { "classname" "trigger_secret" "model" "*61" } { "light" "200" "origin" "-128 -704 -224" "classname" "light" } { "classname" "light" "origin" "512 -704 -224" "light" "200" } { "light" "200" "origin" "192 -832 -224" "classname" "light" } { "mangle" "20 240 0" "origin" "400 1048 240" "classname" "info_intermission" } { "mangle" "20 145 0" "origin" "-160 144 64" "classname" "info_intermission" } { "mangle" "-20 45 0" "origin" "-320 -824 -144" "classname" "info_intermission" } { "classname" "func_wall" "spawnflags" "1792" "model" "*62" } { "classname" "item_artifact_super_damage" "origin" "928 1768 -240" "spawnflags" "1792" } { "classname" "light" "origin" "8 1800 -120" "light" "220" } { "classname" "weapon_lightning" "origin" "1216 1784 -264" "spawnflags" "1792" } { "classname" "item_cells" "origin" "880 1648 -264" "spawnflags" "1793" } { "spawnflags" "1793" "origin" "880 1864 -264" "classname" "item_cells" } { "spawnflags" "1792" "classname" "func_wall" "model" "*63" } { "spawnflags" "1792" "classname" "func_wall" "model" "*64" } { "spawnflags" "1792" "classname" "func_wall" "model" "*65" } { "classname" "info_player_coop" "origin" "664 -1520 -312" "angle" "180" } { "angle" "180" "origin" "592 -1600 -312" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "680 -1712 -312" "angle" "180" } { "classname" "air_bubbles" "origin" "720 1384 -320" } { "classname" "light" "origin" "680 1376 -312" } { "classname" "func_door_secret" "angle" "180" "spawnflags" "2" "targetname" "t41" "model" "*66" } { "classname" "trigger_multiple" "target" "t41" "model" "*67" } { "origin" "688 1176 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "480 1408 -312" } { "origin" "448 1552 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 -312" } { "origin" "840 1080 -160" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 0" } { "classname" "light" "origin" "840 1080 232" "light" "200" } { "classname" "item_artifact_envirosuit" "origin" "576 1440 -344" } { "classname" "item_artifact_invulnerability" "origin" "544 1248 -344" } { "light" "200" "origin" "840 936 232" "classname" "light" } { "classname" "light" "origin" "840 760 232" "light" "150" } { "light" "120" "origin" "808 600 232" "classname" "light" } { "classname" "item_health" "origin" "824 960 152" } { "origin" "848 880 152" "classname" "item_health" } { "classname" "item_health" "origin" "808 768 152" } { "classname" "trigger_multiple" "message" "Welcome to the Well of Wishes!" "wait" "5" "sounds" "1" "model" "*68" } { "classname" "trigger_multiple" "sounds" "1" "wait" "3" "message" "The Dopefish Lives!" "model" "*69" } { "classname" "func_wall" "spawnflags" "1792" "model" "*70" } { "classname" "trigger_secret" "model" "*71" } { "spawnflags" "1" "origin" "-1312 1392 -80" "classname" "item_spikes" } { "classname" "func_wall" "spawnflags" "1792" "model" "*72" } { "classname" "light" "origin" "-544 600 -248" "light" "200" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "194 -214 196" "spawnflags" "2048" } { "light" "200" "origin" "352 984 -328" "classname" "light" } { "classname" "light" "origin" "96 976 -328" "light" "200" } { "classname" "light" "origin" "640 776 -336" "light" "200" } { "classname" "func_plat" "spawnflags" "1" "model" "*73" } { "classname" "monster_fish" "origin" "656 352 -336" "spawnflags" "256" } { "spawnflags" "256" "origin" "432 424 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "296 968 -336" "spawnflags" "256" } { "origin" "-48 800 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "-896 248 -312" } { "origin" "-744 328 -312" "classname" "monster_fish" } { "classname" "path_corner" "origin" "272 -1504 -264" "targetname" "t42" "target" "t43" } { "origin" "56 -1352 -264" "classname" "path_corner" "target" "t42" "targetname" "t43" } { "classname" "item_health" "origin" "312 -1336 -272" } { "origin" "544 -1488 -336" "classname" "item_health" } { "classname" "item_health" "origin" "312 1464 -160" } { "origin" "56 1488 -192" "classname" "item_health" } { "classname" "trigger_once" "killtarget" "t44" "spawnflags" "3072" "model" "*74" } { "classname" "item_rockets" "origin" "216 648 120" "spawnflags" "1" } { "classname" "item_shells" "origin" "136 648 120" "spawnflags" "1" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m1.ent.orig0000644000000000000000000006325312403131422017777 0ustar rootroot{ "worldtype" "2" "sounds" "6" "classname" "worldspawn" "wad" "gfx/base.wad" "message" "the Slipgate Complex" } { "classname" "info_player_start" "origin" "480 -352 88" "angle" "90" } { "classname" "light" "origin" "480 96 168" "light" "250" } { "classname" "light" "origin" "480 288 168" "light" "250" } { "classname" "light" "origin" "272 96 80" } { "origin" "272 288 80" "classname" "light" } { "classname" "light" "origin" "272 192 80" } { "origin" "688 192 80" "classname" "light_fluorospark" "style" "10" } { "style" "10" "classname" "light" "origin" "688 288 80" } { "origin" "688 96 80" "classname" "light" "style" "10" } { "classname" "light" "origin" "480 -280 168" "light" "200" } { "origin" "480 -144 168" "classname" "light" "light" "200" } { "classname" "light" "origin" "480 -376 120" "light" "200" } { "light" "160" "origin" "480 -40 168" "classname" "light" } { "speed" "400" "sounds" "2" "angle" "270" "classname" "func_door" "model" "*1" } { "speed" "400" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "250" "origin" "592 544 88" "classname" "light_fluoro" } { "origin" "456 600 104" "classname" "light" } { "light" "180" "origin" "688 648 136" "classname" "light" } { "classname" "light" "origin" "688 520 136" "light" "180" } { "origin" "688 480 80" "classname" "item_armor1" } { "angle" "180" "spawnflags" "768" "origin" "616 72 40" "classname" "monster_army" } { "light" "250" "origin" "0 576 120" "classname" "light" } { "light" "180" "origin" "160 576 72" "classname" "light" } { "light" "200" "origin" "560 -32 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "400 -32 72" } { "light" "200" "origin" "0 712 72" "classname" "light" } { "classname" "light" "origin" "0 728 -136" "light" "200" } { "light" "200" "origin" "0 592 -136" "classname" "light" } { "wait" "5" "angle" "-2" "sounds" "2" "targetname" "t1" "classname" "func_door" "dmg" "10" "model" "*3" } { "sounds" "1" "target" "t1" "angle" "180" "classname" "func_button" "model" "*4" } { "light" "200" "origin" "412 780 136" "classname" "light" } { "light" "200" "classname" "light" "origin" "328 904 72" } { "light" "200" "origin" "168 800 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "-72 864 72" } { "origin" "264 888 -136" "classname" "light" } { "classname" "light" "origin" "-8 992 -136" "light" "200" } { "light" "250" "classname" "light" "origin" "272 1064 -136" } { "light" "250" "origin" "-8 1232 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "256 1272 -136" } { "light" "250" "origin" "312 1464 -136" "classname" "light" } { "light" "200" "origin" "128 968 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-48 1168 72" } { "light" "250" "origin" "312 1168 72" "classname" "light" } { "light" "220" "classname" "light" "origin" "128 1504 -120" } { "light" "250" "classname" "light" "origin" "-56 1464 -136" } { "sounds" "2" "classname" "func_door" "angle" "180" "speed" "400" "model" "*5" } { "classname" "func_door" "angle" "0" "speed" "400" "model" "*6" } { "classname" "light_fluoro" "origin" "176 1744 -152" } { "origin" "80 1744 -152" "classname" "light_fluoro" } { "light" "250" "origin" "-232 1600 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "488 1600 -136" } { "origin" "-56 1448 72" "classname" "light" "light" "250" } { "light" "250" "classname" "light" "origin" "312 1448 72" } { "light" "260" "classname" "light_fluoro" "origin" "416 2064 -112" } { "light" "260" "origin" "416 1968 -112" "classname" "light_fluoro" } { "light" "250" "origin" "128 1880 -112" "classname" "light" } { "origin" "616 1944 -88" "classname" "light" } { "style" "10" "classname" "light_fluorospark" "origin" "344 2216 -88" } { "light" "180" "origin" "352 2016 -112" "classname" "light" } { "classname" "light" "origin" "128 2056 -112" "light" "250" } { "light" "250" "origin" "-112 1984 -112" "classname" "light" } { "light" "350" "origin" "-472 2064 -88" "classname" "light_fluoro" } { "classname" "light" "origin" "-192 2208 8" "light" "250" } { "light" "250" "origin" "-424 2208 8" "classname" "light" } { "light" "250" "origin" "-248 2088 -96" "classname" "light" } { "origin" "-200 2384 -72" "classname" "light" } { "classname" "light" "origin" "-424 2384 -72" } { "light" "200" "origin" "-448 2408 -128" "classname" "light" } { "classname" "light" "origin" "-176 2408 -128" "light" "200" } { "sounds" "1" "classname" "func_plat" "model" "*7" } { "light" "350" "origin" "-352 2656 184" "classname" "light" } { "light" "350" "classname" "light" "origin" "-352 2464 184" } { "origin" "-576 2800 -40" "classname" "light" } { "light" "500" "origin" "160 2920 232" "classname" "light" } { "classname" "light" "origin" "160 2720 232" "light" "500" } { "origin" "-288 2992 8" "classname" "light" } { "classname" "light" "origin" "-168 2776 -40" } { "classname" "light" "origin" "160 2824 104" "light" "200" } { "light" "150" "origin" "-64 2760 136" "classname" "light" } { "light" "200" "origin" "16 2832 -152" "classname" "light" } { "classname" "light" "origin" "304 2832 -152" "light" "200" } { "origin" "504 2816 16" "classname" "light" } { "sounds" "3" "wait" "-1" "speed" "600" "targetname" "t2" "spawnflags" "1" "angle" "270" "classname" "func_door" "model" "*8" } { "classname" "light" "origin" "160 2840 -152" "light" "200" } { "light" "80" "origin" "16 2904 -88" "classname" "light" } { "classname" "light" "origin" "304 2904 -88" "light" "80" } { "classname" "light" "origin" "160 2904 -88" "light" "80" } { "wait" "-1" "sounds" "1" "target" "t2" "speed" "50" "angle" "270" "classname" "func_button" "model" "*9" } { "light" "100" "origin" "0 1800 -32" "classname" "light" } { "classname" "light" "origin" "248 1800 -32" "light" "100" } { "style" "32" "targetname" "t3" "origin" "8 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2392 200" } { "style" "32" "targetname" "t3" "origin" "56 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2312 200" } { "style" "32" "targetname" "t3" "light" "200" "origin" "32 2352 88" "classname" "light" } { "spawnflags" "2048" "origin" "112 2352 16" "classname" "weapon_nailgun" } { "sounds" "3" "targetname" "t3" "spawnflags" "3" "angle" "270" "classname" "func_door_secret" "model" "*10" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*11" } { "origin" "304 2368 96" "classname" "light" } { "angle" "180" "origin" "248 2392 40" "classname" "monster_army" } { "origin" "272 2352 64" "classname" "item_spikes" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*12" } { "origin" "832 2608 16" "classname" "light" "light" "220" } { "light" "220" "classname" "light" "origin" "832 2480 0" } { "light" "240" "origin" "800 2816 24" "classname" "light" } { "style" "33" "targetname" "t11" "spawnflags" "1" "classname" "light" "origin" "752 2000 -88" "light" "400" } { "style" "34" "spawnflags" "1" "targetname" "t12" "origin" "1280 2000 -152" "classname" "light" "light" "400" } { "style" "35" "spawnflags" "1" "targetname" "t13" "classname" "light" "origin" "1280 2496 -216" "light" "400" } { "style" "36" "spawnflags" "1" "targetname" "t14" "origin" "784 2496 -280" "classname" "light" } { "classname" "light" "origin" "1368 2584 -488" "light" "200" } { "origin" "1368 1944 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2584 -488" "light" "150" } { "origin" "1016 2584 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "1016 1944 -488" "light" "200" } { "origin" "1368 2272 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2272 -488" "light" "200" } { "classname" "light" "origin" "960 2296 -488" "light" "200" } { "light" "200" "origin" "1032 2352 -488" "classname" "light" } { "classname" "light" "origin" "888 2352 -488" "light" "200" } { "light" "200" "origin" "960 2408 -488" "classname" "light" } { "light" "100" "classname" "light" "origin" "984 2448 -304" } { "classname" "light" "origin" "832 2360 112" "light" "400" } { "classname" "light" "origin" "1144 2448 -488" } { "origin" "1232 2360 -488" "classname" "light" } { "classname" "light" "origin" "1320 2448 -488" "light" "200" } { "light" "200" "origin" "1232 2536 -488" "classname" "light" } { "classname" "light" "origin" "1232 2136 -488" } { "origin" "1144 2048 -488" "classname" "light" } { "classname" "light" "origin" "1232 1960 -488" "light" "200" } { "light" "200" "origin" "1320 2048 -488" "classname" "light" } { "classname" "light" "origin" "832 2336 -200" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "sounds" "3" "model" "*13" } { "classname" "func_door_secret" "angle" "180" "sounds" "3" "model" "*14" } { "classname" "light" "origin" "552 2480 -56" "light" "200" } { "light" "200" "origin" "544 2296 -56" "classname" "light" } { "classname" "light" "origin" "664 2480 -56" "light" "200" } { "classname" "func_door" "targetname" "t4" "angle" "-2" "spawnflags" "1" "sounds" "2" "model" "*15" } { "classname" "trigger_multiple" "target" "t4" "health" "1" "model" "*16" } { "spawnflags" "2048" "classname" "func_door" "angle" "90" "targetname" "t5" "wait" "-1" "sounds" "2" "model" "*17" } { "spawnflags" "2048" "classname" "trigger_once" "target" "t5" "model" "*18" } { "classname" "item_artifact_super_damage" "origin" "544 2480 -88" } { "classname" "light" "origin" "832 2104 -208" } { "classname" "light" "origin" "832 2048 -368" "light" "150" } { "classname" "light" "origin" "1120 2464 112" } { "origin" "1120 2080 112" "classname" "light" } { "classname" "light" "origin" "752 2080 112" "light" "200" } { "classname" "light" "origin" "1048 2280 -72" } { "classname" "func_button" "angle" "270" "target" "t1" "model" "*19" } { "classname" "light" "origin" "1136 1848 -504" "light" "220" } { "origin" "1136 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1008 1672 -504" "light" "220" } { "origin" "1008 1848 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1288 1848 -504" "light" "220" } { "origin" "1400 1584 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1584 -504" "light" "220" } { "origin" "1400 1736 -504" "classname" "light" "light" "220" } { "origin" "880 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "744 1672 -504" "light" "220" } { "classname" "light" "origin" "1312 1648 -392" "light" "220" } { "light" "170" "origin" "1312 1520 -392" "classname" "light" } { "classname" "light" "origin" "1200 1760 -392" "light" "220" } { "light" "170" "origin" "1072 1760 -392" "classname" "light" } { "classname" "light" "origin" "944 1760 -392" "light" "170" } { "origin" "832 1992 -208" "classname" "light" "light" "220" } { "origin" "744 1832 -504" "classname" "light" } { "light" "170" "origin" "832 1760 -392" "classname" "light" } { "light" "220" "origin" "680 1936 -504" "classname" "light" } { "classname" "light" "origin" "1312 1392 -352" "light" "170" } { "light" "170" "origin" "1312 1264 -288" "classname" "light" } { "classname" "light" "origin" "1312 1136 -232" } { "origin" "1224 1456 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1456 -504" "light" "220" } { "origin" "1400 1328 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1328 -504" "light" "220" } { "origin" "1224 1200 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1200 -504" "light" "220" } { "origin" "1312 960 -208" "classname" "light" } { "classname" "trigger_teleport" "target" "t6" "model" "*20" } { "classname" "light" "origin" "1312 912 -472" } { "classname" "light" "origin" "1312 1080 -368" } { "classname" "light" "origin" "1128 1064 -504" "light" "170" } { "origin" "1128 856 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1496 856 -504" "light" "170" } { "origin" "1496 1064 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1312 776 -504" "light" "170" } { "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*21" } { "origin" "1072 1024 -168" "classname" "light" } { "spawnflags" "1" "height" "400" "angle" "-1" "sounds" "1" "classname" "func_plat" "model" "*22" } { "targetname" "t8" "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*23" } { "target" "t8" "classname" "trigger_multiple" "model" "*24" } { "light" "220" "origin" "792 888 -248" "classname" "light" } { "light" "180" "classname" "light" "origin" "944 608 -248" } { "light" "150" "origin" "792 512 -248" "classname" "light" } { "classname" "light" "origin" "792 512 -56" "light" "150" } { "classname" "light" "origin" "624 928 -240" "light" "220" } { "light" "220" "origin" "504 1200 -248" "classname" "light" } { "origin" "936 800 -248" "classname" "light" "light" "180" } { "light" "180" "classname" "light" "origin" "960 984 -208" } { "classname" "light" "origin" "792 512 128" "light" "150" } { "spawnflags" "2" "origin" "944 1008 -272" "classname" "item_health" } { "spawnflags" "1792" "origin" "144 2352 16" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1216 1040 -432" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "1392 1024 -432" "classname" "item_rockets" } { "targetname" "t6" "origin" "-32 1800 -56" "classname" "info_teleport_destination" } { "spawnflags" "1792" "origin" "832 2448 -368" "classname" "weapon_supernailgun" } { "spawnflags" "1792" "origin" "128 1216 -208" "classname" "weapon_supershotgun" } { "origin" "296 2136 -192" "classname" "item_shells" } { "spawnflags" "1" "origin" "1424 904 -432" "classname" "item_health" } { "classname" "item_health" "origin" "1376 808 -432" } { "origin" "1176 936 -432" "classname" "item_health" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*25" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*26" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*27" } { "target" "t10" "targetname" "t9" "count" "3" "classname" "trigger_counter" "model" "*28" } { "message" "You must press the three buttons..." "spawnflags" "2048" "sounds" "2" "wait" "-1" "targetname" "t10" "angle" "180" "classname" "func_door" "model" "*29" } { "light" "150" "origin" "832 1928 -384" "classname" "light" } { "style" "33" "sounds" "3" "target" "t11" "classname" "trigger_once" "model" "*30" } { "style" "34" "sounds" "3" "target" "t12" "classname" "trigger_once" "model" "*31" } { "style" "35" "sounds" "3" "target" "t13" "classname" "trigger_once" "model" "*32" } { "style" "36" "sounds" "3" "target" "t14" "classname" "trigger_once" "model" "*33" } { "sounds" "1" "wait" "-1" "targetname" "t11" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*34" } { "targetname" "t12" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*35" } { "targetname" "t13" "sounds" "1" "wait" "-1" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*36" } { "targetname" "t14" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*37" } { "angle" "90" "origin" "1312 880 -248" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "1376 1024 -272" "classname" "item_spikes" } { "origin" "1184 992 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1376 856 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1256 1704 -432" "classname" "item_health" } { "angle" "90" "origin" "480 48 24" "classname" "info_player_deathmatch" } { "angle" "180" "origin" "528 1888 -168" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "-272 2928 -56" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "832 2048 -152" "classname" "info_player_deathmatch" } { "speed" "300" "message" "This door opens elsewhere..." "spawnflags" "2048" "targetname" "t15" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*38" } { "spawnflags" "2048" "target" "t15" "classname" "trigger_once" "model" "*39" } { "spawnflags" "1792" "origin" "480 576 0" "classname" "weapon_nailgun" } { "spawnflags" "1793" "origin" "464 728 64" "classname" "item_spikes" } { "origin" "328 848 -224" "classname" "item_health" } { "classname" "item_health" "origin" "344 920 -224" } { "spawnflags" "1" "origin" "-16 2064 -208" "classname" "item_health" } { "spawnflags" "1792" "origin" "-480 2240 -160" "classname" "item_rockets" } { "spawnflags" "1793" "origin" "-96 2456 16" "classname" "item_shells" } { "classname" "item_rockets" "origin" "-104 2216 16" "spawnflags" "1793" } { "classname" "item_artifact_invulnerability" "origin" "256 1808 -40" "spawnflags" "1792" } { "classname" "monster_army" "origin" "0 576 24" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "8 1520 -200" "angle" "270" } { "classname" "monster_dog" "origin" "88 1520 -200" "angle" "270" } { "classname" "monster_army" "origin" "224 1552 -200" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "-8 936 -200" "classname" "monster_army" } { "classname" "monster_army" "origin" "648 736 104" "spawnflags" "768" "angle" "180" } { "classname" "item_artifact_envirosuit" "origin" "712 2040 -408" "angle" "90" } { "classname" "light" "origin" "712 2040 -360" "light" "100" } { "classname" "item_rockets" "origin" "1328 2536 -528" "spawnflags" "1793" } { "classname" "item_health" "origin" "916 2416 -136" "spawnflags" "2" } { "spawnflags" "1" "classname" "monster_army" "origin" "1312 936 -248" "angle" "90" } { "classname" "monster_dog" "origin" "1336 1784 -408" "angle" "180" "spawnflags" "257" } { "spawnflags" "257" "angle" "90" "origin" "1392 928 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1384 1008 -248" "angle" "90" "spawnflags" "768" } { "spawnflags" "768" "angle" "90" "origin" "1240 1008 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1256 1760 -408" "angle" "180" "spawnflags" "257" } { "classname" "monster_army" "origin" "824 1784 -408" "spawnflags" "257" "angle" "90" } { "classname" "monster_dog" "origin" "1128 1760 -408" "angle" "180" "spawnflags" "769" } { "classname" "path_corner" "origin" "880 2048 -168" "target" "t16" "targetname" "t17" } { "origin" "1232 2048 -232" "classname" "path_corner" "targetname" "t16" "target" "t17" } { "classname" "monster_army" "origin" "1232 2088 -216" "target" "t16" } { "classname" "monster_army" "origin" "1232 2448 -280" "angle" "270" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2464 -344" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2072 -408" "angle" "90" } { "classname" "monster_dog" "origin" "840 1960 -408" "angle" "90" "spawnflags" "768" } { "classname" "trigger_multiple" "target" "t18" "health" "1" "model" "*40" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t18" "model" "*41" } { "classname" "weapon_supershotgun" "origin" "-360 2912 -80" } { "classname" "trigger_multiple" "target" "t18" "model" "*42" } { "classname" "light" "origin" "-352 2912 -24" "light" "120" } { "classname" "light" "origin" "160 3024 0" "light" "120" } { "classname" "item_shells" "origin" "528 720 80" } { "classname" "monster_army" "origin" "416 1912 -168" "angle" "180" "spawnflags" "768" } { "classname" "monster_dog" "origin" "432 2120 -168" "angle" "180" "spawnflags" "256" } { "classname" "path_corner" "origin" "248 1992 -200" "targetname" "t19" "target" "t20" } { "origin" "-200 1992 -200" "classname" "path_corner" "targetname" "t20" "target" "t21" } { "classname" "path_corner" "origin" "-136 1912 -200" "targetname" "t21" "target" "t22" } { "origin" "248 1912 -200" "classname" "path_corner" "target" "t19" "targetname" "t22" } { "classname" "monster_army" "origin" "80 2024 -184" "target" "t20" } { "classname" "monster_army" "origin" "-16 1888 -184" "spawnflags" "256" "target" "t22" } { "classname" "monster_dog" "origin" "-248 2144 -136" "spawnflags" "768" "angle" "315" } { "classname" "path_corner" "origin" "-560 2352 40" "targetname" "t23" "target" "t24" } { "origin" "-104 2352 40" "classname" "path_corner" "target" "t23" "targetname" "t24" } { "classname" "monster_army" "origin" "-432 2352 56" "spawnflags" "768" "target" "t23" } { "angle" "0" "classname" "monster_dog" "origin" "-544 2584 56" "spawnflags" "256" } { "classname" "monster_army" "origin" "-344 2656 -104" "angle" "270" } { "classname" "monster_dog" "origin" "-72 2896 -56" "spawnflags" "256" "angle" "225" } { "classname" "monster_army" "origin" "432 2920 -56" "target" "t25" } { "classname" "monster_army" "origin" "424 2832 -56" "spawnflags" "256" "angle" "180" } { "classname" "path_corner" "origin" "368 2936 -72" "targetname" "t25" "target" "t26" } { "origin" "368 2696 -72" "classname" "path_corner" "targetname" "t26" "target" "t27" } { "classname" "path_corner" "origin" "480 2696 -72" "targetname" "t27" "target" "t28" } { "origin" "480 2936 -72" "classname" "path_corner" "target" "t25" "targetname" "t28" } { "classname" "monster_army" "origin" "424 2672 -56" "target" "t27" } { "classname" "monster_army" "origin" "424 2880 -56" "angle" "180" "spawnflags" "768" } { "classname" "monster_army" "origin" "424 2760 -56" "spawnflags" "768" "angle" "180" } { "classname" "path_corner" "origin" "832 2712 -88" "targetname" "t29" "target" "t30" } { "origin" "832 2416 -104" "classname" "path_corner" "target" "t29" "targetname" "t30" } { "classname" "monster_army" "origin" "848 2584 -72" "spawnflags" "257" "target" "t29" } { "classname" "monster_army" "origin" "824 2008 -152" "angle" "90" "spawnflags" "768" } { "classname" "item_health" "origin" "-376 1704 -224" "spawnflags" "1" } { "angle" "180" "spawnflags" "768" "origin" "248 2352 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "270" "origin" "-72 2464 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "225" "origin" "904 1024 -248" "classname" "monster_army" } { "light" "100" "style" "10" "classname" "light" "origin" "688 0 80" } { "message" "Shoot this secret door..." "spawnflags" "1" "angle" "0" "classname" "func_door_secret" "model" "*43" } { "origin" "672 -40 48" "classname" "item_shells" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_secret" "model" "*45" } { "classname" "trigger_secret" "model" "*46" } { "classname" "trigger_secret" "model" "*47" } { "classname" "trigger_secret" "model" "*48" } { "classname" "trigger_secret" "model" "*49" } { "light" "100" "origin" "0 632 -88" "classname" "light" } { "classname" "item_health" "origin" "600 2200 -128" "spawnflags" "1" } { "light" "220" "classname" "light" "origin" "832 1880 -504" } { "origin" "72 2056 -208" "classname" "misc_explobox" } { "light" "200" "origin" "-128 584 72" "classname" "light" } { "light" "200" "origin" "-128 568 -136" "classname" "light" } { "light" "100" "origin" "-56 632 -168" "classname" "light" } { "light" "200" "origin" "-56 864 -136" "classname" "light" } { "light" "200" "origin" "40 1672 -40" "classname" "light" } { "classname" "light" "origin" "216 1672 -40" "light" "200" } { "classname" "light" "origin" "128 1080 -152" "light" "200" } { "light" "200" "origin" "128 1096 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-352 1656 72" } { "origin" "608 1640 72" "classname" "light" "light" "250" } { "origin" "-48 1144 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "-48 1256 -320" } { "origin" "320 1256 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "312 1128 -320" } { "origin" "136 1128 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "136 1272 -320" } { "spawnflags" "3072" "wait" "5" "sounds" "2" "message" "You can jump across..." "classname" "trigger_multiple" "targetname" "t32" "model" "*50" } { "spawnflags" "3072" "wait" "5" "message" "You can jump up here..." "sounds" "2" "classname" "trigger_multiple" "targetname" "t31" "model" "*51" } { "light" "150" "origin" "1008 2128 -408" "classname" "light" } { "light" "250" "origin" "1312 544 -184" "classname" "light" } { "light" "200" "classname" "light" "origin" "1208 456 -184" } { "origin" "1416 456 -184" "classname" "light" "light" "200" } { "light" "170" "origin" "1312 728 -56" "classname" "light" } { "map" "e1m2" "classname" "trigger_changelevel" "model" "*52" } { "classname" "item_health" "origin" "1224 2464 -304" "spawnflags" "1" } { "classname" "light" "origin" "688 1680 -160" "light" "160" } { "light" "160" "origin" "-392 1688 -160" "classname" "light" } { "spawnflags" "768" "angle" "270" "origin" "288 1536 -200" "classname" "monster_army" } { "spawnflags" "768" "origin" "968 2432 -112" "classname" "monster_army" } { "wait" "5" "message" "Walk into the slipgate to exit." "classname" "trigger_multiple" "sounds" "2" "angle" "270" "model" "*53" } { "classname" "trigger_once" "killtarget" "t31" "target" "t31" "spawnflags" "3072" "model" "*54" } { "classname" "trigger_once" "spawnflags" "3072" "target" "t32" "killtarget" "t32" "model" "*55" } { "classname" "item_armor2" "origin" "1312 1048 -432" } { "classname" "ambient_comp_hum" "origin" "250 194 72" } { "origin" "714 194 72" "classname" "ambient_comp_hum" } { "classname" "ambient_comp_hum" "origin" "626 2058 -104" } { "origin" "466 2226 -104" "classname" "ambient_comp_hum" } { "classname" "info_intermission" "origin" "-112 704 56" "mangle" "20 45 0" } { "classname" "info_intermission" "origin" "-208 2736 192" "mangle" "20 225 0" } { "classname" "info_intermission" "origin" "240 2664 104" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1376 1936 64" "mangle" "20 135 0" } { "angle" "90" "origin" "528 -296 72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "432 -296 72" "angle" "90" } { "angle" "90" "origin" "480 -240 72" "classname" "info_player_coop" } { "classname" "func_wall" "spawnflags" "1792" "model" "*56" } { "classname" "func_wall" "spawnflags" "1792" "model" "*57" } { "classname" "ambient_drone" "origin" "1314 450 -200" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m4.ent.orig0000644000000000000000000012514512425501423020007 0ustar rootroot{ "message" "the Grisly Grotto" "worldtype" "0" "classname" "worldspawn" "wad" "gfx/wizard.wad" "sounds" "5" } { "classname" "light" "origin" "464 480 1656" "light" "300" } { "light" "400" "origin" "712 296 1512" "classname" "light" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*1" } { "angle" "0" "classname" "func_door" "model" "*2" } { "classname" "light_flame_small_yellow" "origin" "560 -112 1374" } { "origin" "848 -112 1374" "classname" "light_flame_small_yellow" } { "origin" "760 656 1536" "classname" "light" } { "light" "400" "origin" "834 498 1040" "classname" "light" } { "light" "200" "classname" "light" "origin" "944 728 1456" } { "classname" "light_flame_small_yellow" "origin" "1016 80 998" } { "origin" "392 80 998" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "680 1224 516" "light" "200" } { "light" "200" "origin" "704 992 516" "classname" "light" } { "light" "200" "origin" "696 1608 628" "classname" "light" } { "origin" "704 1368 588" "classname" "light" } { "origin" "816 1616 444" "classname" "light" } { "classname" "light" "origin" "712 1728 444" } { "origin" "592 1608 444" "classname" "light" } { "classname" "light" "origin" "624 1096 444" } { "light" "300" "origin" "432 1328 816" "classname" "light" } { "classname" "light" "origin" "696 1112 816" "light" "300" } { "classname" "light" "origin" "704 -80 960" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "818 18 948" "light" "200" } { "origin" "586 18 948" "classname" "light_torch_small_walltorch" "light" "200" } { "light" "200" "origin" "688 496 880" "classname" "light" } { "origin" "489 483 1356" "classname" "light_flame_large_yellow" } { "light" "200" "origin" "704 -120 1360" "classname" "light" } { "classname" "light" "origin" "1216 936 1560" "light" "200" } { "origin" "1294 826 1576" "classname" "light_flame_large_yellow" } { "sounds" "1" "targetname" "t1" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*3" } { "angle" "0" "wait" "-1" "classname" "func_door" "model" "*4" } { "target" "t1" "classname" "trigger_once" "model" "*5" } { "map" "e1m5" "classname" "trigger_changelevel" "model" "*6" } { "wait" "-1" "angle" "0" "classname" "func_door" "speed" "50" "model" "*7" } { "classname" "info_player_start" "origin" "-256 2272 1240" "angle" "270" } { "targetname" "t23" "classname" "func_door" "angle" "180" "wait" "-1" "speed" "50" "sounds" "3" "model" "*8" } { "classname" "light" "origin" "696 704 820" } { "classname" "light" "origin" "704 776 672" "light" "200" } { "classname" "light" "origin" "360 904 520" "light" "200" } { "origin" "704 856 400" "classname" "light" "light" "200" } { "classname" "light" "origin" "944 880 416" "light" "150" } { "origin" "1056 1176 424" "classname" "light" "light" "200" } { "classname" "light" "origin" "1096 1408 360" } { "classname" "light" "origin" "416 1696 360" } { "origin" "328 1368 360" "classname" "light" "light" "200" } { "light" "200 " "classname" "light" "origin" "696 752 896" } { "light" "250" "origin" "798 1850 1024" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "642 1850 1024" "light" "250" } { "light" "200 " "origin" "700 1364 952" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "1094 1494 1064" } { "origin" "324 1104 1064" "classname" "light_flame_large_yellow" } { "light" "200 " "origin" "704 1660 952" "classname" "light" } { "sounds" "3" "classname" "func_door" "angle" "180" "wait" "-1" "targetname" "t2" "model" "*9" } { "sounds" "3" "classname" "func_door" "wait" "-1" "angle" "0" "targetname" "t4" "model" "*10" } { "classname" "trigger_once" "targetname" "t4" "target" "t7" "model" "*11" } { "classname" "trigger_once" "targetname" "t2" "target" "t7" "model" "*12" } { "dmg" "90" "speed" "200" "classname" "func_train" "target" "t5" "targetname" "t8" "model" "*13" } { "classname" "path_corner" "origin" "-359 1528 1316" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-359 1528 880" "targetname" "t6" "target" "t5" "wait" "-1" } { "classname" "trigger_counter" "targetname" "t7" "target" "t8" "count" "2" "model" "*14" } { "targetname" "t11" "origin" "-96 1640 1256" "classname" "info_null" } { "targetname" "t9" "origin" "-416 1640 1256" "classname" "info_null" } { "light" "400" "target" "t9" "origin" "-396 1640 1256" "classname" "light" } { "light" "400" "target" "t11" "origin" "-116 1640 1256" "classname" "light" } { "classname" "light" "origin" "-328 1564 1532" } { "classname" "light_flame_small_yellow" "origin" "-88 1640 1514" } { "origin" "-424 1640 1514" "classname" "light_flame_small_yellow" } { "origin" "-248 1464 1154" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "-256 1808 1046" } { "light" "200" "origin" "-164 1732 1268" "classname" "light" } { "classname" "light" "origin" "-348 1732 1268" "light" "200" } { "light" "150" "origin" "-248 1500 1056" "classname" "light" } { "light" "150" "origin" "-256 1772 956" "classname" "light" } { "classname" "light" "origin" "-128 1636 920" "light" "150" } { "light" "150" "origin" "-124 1640 920" "classname" "light" } { "classname" "light" "origin" "-172 1524 920" "light" "150" } { "light" "150" "origin" "-284 1516 920" "classname" "light" } { "classname" "light" "origin" "-360 1580 920" "light" "150" } { "light" "75" "origin" "-360 1700 920" "classname" "light" } { "classname" "light" "origin" "72 1632 928" "light" "125" } { "light" "150" "origin" "80 1488 928" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "158 1308 1064" } { "classname" "light" "origin" "-192 1688 1380" "light" "100" } { "light" "100" "origin" "-192 1592 1380" "classname" "light" } { "classname" "light" "origin" "-320 1592 1380" "light" "100" } { "light" "100" "origin" "-320 1688 1380" "classname" "light" } { "classname" "light" "origin" "-384 1640 1452" "light" "125" } { "classname" "light" "origin" "-112 1640 1452" "light" "125" } { "light" "200" "origin" "-248 1504 1336" "classname" "light" } { "classname" "light" "origin" "-128 1640 1336" "light" "200" } { "light" "200" "origin" "-384 1640 1336" "classname" "light" } { "classname" "light" "origin" "696 1608 816" "light" "300" } { "light" "200" "classname" "light" "origin" "888 1328 424" } { "classname" "light" "origin" "160 1352 960" "light" "150" } { "light" "150" "origin" "368 1104 1000" "classname" "light" } { "classname" "light" "origin" "1048 1504 1000" "light" "150" } { "classname" "light" "origin" "992 992 1048" "light" "150" } { "classname" "light" "origin" "48 1384 1008" "light" "100" } { "light" "150" "origin" "1080 1144 1048" "classname" "light" } { "classname" "light" "origin" "968 824 976" "light" "150" } { "classname" "light" "origin" "896 1328 1000" "light" "150" } { "classname" "light" "origin" "368 1616 1000" "light" "175" } { "classname" "light" "origin" "256 1496 824" "light" "150" } { "light" "150" "origin" "256 1352 824" "classname" "light" } { "classname" "light" "origin" "856 1368 536" "light" "200" } { "light" "150" "origin" "552 1360 536" "classname" "light" } { "classname" "light" "origin" "872 1552 1056" "light" "150" } { "classname" "light" "origin" "1032 1056 904" "light" "150" } { "light" "150" "origin" "1080 1184 904" "classname" "light" } { "classname" "light" "origin" "864 848 904" "light" "150" } { "classname" "light" "origin" "312 1496 1016" "light" "175" } { "light" "200" "origin" "704 1368 808" "classname" "light" } { "light" "150" "origin" "464 816 904" "classname" "light" } { "light" "200" "origin" "944 888 800" "classname" "light" } { "light" "150" "origin" "888 1112 728" "classname" "light" } { "light" "175" "origin" "1008 1504 728" "classname" "light" } { "light" "200" "origin" "720 1848 1200" "classname" "light" } { "light" "200" "origin" "704 888 672" "classname" "light" } { "light" "175" "origin" "512 1456 1040" "classname" "light" } { "light" "150" "origin" "440 840 380" "classname" "light" } { "light" "150" "origin" "720 1848 1028" "classname" "light" } { "light" "150" "origin" "720 1936 1024" "classname" "light" } { "origin" "544 2128 984" "classname" "light" "light" "200" } { "light" "200" "origin" "712 1912 576" "classname" "light" } { "light" "300" "origin" "720 2544 856" "classname" "light" } { "light" "200" "origin" "888 2048 592" "classname" "light" } { "origin" "704 2496 1128" "classname" "light" } { "origin" "512 2048 976" "classname" "light" "light" "150" } { "origin" "952 2328 528" "classname" "light" "light" "200" } { "classname" "light" "origin" "700 2760 808" "light" "200" } { "classname" "light" "origin" "700 2800 616" "light" "200" } { "classname" "light" "origin" "584 2780 584" "light" "175" } { "light" "175" "origin" "808 2780 584" "classname" "light" } { "sounds" "3" "wait" "-1" "targetname" "t29" "classname" "func_door" "angle" "270" "model" "*15" } { "message" "This door is opened elsewhere..." "wait" "-1" "classname" "func_door" "angle" "90" "model" "*16" } { "classname" "light" "origin" "424 2304 1000" "light" "200" } { "light" "300" "origin" "696 2880 1062" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "864 2128 984" } { "classname" "light_flame_large_yellow" "origin" "702 2154 1228" "light" "0" } { "classname" "light" "origin" "832 2008 1136" "light" "200" } { "classname" "light" "origin" "584 2008 1136" "light" "200" } { "light" "200" "origin" "272 2016 1136" "classname" "light" } { "classname" "light" "origin" "272 2320 1136" "light" "150" } { "light" "150" "origin" "1104 2408 984" "classname" "light" } { "sounds" "2" "classname" "func_plat" "wait" "4" "model" "*17" } { "classname" "light" "origin" "700 2844 996" "light" "200" } { "classname" "light" "origin" "704 2216 1252" } { "origin" "936 2304 1252" "classname" "light" } { "classname" "light" "origin" "936 2536 1252" } { "light" "300" "classname" "light" "origin" "488 2536 1252" } { "origin" "488 2304 1252" "classname" "light" "light" "300" } { "light" "0" "origin" "998 2306 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "998 2534 1228" "light" "0" } { "light" "0" "origin" "426 2534 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "426 2306 1228" "light" "0" } { "classname" "light" "origin" "704 2152 984" "light" "200" } { "classname" "light_flame_small_yellow" "origin" "1160 2400 1038" "light" "250" } { "origin" "840 2536 630" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light_flame_small_yellow" "origin" "584 2544 630" "light" "250" } { "light" "250" "origin" "408 2304 694" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "960 2632 528" } { "classname" "light" "origin" "960 2480 528" "light" "150" } { "classname" "light" "origin" "456 2304 624" "light" "175" } { "classname" "light" "origin" "704 2416 576" "light" "250" } { "light" "150" "origin" "728 2184 528" "classname" "light" } { "classname" "light" "origin" "512 2176 528" "light" "150" } { "classname" "light" "origin" "832 2336 656" "light" "175" } { "light" "175" "origin" "592 2336 656" "classname" "light" } { "classname" "light" "origin" "808 2584 576" "light" "150" } { "light" "150" "origin" "616 2576 576" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "488 2712 694" "light" "250" } { "classname" "light" "origin" "488 2624 608" "light" "175" } { "classname" "light" "origin" "480 2464 608" "light" "175" } { "light" "250" "origin" "184 2184 1038" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "512 2096 1038" "light" "250" } { "classname" "light" "origin" "224 2184 976" "light" "150" } { "classname" "light" "origin" "848 2024 992" "light" "200" } { "classname" "light" "origin" "256 1992 1000" "light" "150" } { "classname" "light" "origin" "272 2376 1000" "light" "200" } { "classname" "light" "origin" "1112 2208 1032" "light" "150" } { "light" "0" "origin" "698 2860 1228" "classname" "light_flame_large_yellow" } { "origin" "700 2808 1252" "classname" "light" } { "light" "250" "origin" "472 2632 1062" "classname" "light" } { "classname" "light" "origin" "952 2632 1062" "light" "250" } { "light" "150" "origin" "952 2424 894" "classname" "light" } { "classname" "light" "origin" "480 2424 894" "light" "150" } { "light" "150" "origin" "896 2296 966" "classname" "light" } { "classname" "light" "origin" "704 2304 966" "light" "150" } { "light" "150" "origin" "552 2304 966" "classname" "light" } { "light" "250" "origin" "704 2184 406" "classname" "light" } { "light" "150" "origin" "712 2000 406" "classname" "light" } { "light" "200" "origin" "504 2224 400" "classname" "light" } { "light" "200" "origin" "960 2576 400" "classname" "light" } { "light" "150" "origin" "960 2400 400" "classname" "light" } { "light" "150" "origin" "808 1112 592" "classname" "light" } { "classname" "light" "origin" "600 1112 592" "light" "150" } { "light" "175" "origin" "1016 128 936" "classname" "light" } { "classname" "light" "origin" "392 128 936" "light" "175" } { "light" "150" "origin" "848 -64 1304" "classname" "light" } { "classname" "light" "origin" "560 -64 1304" "light" "150" } { "light" "175" "origin" "1248 832 1496" "classname" "light" } { "light" "150" "origin" "1096 872 1496" "classname" "light" } { "light" "150" "origin" "896 2208 976" "classname" "light" } { "classname" "light" "origin" "1096 200 1512" "light" "350" } { "light" "350" "origin" "264 192 1512" "classname" "light" } { "light" "175" "origin" "488 448 1264" "classname" "light" } { "origin" "1048 728 1456" "classname" "light" "light" "200" } { "light" "125" "origin" "1328 928 1448" "classname" "light" } { "light" "225" "origin" "696 672 1320" "classname" "light" } { "origin" "816 -56 1640" "classname" "light" } { "classname" "light" "origin" "584 -56 1640" } { "light" "175" "origin" "880 96 1376" "classname" "light" } { "classname" "light" "origin" "528 96 1376" "light" "175" } { "light" "200" "origin" "960 344 1072" "classname" "light" } { "classname" "light" "origin" "1120 264 1072" "light" "200" } { "light" "175" "origin" "1208 96 1016" "classname" "light" } { "light" "175" "origin" "1024 360 968" "classname" "light" } { "classname" "light" "origin" "328 336 968" "light" "150" } { "classname" "light" "origin" "416 424 1072" "light" "200" } { "light" "200" "origin" "296 256 1072" "classname" "light" } { "light" "150" "origin" "704 48 1040" "classname" "light" } { "light" "175" "origin" "384 464 976" "classname" "light" } { "classname" "light" "origin" "224 264 976" "light" "150" } { "light" "150" "origin" "952 2192 416" "classname" "light" } { "classname" "light" "origin" "1040 -464 1016" "light" "150" } { "sounds" "2" "spawnflags" "1" "classname" "func_plat" "model" "*18" } { "light" "250" "origin" "1072 -272 1262" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1024 -272 1200" "light" "150" } { "light" "200" "origin" "624 -240 1328" "classname" "light" } { "light" "175" "origin" "1080 -624 1168" "classname" "light" } { "origin" "456 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "504 -576 1168" "light" "175" } { "origin" "624 -448 924" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "624 -704 924" } { "light" "125" "origin" "664 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -488 880" "light" "125" } { "light" "125" "origin" "584 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -408 880" "light" "125" } { "light" "125" "origin" "624 -664 880" "classname" "light" } { "classname" "light" "origin" "664 -704 880" "light" "125" } { "light" "125" "origin" "624 -744 880" "classname" "light" } { "classname" "light" "origin" "584 -704 880" "light" "125" } { "light" "250" "origin" "296 -96 968" "classname" "light" } { "light" "250" "origin" "1112 -96 968" "classname" "light" } { "light" "175" "origin" "1056 -224 968" "classname" "light" } { "light" "500" "origin" "-264 2120 1504" "classname" "light" } { "spawnflags" "2064" "angle" "0" "classname" "func_door" "wait" "-1" "model" "*19" } { "sounds" "3" "classname" "func_door" "angle" "180" "spawnflags" "2064" "wait" "-1" "model" "*20" } { "light" "200" "origin" "704 32 952" "classname" "light" } { "target" "t101" "spawnflags" "256" "targetname" "t23" "angle" "90" "origin" "-248 1560 1224" "classname" "monster_wizard" } { "target" "t23" "classname" "trigger_once" "model" "*21" } { "origin" "-280 1560 1348" "classname" "item_armor2" } { "origin" "-488 2112 1220" "classname" "item_health" } { "classname" "item_health" "origin" "-488 2064 1220" } { "spawnflags" "1536" "origin" "-272 1784 1156" "classname" "item_shells" } { "spawnflags" "256" "target" "t25" "targetname" "t24" "origin" "888 1640 1028" "classname" "path_corner" } { "spawnflags" "256" "target" "t24" "targetname" "t25" "classname" "path_corner" "origin" "624 1264 1028" } { "spawnflags" "256" "target" "t24" "origin" "928 1672 1028" "classname" "monster_wizard" } { "spawnflags" "2304" "target" "t26" "classname" "trigger_once" "model" "*22" } { "target" "t27" "targetname" "t28" "origin" "568 2040 928" "classname" "path_corner" } { "target" "t28" "targetname" "t27" "classname" "path_corner" "origin" "368 2044 928" } { "target" "t27" "origin" "472 2040 944" "classname" "monster_ogre" "spawnflags" "1" } { "sounds" "2" "target" "t29" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*23" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*24" } { "angle" "0" "classname" "func_door" "model" "*25" } { "light" "175" "origin" "1112 2520 964" "classname" "light" } { "origin" "104 1308 892" "classname" "item_health" } { "spawnflags" "2048" "origin" "704 1344 936" "classname" "item_key1" "sounds" "1" } { "target" "t34" "angle" "180" "origin" "920 2040 544" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t35" "targetname" "t34" "origin" "864 2044 528" "classname" "path_corner" } { "target" "t37" "targetname" "t35" "classname" "path_corner" "origin" "704 2048 528" } { "target" "t34" "targetname" "t36" "origin" "704 2048 528" "classname" "path_corner" } { "target" "t36" "targetname" "t37" "classname" "path_corner" "origin" "704 1808 528" } { "targetname" "t38" "angle" "270" "origin" "456 2476 544" "classname" "monster_ogre" } { "target" "t38" "classname" "trigger_once" "model" "*26" } { "targetname" "t29" "angle" "0" "origin" "336 2272 952" "classname" "monster_knight" "spawnflags" "768" } { "targetname" "t39" "spawnflags" "1" "wait" "-1" "angle" "-2" "classname" "func_door" "sounds" "1" "model" "*27" } { "targetname" "t39" "angle" "270" "origin" "712 2540 448" "classname" "monster_ogre" } { "target" "t39" "classname" "trigger_once" "model" "*28" } { "classname" "light" "origin" "712 2544 440" "light" "200" } { "classname" "item_health" "origin" "1064 2184 920" "spawnflags" "1024" } { "spawnflags" "1" "origin" "1128 2184 920" "classname" "item_health" } { "classname" "item_spikes" "origin" "816 2840 920" } { "origin" "816 2800 920" "classname" "item_spikes" } { "spawnflags" "256" "classname" "monster_wizard" "origin" "1656 1496 968" "angle" "180" "target" "t41" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t40" "targetname" "t39" "model" "*29" } { "classname" "info_teleport_destination" "origin" "984 1496 1000" "angle" "180" "targetname" "t40" } { "spawnflags" "256" "classname" "path_corner" "origin" "912 1496 1000" "targetname" "t41" "target" "t42" } { "spawnflags" "256" "origin" "528 1368 1000" "classname" "path_corner" "targetname" "t42" "target" "t41" } { "classname" "item_health" "origin" "408 2640 520" } { "classname" "item_shells" "origin" "456 2672 520" "spawnflags" "1" } { "angle" "0" "origin" "80 864 968" "classname" "monster_wizard" "target" "t44" } { "targetname" "t119" "spawnflags" "2" "classname" "trigger_teleport" "target" "t45" "model" "*30" } { "classname" "info_teleport_destination" "origin" "432 856 952" "angle" "45" "targetname" "t45" } { "classname" "path_corner" "origin" "496 872 952" "target" "t43" "targetname" "t44" } { "origin" "872 1056 952" "classname" "path_corner" "targetname" "t43" "target" "t44" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t39" "model" "*31" } { "spawnflags" "1024" "classname" "monster_knight" "origin" "1168 56 904" "angle" "135" "target" "t49" "targetname" "t67" } { "classname" "monster_ogre" "origin" "1800 224 920" "angle" "180" "target" "t116" "spawnflags" "256" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t49" "lip" "-24" "model" "*32" } { "wait" "-1" "angle" "-1" "classname" "func_door" "spawnflags" "1" "targetname" "t39" "lip" "-24" "model" "*33" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t29" "lip" "-24" "model" "*34" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t46" "model" "*35" } { "classname" "info_teleport_destination" "origin" "1104 232 880" "angle" "180" "targetname" "t46" } { "classname" "path_corner" "origin" "1064 256 880" "target" "t47" "targetname" "t48" "spawnflags" "256" } { "origin" "312 232 880" "classname" "path_corner" "targetname" "t47" "target" "t48" "spawnflags" "256" } { "classname" "info_teleport_destination" "origin" "704 -40 1256" "angle" "90" "targetname" "t50" } { "classname" "trigger_once" "target" "t52" "model" "*36" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t50" "targetname" "t52" "model" "*37" } { "angle" "90" "origin" "570 -898 1300" "classname" "monster_wizard" "targetname" "t52" } { "classname" "item_shells" "origin" "216 120 880" "spawnflags" "1" } { "classname" "item_health" "origin" "192 232 880" } { "origin" "192 192 880" "classname" "item_health" "spawnflags" "1024" } { "classname" "item_shells" "origin" "-216 1464 888" } { "classname" "item_health" "origin" "816 1160 512" "spawnflags" "1024" } { "spawnflags" "1" "origin" "816 1120 512" "classname" "item_health" } { "classname" "monster_wizard" "origin" "944 840 956" "angle" "135" "target" "t53" "targetname" "t64" } { "classname" "trigger_once" "target" "t64" "model" "*38" } { "classname" "monster_knight" "origin" "704 392 1280" "angle" "270" "target" "t65" "spawnflags" "1" } { "classname" "path_corner" "origin" "704 208 1264" "targetname" "t65" "target" "t66" } { "origin" "704 496 1264" "classname" "path_corner" "targetname" "t66" "target" "t65" } { "classname" "trigger_once" "target" "t67" "model" "*39" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t72" "model" "*40" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t72" "model" "*41" } { "classname" "trigger_once" "target" "t72" "model" "*42" } { "classname" "info_null" "origin" "852 -580 820" "targetname" "t73" } { "classname" "light" "origin" "856 -584 936" "light" "400" "target" "t73" } { "classname" "light" "origin" "920 -448 744" "light" "150" } { "light" "150" "origin" "760 -448 744" "classname" "light" } { "classname" "light" "origin" "552 -424 744" "light" "150" } { "light" "150" "origin" "560 -728 744" "classname" "light" } { "classname" "light" "origin" "776 -712 744" "light" "150" } { "light" "150" "origin" "936 -712 744" "classname" "light" } { "classname" "path_corner" "origin" "652 -576 952" "targetname" "t75" "target" "t74" } { "origin" "908 -576 952" "classname" "path_corner" "targetname" "t74" "target" "t75" } { "classname" "monster_ogre" "origin" "816 -260 952" "angle" "270" "targetname" "t72" } { "classname" "monster_ogre" "origin" "724 -260 952" "angle" "270" "targetname" "t72" "spawnflags" "256" } { "classname" "item_health" "origin" "632 -548 820" "spawnflags" "3072" } { "origin" "672 -548 820" "classname" "item_health" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t76" "model" "*43" } { "sounds" "1" "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t76" "model" "*44" } { "light" "150" "origin" "1040 -712 1016" "classname" "light" } { "origin" "1112 -576 942" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1064 -576 896" "light" "150" } { "classname" "light" "origin" "888 -80 968" "light" "175" } { "light" "175" "origin" "512 -80 968" "classname" "light" } { "classname" "item_armor2" "origin" "1184 -96 920" } { "classname" "monster_ogre" "origin" "392 8 912" "angle" "315" "targetname" "t77" "spawnflags" "256" } { "classname" "trigger_once" "target" "t77" "model" "*45" } { "classname" "item_health" "origin" "336 -224 888" "spawnflags" "1" } { "classname" "item_spikes" "origin" "968 16 888" "spawnflags" "1" } { "classname" "item_health" "origin" "560 2808 516" "spawnflags" "1" } { "classname" "path_corner" "origin" "1056 -384 824" "targetname" "t78" "target" "t79" "spawnflags" "256" } { "origin" "1056 -736 824" "classname" "path_corner" "targetname" "t79" "target" "t78" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "1064 -656 840" "angle" "90" "target" "t78" "spawnflags" "257" } { "angle" "90" "origin" "848 -880 952" "classname" "monster_ogre" "targetname" "t72" } { "classname" "item_shells" "origin" "1160 16 1248" } { "classname" "item_health" "origin" "280 64 1248" } { "classname" "item_rockets" "origin" "648 -256 928" "spawnflags" "1025" } { "classname" "item_spikes" "origin" "648 -304 1248" } { "origin" "696 -304 1248" "classname" "item_spikes" } { "classname" "light_flame_small_yellow" "origin" "768 -352 1230" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*46" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*47" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*48" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*49" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*50" } { "targetname" "t83" "classname" "trap_spikeshooter" "origin" "1108 -576 1140" "spawnflags" "1" "angle" "180" } { "targetname" "t83" "angle" "90" "spawnflags" "1" "origin" "768 -796 1140" "classname" "trap_spikeshooter" } { "origin" "1112 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "768 -800 1230" } { "classname" "light" "origin" "712 -756 1168" "light" "175" } { "light" "175" "origin" "768 -424 1168" "classname" "light" } { "light" "175" "origin" "888 -744 956" "classname" "light" } { "classname" "light" "origin" "888 -408 956" "light" "175" } { "origin" "384 -224 888" "classname" "item_shells" } { "origin" "584 1784 920" "classname" "item_health" } { "origin" "832 2064 920" "classname" "item_shells" } { "target" "t72" "classname" "trigger_once" "model" "*51" } { "target" "t85" "targetname" "t84" "origin" "1560 216 896" "classname" "path_corner" } { "target" "t48" "targetname" "t85" "classname" "path_corner" "origin" "1456 216 896" } { "origin" "704 1368 516" "classname" "weapon_supernailgun" } { "spawnflags" "1" "origin" "184 1928 920" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "656 1816 528" "spawnflags" "768" } { "classname" "item_shells" "origin" "1072 -800 820" "spawnflags" "1024" } { "classname" "monster_ogre" "origin" "840 -40 1276" "angle" "180" "targetname" "t86" "spawnflags" "768" } { "classname" "trigger_once" "target" "t86" "model" "*52" } { "classname" "light" "origin" "472 -576 876" "light" "150" } { "classname" "item_shells" "origin" "656 680 1256" "spawnflags" "1536" } { "angle" "270" "origin" "880 2224 536" "classname" "monster_knight" "spawnflags" "256" } { "angle" "180" "origin" "1112 2424 944" "classname" "monster_ogre" "spawnflags" "1281" } { "targetname" "t88" "target" "t87" "origin" "360 384 880" "classname" "path_corner" "spawnflags" "1280" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "504 160 880" "spawnflags" "1280" } { "target" "t87" "angle" "315" "origin" "384 320 896" "classname" "monster_knight" "spawnflags" "1280" } { "spawnflags" "257" "targetname" "t86" "angle" "0" "origin" "376 120 1272" "classname" "monster_ogre" } { "origin" "776 1368 916" "classname" "item_shells" "spawnflags" "1024" } { "classname" "item_spikes" "origin" "184 1968 920" "spawnflags" "1" } { "classname" "monster_wizard" "origin" "312 936 944" "angle" "45" "spawnflags" "769" } { "classname" "monster_knight" "origin" "704 -80 908" "angle" "90" } { "angle" "0" "origin" "568 -56 1276" "classname" "monster_ogre" "targetname" "t86" "spawnflags" "768" } { "spawnflags" "1536" "targetname" "t90" "target" "t89" "origin" "720 1696 924" "classname" "path_corner" } { "spawnflags" "1536" "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "720 1416 924" } { "spawnflags" "1537" "target" "t90" "origin" "704 1784 948" "classname" "monster_ogre" } { "classname" "func_door_secret" "angle" "0" "spawnflags" "1" "targetname" "t91" "model" "*53" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*54" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*55" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*56" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*57" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*58" } { "classname" "trigger_counter" "count" "5" "target" "t91" "targetname" "t92" "model" "*59" } { "classname" "light" "origin" "680 -920 1144" "light" "150" } { "classname" "item_spikes" "origin" "752 -876 928" "spawnflags" "1" } { "spawnflags" "3" "angle" "0" "classname" "func_door_secret" "targetname" "t91" "model" "*60" } { "light" "150" "origin" "680 -256 1144" "classname" "light" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t72" "model" "*61" } { "classname" "trigger_secret" "sounds" "1" "targetname" "t8" "model" "*62" } { "angle" "270" "origin" "704 624 1280" "classname" "monster_knight" "spawnflags" "1" } { "angle" "225" "origin" "1016 -440 1128" "classname" "monster_knight" } { "angle" "180" "origin" "1024 -728 1128" "classname" "monster_knight" } { "spawnflags" "256" "angle" "180" "origin" "1008 -568 1128" "classname" "monster_knight" } { "spawnflags" "256" "classname" "monster_knight" "origin" "304 2312 952" "angle" "0" "targetname" "t29" } { "spawnflags" "1025" "classname" "monster_knight" "origin" "272 136 896" "angle" "45" } { "classname" "monster_wizard" "origin" "784 -576 960" "angle" "0" "target" "t74" "spawnflags" "1" } { "classname" "item_health" "origin" "732 -936 928" } { "origin" "772 -936 928" "classname" "item_health" } { "spawnflags" "256" "classname" "monster_knight" "origin" "704 -248 1272" "angle" "0" } { "classname" "path_corner" "origin" "1328 928 1396" "targetname" "t93" "target" "t94" "spawnflags" "512" } { "origin" "1176 928 1396" "classname" "path_corner" "target" "t93" "targetname" "t94" "spawnflags" "512" } { "classname" "monster_knight" "origin" "1192 884 1412" "angle" "0" "target" "t93" "spawnflags" "513" } { "classname" "monster_knight" "origin" "704 2376 944" "angle" "90" } { "classname" "monster_knight" "origin" "888 2312 944" "angle" "180" "targetname" "t95" } { "angle" "0" "origin" "512 2304 944" "classname" "monster_knight" "targetname" "t95" "spawnflags" "768" } { "classname" "trigger_once" "target" "t95" "model" "*63" } { "spawnflags" "256" "angle" "315" "origin" "728 2312 536" "classname" "monster_knight" } { "light" "200" "origin" "1296 1528 936" "classname" "light" } { "light" "250" "origin" "1432 1360 982" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1400 1360 920" "classname" "light" } { "light" "200" "origin" "1248 1344 680" "classname" "light" } { "lip" "-384" "wait" "-1" "angle" "90" "classname" "func_door" "targetname" "t98" "model" "*64" } { "light" "150" "origin" "1152 1328 584" "classname" "light" } { "origin" "1376 1480 864" "classname" "item_health" } { "sounds" "1" "classname" "trigger_secret" "model" "*65" } { "classname" "func_button" "sounds" "1" "angle" "0" "wait" "-1" "target" "t97" "model" "*66" } { "classname" "func_button" "wait" "-1" "angle" "0" "sounds" "1" "target" "t97" "model" "*67" } { "classname" "trigger_counter" "targetname" "t97" "target" "t98" "model" "*68" } { "classname" "light" "origin" "880 -888 968" "light" "150" } { "light" "150" "origin" "880 -264 968" "classname" "light" } { "angle" "180" "origin" "1112 2344 944" "classname" "monster_ogre" "spawnflags" "769" } { "angle" "270" "origin" "1120 880 1412" "classname" "monster_ogre" "spawnflags" "257" } { "map" "e1m8" "classname" "trigger_changelevel" "model" "*69" } { "light" "175" "origin" "824 -756 1168" "classname" "light" } { "classname" "light" "origin" "1080 -528 1168" "light" "175" } { "classname" "monster_wizard" "origin" "672 -392 1024" "angle" "315" "spawnflags" "257" } { "classname" "monster_knight" "origin" "1008 -656 1128" "angle" "180" "spawnflags" "768" } { "origin" "520 1064 440" "classname" "air_bubbles" } { "classname" "item_spikes" "origin" "16 1432 892" } { "classname" "trigger_once" "message" "A secret cave has opened..." "targetname" "t98" "model" "*70" } { "target" "t4" "health" "1" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*71" } { "target" "t2" "health" "1" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*72" } { "target" "t49" "spawnflags" "769" "angle" "135" "origin" "1208 128 904" "classname" "monster_demon1" } { "spawnflags" "769" "angle" "45" "origin" "288 160 896" "classname" "monster_demon1" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "692 -884 952" "angle" "90" } { "classname" "monster_ogre" "origin" "-312 1648 1372" "angle" "90" "targetname" "t23" "spawnflags" "768" } { "angle" "90" "origin" "-192 1648 1372" "classname" "monster_ogre" "targetname" "t23" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "704 1288 540" "angle" "270" "spawnflags" "768" } { "targetname" "t101" "target" "t106" "spawnflags" "770" "classname" "trigger_teleport" "model" "*73" } { "spawnflags" "768" "targetname" "t101" "angle" "270" "origin" "-256 2424 1288" "classname" "monster_wizard" } { "targetname" "t101" "origin" "-248 2440 1280" "classname" "trigger_relay" } { "targetname" "t29" "spawnflags" "256" "angle" "0" "origin" "144 2648 1024" "classname" "monster_wizard" } { "targetname" "t29" "spawnflags" "768" "classname" "monster_wizard" "origin" "144 2592 1024" "angle" "0" } { "targetname" "t29" "spawnflags" "768" "angle" "0" "origin" "144 2536 1024" "classname" "monster_wizard" } { "targetname" "t29" "target" "t102" "spawnflags" "258" "classname" "trigger_teleport" "model" "*74" } { "targetname" "t29" "target" "t103" "spawnflags" "770" "classname" "trigger_teleport" "model" "*75" } { "targetname" "t29" "target" "t104" "spawnflags" "770" "classname" "trigger_teleport" "model" "*76" } { "angle" "270" "targetname" "t102" "spawnflags" "256" "origin" "704 2656 1008" "classname" "info_teleport_destination" } { "angle" "270" "targetname" "t103" "spawnflags" "768" "classname" "info_teleport_destination" "origin" "920 2520 1008" } { "angle" "0" "targetname" "t104" "spawnflags" "768" "origin" "592 2192 1008" "classname" "info_teleport_destination" } { "sounds" "1" "classname" "func_door" "angle" "0" "wait" "-1" "speed" "150" "targetname" "t105" "model" "*77" } { "classname" "monster_demon1" "origin" "1056 -880 1128" "angle" "90" "spawnflags" "768" "targetname" "t105" } { "classname" "trigger_once" "spawnflags" "768" "target" "t105" "model" "*78" } { "classname" "light" "origin" "1056 -920 1184" "light" "125" } { "classname" "trigger_relay" "origin" "1104 -864 1120" "target" "t105" } { "classname" "monster_ogre" "origin" "1120 768 1416" "angle" "180" "spawnflags" "769" } { "classname" "air_bubbles" "origin" "884 1616 440" } { "targetname" "t106" "spawnflags" "768" "angle" "270" "origin" "-264 2232 1296" "classname" "info_teleport_destination" } { "classname" "path_corner" "origin" "568 1984 928" "targetname" "t107" "target" "t108" "spawnflags" "256" } { "origin" "424 1960 928" "classname" "path_corner" "target" "t107" "spawnflags" "256" "targetname" "t111" } { "classname" "path_corner" "origin" "704 2024 928" "targetname" "t108" "target" "t109" "spawnflags" "256" } { "origin" "712 1712 928" "classname" "path_corner" "targetname" "t109" "target" "t110" "spawnflags" "256" } { "classname" "path_corner" "origin" "712 1416 928" "targetname" "t110" "target" "t109" "spawnflags" "256" } { "classname" "func_door" "angle" "-2" "wait" "-1" "lip" "-24" "targetname" "t26" "spawnflags" "2304" "model" "*79" } { "classname" "monster_ogre" "origin" "240 2048 944" "angle" "0" "spawnflags" "257" "target" "t111" } { "target" "t23" "spawnflags" "1792" "classname" "trigger_once" "model" "*80" } { "classname" "monster_knight" "origin" "576 2768 536" "angle" "0" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "824 2776 536" "classname" "monster_knight" } { "classname" "monster_wizard" "origin" "704 -1032 1024" "angle" "90" "spawnflags" "256" "targetname" "t52" } { "angle" "90" "origin" "760 -1032 1024" "classname" "monster_wizard" "spawnflags" "768" "targetname" "t114" } { "classname" "monster_wizard" "origin" "816 -1032 1024" "angle" "90" "spawnflags" "768" "targetname" "t114" } { "targetname" "t52" "classname" "trigger_teleport" "spawnflags" "258" "target" "t112" "model" "*81" } { "classname" "trigger_teleport" "spawnflags" "770" "target" "t113" "targetname" "t114" "model" "*82" } { "targetname" "t114" "classname" "trigger_teleport" "spawnflags" "770" "target" "t115" "model" "*83" } { "classname" "info_teleport_destination" "origin" "896 224 1352" "angle" "135" "spawnflags" "256" "targetname" "t112" } { "classname" "info_teleport_destination" "origin" "488 1648 1016" "angle" "315" "spawnflags" "768" "targetname" "t113" } { "classname" "trigger_once" "spawnflags" "768" "target" "t114" "model" "*84" } { "classname" "info_teleport_destination" "origin" "800 904 928" "angle" "90" "spawnflags" "768" "targetname" "t115" } { "angle" "270" "origin" "-256 2232 1242" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "-256 2096 1218" "classname" "weapon_supershotgun" } { "angle" "270" "origin" "704 424 1266" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 2488 946" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1968 546" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "704 104 898" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1568 938" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "704 1344 912" "classname" "weapon_rocketlauncher" } { "angle" "0" "origin" "712 -576 840" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "944 -576 816" "classname" "weapon_nailgun" } { "angle" "180" "origin" "1064 -576 1128" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "696 584 1256" "classname" "weapon_grenadelauncher" } { "classname" "light" "origin" "316 804 780" "light" "150" } { "classname" "light" "origin" "316 804 644" "light" "75" } { "classname" "item_rockets" "origin" "298 710 706" "spawnflags" "1" } { "classname" "item_shells" "origin" "988 -928 1104" "spawnflags" "1" } { "classname" "item_health" "origin" "-56 2112 1220" "spawnflags" "3584" } { "classname" "item_health" "origin" "-56 2072 1220" "spawnflags" "2305" } { "spawnflags" "1" "origin" "584 2416 512" "classname" "item_spikes" } { "origin" "688 1392 516" "classname" "item_spikes" } { "classname" "item_artifact_invulnerability" "origin" "712 2312 948" "spawnflags" "1792" } { "origin" "32 1392 916" "classname" "item_artifact_envirosuit" } { "mangle" "26 310 0" "origin" "384 488 1552" "classname" "info_intermission" } { "light" "100" "origin" "800 1160 464" "classname" "light" } { "classname" "light" "origin" "608 1160 464" "light" "100" } { "light" "100" "origin" "604 1472 464" "classname" "light" } { "target" "t40" "classname" "trigger_teleport" "model" "*85" } { "mangle" "-20 75 0" "origin" "456 2144 568" "classname" "info_intermission" } { "mangle" "10 80 0" "origin" "464 1032 1000" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "1080 -752 1008" "classname" "info_intermission" } { "classname" "trigger_secret" "model" "*86" } { "classname" "path_corner" "origin" "1632 216 896" "targetname" "t116" "target" "t84" } { "classname" "item_spikes" "origin" "1200 48 872" "spawnflags" "1" } { "classname" "item_spikes" "origin" "928 2664 320" "spawnflags" "1" } { "classname" "item_shells" "origin" "1240 768 1384" "spawnflags" "1" } { "classname" "item_shells" "origin" "-88 2160 1224" "spawnflags" "2049" } { "classname" "info_player_coop" "origin" "-208 2272 1240" "angle" "270" } { "angle" "270" "origin" "-304 2272 1240" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-352 2272 1240" "angle" "270" } { "light" "200" "origin" "1288 1648 956" "classname" "light" } { "classname" "item_spikes" "origin" "-264 1464 888" } { "spawnflags" "1792" "origin" "1296 1488 888" "classname" "item_artifact_invisibility" } { "spawnflags" "1792" "classname" "func_wall" "model" "*87" } { "classname" "func_wall" "spawnflags" "1792" "model" "*88" } { "target" "t118" "targetname" "t117" "origin" "-176 1640 888" "classname" "path_corner" } { "targetname" "t118" "target" "t117" "classname" "path_corner" "origin" "-320 1640 888" } { "target" "t117" "origin" "-256 1632 904" "classname" "monster_knight" "spawnflags" "1" } { "origin" "1376 1424 864" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*89" } { "target" "t120" "targetname" "t53" "classname" "trigger_once" "model" "*90" } { "target" "t120" "targetname" "t39" "classname" "trigger_once" "model" "*91" } { "classname" "light" "origin" "480 2568 568" "light" "125" } { "origin" "162 1482 976" "classname" "ambient_drip" } { "origin" "786 1010 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "778 1210 584" } { "origin" "594 1202 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "602 1010 584" } { "origin" "786 1514 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "794 1698 584" } { "origin" "618 1690 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "618 1522 584" } { "origin" "698 1362 584" "classname" "ambient_drip" } { "origin" "714 1970 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "898 2170 592" } { "origin" "938 2346 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "682 2298 592" } { "origin" "458 2306 592" "classname" "ambient_drip" } { "origin" "458 1690 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "322 1506 880" } { "origin" "338 1226 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "466 1090 880" } { "origin" "394 882 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "674 810 880" } { "origin" "914 818 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "922 1034 880" } { "origin" "1082 1266 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "994 1442 880" } { "origin" "898 1714 880" "classname" "ambient_drip" } { "origin" "706 1362 1080" "classname" "ambient_drip" } { "origin" "1194 1522 1032" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1314 1354 1032" } { "origin" "442 354 920" "classname" "ambient_swamp1" } { "origin" "978 314 920" "classname" "ambient_swamp2" } quakespasm-0.93.0/Misc/qs_pak/maps/e2m2.ent0000644000000000000000000006505312425501423017050 0ustar rootroot{ "message" "the Ogre Citadel" "sounds" "8" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" } { "origin" "160 -160 120" "classname" "light" } { "angle" "90" "origin" "-256 -1952 280" "classname" "info_player_start" } { "classname" "light" "origin" "160 -392 248" "light" "200" } { "classname" "light" "origin" "160 -648 184" } { "classname" "light" "origin" "-56 -392 248" "light" "200" } { "classname" "light" "origin" "376 -392 248" "light" "200" } { "classname" "light" "origin" "288 -416 -72" "light" "200" } { "classname" "light" "origin" "32 -416 -72" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "10 -270 148" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "314 -270 148" "light" "250" } { "classname" "light" "origin" "-264 -440 248" "light" "200" } { "classname" "light" "origin" "584 -440 248" "light" "200" } { "classname" "light" "origin" "544 -648 248" } { "light" "150" "origin" "648 -456 -184" "classname" "light" } { "origin" "376 -800 184" "classname" "light" } { "classname" "light" "origin" "-152 -752 184" "light" "250" } { "classname" "light" "origin" "-232 -592 184" "light" "250" } { "classname" "light" "origin" "160 112 184" } { "classname" "light" "origin" "160 352 120" } { "classname" "light" "origin" "160 216 -48" "light" "120" } { "classname" "light" "origin" "160 16 -48" "light" "120" } { "classname" "light" "origin" "160 544 144" "light" "225" } { "classname" "light" "origin" "480 576 88" } { "classname" "light" "origin" "480 448 88" } { "classname" "light" "origin" "480 576 168" "light" "250" } { "classname" "light" "origin" "480 448 168" "light" "250" } { "classname" "light" "origin" "160 896 312" "light" "350" } { "classname" "light" "origin" "288 896 312" "light" "100" } { "classname" "light" "origin" "32 896 312" "light" "100" } { "classname" "light" "origin" "160 1008 312" "light" "100" } { "classname" "light" "origin" "160 784 312" "light" "100" } { "classname" "light" "origin" "392 120 184" "light" "350" } { "classname" "light" "origin" "568 208 184" "light" "350" } { "classname" "light" "origin" "720 480 184" } { "classname" "light" "origin" "640 632 184" } { "classname" "light" "origin" "472 1152 56" } { "classname" "light" "origin" "512 896 152" } { "origin" "800 800 184" "classname" "light" } { "light" "200" "origin" "632 1264 -40" "classname" "light" } { "origin" "800 1032 184" "classname" "light" } { "origin" "760 1472 64" "classname" "light" } { "light" "200" "origin" "544 1416 56" "classname" "light" } { "origin" "672 1256 184" "classname" "light" "light" "200" } { "light" "200" "origin" "1024 1272 184" "classname" "light" } { "light" "200" "origin" "992 1440 184" "classname" "light" } { "origin" "1240 488 184" "classname" "light" } { "origin" "1280 136 176" "classname" "light" } { "origin" "160 1304 136" "classname" "light" } { "origin" "160 1648 256" "classname" "light" "light" "200" } { "origin" "240 1600 256" "classname" "light" "light" "200" } { "origin" "16 1616 168" "classname" "light" "light" "200" } { "light" "200" "origin" "-120 1304 40" "classname" "light" } { "origin" "-352 1144 16" "classname" "light" "light" "250" } { "origin" "-56 1096 64" "classname" "light" } { "light" "200" "origin" "-56 1152 280" "classname" "light" } { "light" "350" "origin" "-440 1144 200" "classname" "light" } { "light" "200" "origin" "-352 1336 -72" "classname" "light" } { "origin" "-488 368 56" "classname" "light" "light" "250" } { "light" "350" "origin" "-488 896 136" "classname" "light" } { "origin" "-216 896 184" "classname" "light" "light" "250" } { "origin" "-128 536 168" "classname" "light" "light" "200" } { "light" "150" "origin" "-104 480 32" "classname" "light" } { "light" "150" "origin" "-208 936 56" "classname" "light" } { "light" "150" "origin" "-208 736 32" "classname" "light" } { "origin" "-344 64 184" "classname" "light" } { "light" "350" "origin" "-648 384 184" "classname" "light" } { "light" "350" "origin" "-488 688 184" "classname" "light" } { "light" "150" "origin" "-680 496 -36" "classname" "light" } { "light" "350" "origin" "-600 1104 96" "classname" "light" } { "origin" "-824 896 168" "classname" "light" } { "origin" "-896 600 160" "classname" "light" } { "target" "t1" "classname" "trigger_teleport" "model" "*1" } { "spawnflags" "1792" "targetname" "t1" "origin" "-448 264 -56" "classname" "info_teleport_destination" "angle" "45" } { "light" "250" "origin" "-168 1320 160" "classname" "light" } { "origin" "-24 896 184" "classname" "light" } { "origin" "-104 896 184" "classname" "light" "light" "250" } { "spawnflags" "2" "origin" "-680 880 48" "classname" "item_health" } { "angle" "90" "origin" "-896 512 24" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "-184 560 128" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "160 1640 160" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "1272 112 80" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "176 -784 24" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "208 -160 0" "classname" "item_rockets" } { "origin" "64 464 0" "classname" "item_health" } { "origin" "120 464 0" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 840 64" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 952 64" "classname" "item_health" } { "spawnflags" "9" "origin" "568 880 0" "classname" "item_weapon" } { "origin" "456 1104 -88" "classname" "item_health" } { "spawnflags" "1" "origin" "664 1272 -88" "classname" "item_shells" } { "angle" "225" "origin" "888 1312 120" "classname" "info_player_deathmatch" } { "origin" "1328 168 56" "classname" "item_health" } { "origin" "1328 128 56" "classname" "item_health" } { "origin" "1328 208 56" "classname" "item_health" } { "spawnflags" "1" "origin" "816 1472 0" "classname" "item_shells" } { "light" "200" "origin" "-64 -120 -56" "classname" "light" } { "light" "200" "origin" "-296 -160 -56" "classname" "light" } { "light" "200" "origin" "-296 -208 -272" "classname" "light" } { "classname" "func_plat" "model" "*2" } { "spawnflags" "2" "origin" "-192 -176 -80" "classname" "item_health" } { "light" "350" "origin" "-816 128 184" "classname" "light" } { "light" "350" "origin" "-664 -80 184" "classname" "light" } { "light" "250" "origin" "-656 112 32" "classname" "light" } { "origin" "-352 208 56" "classname" "light" "light" "250" } { "origin" "-184 504 24" "classname" "light" "light" "250" } { "classname" "item_armor1" "origin" "400 -888 0" } { "spawnflags" "1792" "classname" "item_armorInv" "origin" "-104 448 104" } { "classname" "item_armor2" "origin" "-680 496 32" } { "classname" "weapon_grenadelauncher" "origin" "168 1608 136" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "1232 176 56" "spawnflags" "1792" } { "classname" "weapon_supernailgun" "origin" "-960 944 40" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "752 1464 0" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "160 120 24" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-224 792 104" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-640 120 -56" "spawnflags" "1792" } { "light" "200" "origin" "-70 -1126 212" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "186 -1134 148" } { "light" "200" "origin" "138 -966 100" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "-150 -1318 260" } { "light" "150" "origin" "184 -880 72" "classname" "light" } { "style" "6" "light" "200" "origin" "-46 -1742 340" "classname" "light_torch_small_walltorch" } { "style" "1" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-486 -1726 340" } { "origin" "-272 -1544 384" "classname" "light" "light" "200" } { "style" "6" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-414 -2006 340" } { "style" "1" "light" "200" "origin" "-134 -1958 340" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-472 -1840 328" "classname" "light" } { "classname" "light" "origin" "-72 -1592 328" "light" "150" } { "light" "150" "origin" "-320 -1424 264" "classname" "light" } { "light" "150" "origin" "-256 -1952 280" "classname" "light" } { "light" "200" "classname" "light" "origin" "-272 -1720 312" } { "origin" "-232 -1280 168" "classname" "path_corner" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-64 -1176 120" "targetname" "t6" "target" "t7" } { "origin" "152 -1104 56" "classname" "path_corner" "targetname" "t7" "target" "t8" } { "classname" "path_corner" "origin" "192 -952 8" "targetname" "t8" "target" "t9" } { "origin" "184 -808 8" "classname" "path_corner" "target" "t3" "targetname" "t9" } { "classname" "path_corner" "origin" "512 -776 8" "targetname" "t3" "target" "t10" } { "origin" "-200 -648 8" "classname" "path_corner" "targetname" "t4" "target" "t11" } { "classname" "monster_knight" "origin" "-360 -1616 232" "target" "t5" "angle" "90" } { "origin" "512 -648 8" "classname" "path_corner" "targetname" "t10" "target" "t4" } { "classname" "path_corner" "origin" "-200 -776 8" "targetname" "t11" "target" "t3" } { "classname" "monster_knight" "origin" "24 -632 24" "spawnflags" "256" "target" "t4" } { "classname" "monster_knight" "origin" "336 -752 24" "spawnflags" "256" "target" "t3" } { "classname" "monster_knight" "origin" "56 -712 24" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "160 -712 24" "classname" "monster_knight" } { "classname" "monster_knight" "origin" "264 -712 24" "angle" "270" "spawnflags" "768" } { "classname" "item_health" "origin" "-432 -1640 208" "spawnflags" "1" } { "classname" "item_shells" "origin" "-352 -592 0" } { "classname" "func_door" "angle" "90" "spawnflags" "1" "targetname" "t13" "wait" "-1" "sounds" "3" "dmg" "100" "model" "*3" } { "health" "1" "angle" "90" "classname" "func_button" "target" "t12" "wait" "-1" "sounds" "1" "model" "*4" } { "classname" "func_button" "angle" "90" "health" "1" "target" "t12" "wait" "-1" "sounds" "1" "model" "*5" } { "classname" "trigger_counter" "targetname" "t12" "count" "2" "target" "t13" "model" "*6" } { "classname" "monster_demon1" "origin" "160 -128 24" "angle" "270" "targetname" "t12" } { "classname" "light" "origin" "-8 -288 64" "light" "200" } { "light" "200" "origin" "328 -288 64" "classname" "light" } { "light" "200" "origin" "-296 -448 -248" "classname" "light" } { "classname" "light" "origin" "160 -368 -248" "light" "200" } { "light" "200" "origin" "616 -448 -248" "classname" "light" } { "classname" "light" "origin" "408 -456 -248" "light" "200" } { "light" "200" "origin" "-40 -424 -248" "classname" "light" } { "classname" "light" "origin" "-280 -448 32" "light" "200" } { "light" "200" "origin" "632 -424 32" "classname" "light" } { "classname" "light" "origin" "160 -480 8" "light" "150" } { "classname" "func_door" "angle" "180" "targetname" "t12" "sounds" "3" "speed" "200" "wait" "-1" "lip" "-2" "model" "*7" } { "classname" "func_door" "angle" "0" "speed" "200" "wait" "-1" "lip" "-2" "model" "*8" } { "classname" "light" "origin" "160 -304 112" "light" "170" } { "classname" "light" "origin" "160 640 104" "light" "200" } { "classname" "func_door" "angle" "-1" "targetname" "t14" "sounds" "1" "model" "*9" } { "classname" "trigger_multiple" "target" "t14" "wait" "10" "model" "*10" } { "targetname" "t28" "classname" "func_door" "angle" "-1" "wait" "-1" "sounds" "1" "speed" "200" "spawnflags" "2048" "model" "*11" } { "angle" "270" "classname" "func_button" "target" "t15" "wait" "-1" "lip" "2" "sounds" "1" "spawnflags" "2048" "model" "*12" } { "classname" "light" "origin" "-296 432 0" "light" "200" } { "light" "200" "origin" "-312 416 152" "classname" "light" } { "classname" "light" "origin" "-184 320 146" "light" "200" } { "classname" "item_key2" "origin" "-552 192 -40" "sounds" "1" "spawnflags" "2048" } { "classname" "item_spikes" "origin" "-680 88 -48" } { "classname" "func_door" "angle" "-2" "spawnflags" "33" "speed" "10" "sounds" "3" "wait" "-1" "targetname" "t16" "dmg" "100" "model" "*13" "lip" "7" // svdijk -- added to prevent z-fighting } { "sounds" "3" "classname" "func_door" "angle" "90" "spawnflags" "2056" "wait" "-1" "model" "*14" } { "spawnflags" "2056" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*15" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t16" "lip" "12" "model" "*16" } { "classname" "light" "origin" "888 1080 -104" "light" "160" } { "light" "160" "origin" "888 888 -104" "classname" "light" } { "classname" "light" "origin" "880 696 -104" "light" "160" } { "light" "160" "origin" "696 888 -104" "classname" "light" } { "classname" "light" "origin" "696 1080 -104" "light" "160" } { "light" "200" "origin" "696 672 -104" "classname" "light" } { "classname" "light" "origin" "568 512 -104" "light" "200" } { "light" "160" "origin" "840 504 -104" "classname" "light" } { "classname" "light" "origin" "832 328 -104" "light" "160" } { "classname" "light" "origin" "936 512 96" "light" "200" } { "classname" "light" "origin" "-248 1080 56" "light" "150" } { "classname" "light" "origin" "888 1264 176" "light" "170" } { "classname" "func_door" "angle" "-2" "targetname" "t17" "sounds" "1" "model" "*17" } { "classname" "trigger_multiple" "target" "t17" "wait" "5" "model" "*18" } { "classname" "light" "origin" "160 120 -24" "light" "160" } { "spawnflags" "256" "classname" "trigger_multiple" "target" "t18" "targetname" "t23" "model" "*19" } { "wait" "0.5" "classname" "trap_spikeshooter" "origin" "88 368 40" "angle" "0" "targetname" "t18" } { "classname" "func_wall" "spawnflags" "2048" "model" "*20" } { "classname" "monster_demon1" "origin" "-160 608 128" "targetname" "t19" } { "classname" "trigger_once" "target" "t19" "model" "*21" } { "classname" "light" "origin" "-528 512 -104" "light" "200" } { "light" "200" "origin" "-344 592 -104" "classname" "light" } { "classname" "light" "origin" "-336 760 -104" "light" "200" } { "light" "200" "origin" "-432 856 -104" "classname" "light" } { "classname" "monster_ogre" "origin" "-416 440 -40" "spawnflags" "1536" "target" "t20" } { "classname" "monster_shambler" "origin" "-272 296 -40" "spawnflags" "256" "target" "t20" } { "classname" "path_corner" "origin" "-328 272 -56" "targetname" "t20" "target" "t21" } { "origin" "-400 480 -56" "classname" "path_corner" "target" "t20" "targetname" "t21" } { "classname" "item_health" "origin" "-600 144 -64" } { "classname" "weapon_supershotgun" "origin" "440 512 0" "spawnflags" "2048" } { "classname" "light" "origin" "-120 176 184" "light" "250" } { "origin" "-96 0 184" "classname" "light" "light" "200" } { "classname" "light" "origin" "312 116 -48" "light" "200" } { "classname" "path_corner" "origin" "8 112 32" "target" "t25" "targetname" "t24" } { "origin" "312 112 32" "classname" "path_corner" "targetname" "t25" "target" "t24" } { "classname" "monster_ogre" "origin" "112 112 48" "target" "t24" "spawnflags" "256" } { "classname" "item_shells" "origin" "88 -160 0" "spawnflags" "1" } { "light" "250" "classname" "light" "origin" "-352 144 56" } { "classname" "monster_demon1" "origin" "-80 -440 -296" "spawnflags" "256" "target" "t26" } { "classname" "path_corner" "origin" "-216 -456 -312" "targetname" "t26" "target" "t27" } { "origin" "536 -448 -312" "classname" "path_corner" "target" "t26" "targetname" "t27" } { "light" "200" "origin" "320 232 -104" "classname" "light" } { "classname" "light" "origin" "312 0 -104" "light" "200" } { "light" "160" "origin" "-8 0 -104" "classname" "light" } { "classname" "light" "origin" "-8 232 -104" "light" "200" } { "light" "200" "origin" "-16 112 -104" "classname" "light" } { "classname" "light" "origin" "-248 -8 -104" "light" "200" } { "light" "200" "origin" "-472 120 -104" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "-1" "targetname" "t15" "sounds" "1" "spawnflags" "2048" "model" "*22" } { "classname" "func_door" "angle" "-2" "spawnflags" "2081" "targetname" "t28" "wait" "10" "speed" "200" "sounds" "1" "model" "*23" } { "classname" "trigger_once" "target" "t28" "model" "*24" } { "classname" "trigger_once" "target" "t12" "model" "*25" } { "classname" "monster_ogre" "origin" "160 1432 128" "angle" "270" } { "classname" "monster_ogre" "origin" "-216 784 128" "angle" "90" } { "classname" "info_teleport_destination" "origin" "-360 888 24" "spawnflags" "2048" "targetname" "t1" } { "classname" "monster_zombie" "origin" "-288 -232 -296" "angle" "270" } { "classname" "monster_zombie" "origin" "168 -8 -104" "angle" "90" } { "classname" "monster_ogre" "origin" "648 1232 -64" "angle" "180" } { "sounds" "1" "classname" "func_door" "angle" "-2" "targetname" "t29" "model" "*26" } { "classname" "monster_ogre" "origin" "520 752 24" "angle" "90" "spawnflags" "256" "targetname" "t29" } { "classname" "trigger_once" "target" "t29" "model" "*27" } { "classname" "weapon_nailgun" "origin" "152 1608 136" "spawnflags" "2048" } { "classname" "path_corner" "origin" "544 896 8" "targetname" "t30" "target" "t31" } { "origin" "56 896 72" "classname" "path_corner" "target" "t30" "targetname" "t31" } { "classname" "path_corner" "origin" "168 552 8" "targetname" "t32" "target" "t33" } { "origin" "168 1392 8" "classname" "path_corner" "target" "t32" "targetname" "t33" } { "classname" "monster_knight" "origin" "240 584 24" "target" "t32" } { "classname" "monster_knight" "origin" "504 960 24" "target" "t30" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-16 896 88" "angle" "0" } { "classname" "monster_knight" "origin" "256 1224 24" "angle" "225" "spawnflags" "256" } { "target" "t49" "origin" "-24 1064 16" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "45" "origin" "-184 1080 128" "classname" "monster_ogre" } { "angle" "45" "origin" "-256 1216 128" "classname" "monster_knight" } { "targetname" "t16" "angle" "270" "origin" "784 520 56" "classname" "monster_demon1" } { "classname" "path_corner" "origin" "-352 888 16" "targetname" "t34" "target" "t35" } { "origin" "-120 888 8" "classname" "path_corner" "target" "t34" "targetname" "t35" } { "classname" "monster_ogre" "origin" "-184 912 24" "target" "t34" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "-720 896 64" } { "classname" "monster_knight" "origin" "-344 760 24" "angle" "135" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-392 584 24" "angle" "90" "spawnflags" "256" } { "angle" "90" "classname" "monster_knight" "origin" "-528 528 24" "spawnflags" "768" } { "targetname" "t37" "target" "t36" "origin" "-896 968 48" "classname" "path_corner" } { "target" "t37" "targetname" "t36" "classname" "path_corner" "origin" "-896 568 48" } { "spawnflags" "256" "target" "t36" "origin" "-840 600 24" "classname" "monster_ogre" } { "style" "32" "targetname" "t15" "light" "200" "origin" "-56 304 152" "classname" "light" } { "dmg" "100" "speed" "200" "targetname" "t15" "target" "t38" "classname" "func_train" "model" "*28" } { "target" "t39" "targetname" "t40" "origin" "-216 280 104" "classname" "path_corner" } { "target" "t40" "targetname" "t38" "classname" "path_corner" "origin" "-15 280 104" // svdijk -- changed to prevent z-fighting (was "-16 280 104") } { "target" "t38" "wait" "-1" "targetname" "t39" "origin" "-15 280 104" // svdijk -- changed to prevent z-fighting (was "-16 280 104") "classname" "path_corner" } { "angle" "180" "origin" "680 1472 24" "classname" "monster_ogre" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "864 1448 24" "classname" "monster_knight" } { "target" "t42" "targetname" "t41" "origin" "1024 1264 104" "classname" "path_corner" } { "targetname" "t42" "target" "t41" "classname" "path_corner" "origin" "704 1264 104" } { "target" "t41" "origin" "1056 1320 120" "classname" "monster_knight" } { "spawnflags" "257" "origin" "552 1280 128" "classname" "monster_knight" } { "spawnflags" "1" "origin" "432 1280 128" "classname" "monster_knight" } { "spawnflags" "2048" "angle" "0" "wait" "-1" "sounds" "0" "health" "1" "target" "t28" "classname" "func_button" "model" "*29" } { "origin" "-72 296 104" "classname" "item_health" } { "classname" "item_health" "origin" "-64 504 104" } { "origin" "-312 192 -64" "classname" "item_health" } { "spawnflags" "1" "origin" "-80 384 -64" "classname" "item_spikes" } { "origin" "-224 976 104" "classname" "item_shells" } { "target" "t666" "classname" "trigger_multiple" "wait" "10" "model" "*30" } { "target" "t45" "targetname" "t44" "origin" "984 568 8" "classname" "path_corner" } { "target" "t46" "targetname" "t45" "classname" "path_corner" "origin" "976 464 8" } { "target" "t47" "targetname" "t46" "origin" "1224 448 40" "classname" "path_corner" } { "target" "t44" "targetname" "t43" "classname" "path_corner" "origin" "1272 520 40" } { "target" "t48" "targetname" "t47" "classname" "path_corner" "origin" "1224 144 64" } { "targetname" "t48" "target" "t43" "origin" "1344 120 64" "classname" "path_corner" } { "target" "t45" "origin" "968 520 24" "classname" "monster_ogre" } { "spawnflags" "256" "target" "t43" "origin" "1312 328 80" "classname" "monster_knight" } { "spawnflags" "768" "target" "t47" "origin" "1240 296 80" "classname" "monster_ogre" } { "origin" "-360 1064 -8" "classname" "weapon_grenadelauncher" } { "target" "t50" "targetname" "t49" "origin" "-16 1168 0" "classname" "path_corner" } { "targetname" "t50" "target" "t49" "classname" "path_corner" "origin" "-232 1168 0" } { "origin" "1448 -128 -56" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "270" "model" "*31" } { "wait" "-1" "targetname" "t52" "sounds" "3" "classname" "func_door" "angle" "90" "model" "*32" } { "classname" "light" "origin" "1984 -184 288" "light" "350" } { "classname" "light" "origin" "1760 -312 -184" "light" "200" } { "origin" "1832 -64 -184" "classname" "light" "light" "200" } { "light" "160" "origin" "1808 -296 -24" "classname" "light" } { "classname" "light" "origin" "1224 -8 -184" "light" "160" } { "light" "160" "origin" "1240 -208 -184" "classname" "light" } { "classname" "light" "origin" "1680 -104 -184" "light" "160" } { "light" "160" "origin" "1584 -304 -184" "classname" "light" } { "origin" "1592 -72 288" "classname" "light" } { "classname" "light" "origin" "1328 -232 288" } { "classname" "light" "origin" "1280 -16 327" "light" "250" } { "classname" "light" "origin" "1792 -152 88" "light" "160" } { "light" "160" "origin" "1632 -296 88" "classname" "light" } { "origin" "1696 -280 288" "classname" "light" } { "classname" "light" "origin" "1240 -224 -8" "light" "200" } { "classname" "func_wall" "spawnflags" "3072" "model" "*33" } { "classname" "func_wall" "spawnflags" "3072" "model" "*34" } { "classname" "func_wall" "spawnflags" "3072" "model" "*35" } { "classname" "func_wall" "spawnflags" "3072" "model" "*36" } { "classname" "func_wall" "spawnflags" "3072" "model" "*37" } { "classname" "monster_ogre" "origin" "1992 -192 200" "angle" "180" "spawnflags" "256" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t51" "model" "*38" } { "classname" "trigger_multiple" "target" "t51" "model" "*39" } { "classname" "func_plat" "model" "*40" } { "classname" "light" "origin" "1480 56 -168" "light" "160" } { "origin" "1432 280 -64" "classname" "light" "light" "160" } { "light" "160" "classname" "light" "origin" "1424 280 96" } { "light" "160" "origin" "1472 232 -168" "classname" "light" } { "classname" "monster_zombie" "origin" "1592 -24 160" "angle" "180" } { "classname" "monster_zombie" "origin" "1432 -304 112" "angle" "135" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1304 -288 112" "angle" "90" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1576 -216 136" "angle" "180" "spawnflags" "768" } { "classname" "monster_zombie" "origin" "1928 -80 200" "spawnflags" "768" "angle" "180" } { "angle" "180" "spawnflags" "768" "origin" "1928 -280 200" "classname" "monster_zombie" } { "classname" "trigger_changelevel" "map" "e2m3" "model" "*41" } { "classname" "light" "origin" "80 1544 40" "light" "160" } { "light" "160" "origin" "-112 1544 40" "classname" "light" } { "classname" "item_shells" "origin" "1056 552 8" } { "classname" "light" "origin" "-112 1672 40" "light" "160" } { "light" "160" "origin" "88 1680 40" "classname" "light" } { "classname" "item_health" "origin" "-168 1688 -8" "spawnflags" "1" } { "classname" "monster_knight" "origin" "-112 1616 16" "angle" "45" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1760 -208 -216" "angle" "180" } { "classname" "trigger_secret" "model" "*42" } { "classname" "trigger_secret" "model" "*43" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_multiple" "target" "t29" "model" "*45" } { "classname" "item_shells" "origin" "-144 -1728 288" "spawnflags" "768" } { "classname" "item_rockets" "origin" "2000 -304 176" "spawnflags" "1793" } { "classname" "item_rockets" "origin" "2000 -112 176" "spawnflags" "1793" } { "origin" "-62 -702 72" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "386 -702 72" } { "origin" "-246 -470 -264" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "554 -454 -264" } { "origin" "162 -430 -264" "classname" "ambient_swamp1" } { "spawnflags" "1" "origin" "-72 576 104" "classname" "item_shells" } { "spawnflags" "2048" "wait" "10" "target" "t16" "classname" "trigger_multiple" "model" "*46" } { "targetname" "t16" "sounds" "4" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*47" } { "origin" "-184 1480 168" "classname" "light" "light" "160" } { "classname" "light" "origin" "-224 1592 168" "light" "100" } { "target" "t52" "classname" "trigger_once" "model" "*48" } { "mangle" "20 30 0" "origin" "1224 -288 336" "classname" "info_intermission" } { "mangle" "20 180 0" "origin" "-352 760 240" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "480 -440 208" "classname" "info_intermission" } { "angle" "90" "origin" "-176 -1904 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-128 -1848 264" "angle" "90" } { "angle" "90" "origin" "-192 -1808 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-320 -1824 264" "angle" "90" } { "spawnflags" "1792" "classname" "func_wall" "model" "*49" } { "spawnflags" "1792" "origin" "200 -664 0" "classname" "weapon_lightning" } { "origin" "-184 1512 144" "classname" "item_artifact_super_damage" } { "classname" "item_cells" "origin" "240 -664 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "392 640 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "-168 456 104" "spawnflags" "1793" } { "classname" "func_door" "angle" "-1" "spawnflags" "1" "wait" "6" "speed" "1000" "sounds" "3" "targetname" "t15" "model" "*50" } { "classname" "weapon_grenadelauncher" "origin" "1312 280 56" "spawnflags" "3584" } { "sounds" "2" "wait" "5" "message" "Shoot the buttons..." "spawnflags" "3584" "classname" "trigger_multiple" "targetname" "t53" "model" "*51" } { "classname" "trigger_relay" "origin" "-72 -320 48" "targetname" "t13" "killtarget" "t53" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m2.ent0000644000000000000000000012050712403131422017035 0ustar rootroot{ "message" "Castle of the Damned" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" "sounds" "8" } { "angle" "270" "origin" "1496 1664 296" "classname" "info_player_start" } { "origin" "1432 672 336" "classname" "light" "light" "250" } { "light" "200" "origin" "1496 888 272" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "932 640 340" } { "classname" "light_torch_small_walltorch" "origin" "1104 812 340" } { "classname" "light" "origin" "1104 640 544" "light" "300" } { "light" "175" "origin" "1216 536 353" "classname" "light" } { "light" "250" "origin" "1816 328 448" "classname" "light" } { "light" "200" "origin" "1632 472 208" "classname" "light" } { "light" "200" "origin" "1792 -392 240" "classname" "light" } { "light" "200" "origin" "1452 -124 508" "classname" "light" } { "light" "150" "origin" "1196 -124 508" "classname" "light" } { "light" "150" "origin" "1044 -124 508" "classname" "light" } { "light" "200" "origin" "756 -124 508" "classname" "light" } { "light" "250" "origin" "744 336 145" "classname" "light" } { "light" "200" "origin" "1176 -912 672" "classname" "light" } { "origin" "1328 -544 552" "classname" "light" } { "classname" "light" "origin" "1528 -912 640" "light" "200" } { "light" "200" "classname" "light" "origin" "880 -648 672" } { "origin" "240 -264 392" "classname" "light" "light" "250" } { "classname" "light" "origin" "-352 -504 464" "light" "200" } { "origin" "-448 -608 804" "classname" "light" "light" "450" } { "light" "250" "origin" "776 -912 472" "classname" "light" } { "light" "300" "origin" "1630 -806 428" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "1528 -912 464" "classname" "light" } { "classname" "light" "origin" "1180 -484 560" } { "classname" "light" "origin" "1184 -612 560" } { "classname" "light" "origin" "1016 -368 472" "light" "100" } { "classname" "light" "origin" "1016 -464 472" "light" "100" } { "classname" "light" "origin" "1020 -560 472" "light" "100" } { "classname" "light" "origin" "1208 -776 472" "light" "100" } { "classname" "light" "origin" "1288 -776 472" "light" "100" } { "classname" "light" "origin" "1360 -776 472" "light" "100" } { "light" "200" "origin" "1792 120 376" "classname" "light" } { "origin" "1538 182 356" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "1640 80 360" "classname" "light" } { "light" "200" "origin" "1928 80 360" "classname" "light" } { "light" "250" "origin" "1792 296 208" "classname" "light" } { "light" "150" "origin" "1800 40 160" "classname" "light" } { "light" "200" "origin" "1776 -392 160" "classname" "light" } { "light" "200" "origin" "1304 -392 152" "classname" "light" } { "light" "250" "origin" "1632 112 136" "classname" "light" } { "light" "250" "origin" "1432 312 136" "classname" "light" } { "light" "200" "origin" "1136 -656 160" "classname" "light" } { "light" "200" "origin" "1136 -416 160" "classname" "light" } { "light" "250" "origin" "1448 -552 160" "classname" "light" } { "light" "200" "origin" "1920 440 136" "classname" "light" } { "light" "200" "origin" "968 88 177" "classname" "light" } { "light" "300" "origin" "1088 312 129" "classname" "light" } { "light" "150" "origin" "1376 168 129" "classname" "light" } { "light" "250" "origin" "112 -384 392" "classname" "light" } { "origin" "300 -1004 508" "classname" "light" } { "origin" "296 -812 505" "classname" "light" } { "origin" "300 -1204 505" "classname" "light" } { "light" "150" "origin" "470 -1006 468" "classname" "light_torch_small_walltorch" } { "light" "250" "origin" "984 -1216 496" "classname" "light" } { "light" "250" "origin" "888 -1128 552" "classname" "light" } { "light" "200" "origin" "800 -1216 592" "classname" "light" } { "light" "200" "origin" "664 -1216 592" "classname" "light" } { "light" "200" "origin" "584 -1136 592" "classname" "light" } { "light" "200" "origin" "584 -968 592" "classname" "light" } { "light" "250" "origin" "584 -744 592" "classname" "light" } { "light" "200" "origin" "528 -1144 464" "classname" "light" } { "light" "200" "origin" "528 -856 464" "classname" "light" } { "light" "200" "classname" "light" "origin" "1496 1544 440" } { "origin" "1384 1392 440" "classname" "light" "light" "250" } { "origin" "1496 1104 520" "classname" "light" } { "origin" "1608 1400 440" "classname" "light" "light" "250" } { "light" "250" "origin" "1240 1712 360" "classname" "light" } { "light" "250" "origin" "1744 1696 360" "classname" "light" } { "classname" "light" "origin" "1384 1136 440" "light" "250" } { "classname" "light" "origin" "1608 1144 440" "light" "250" } { "classname" "path_corner" "origin" "1168 736 296" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "992 744 296" "targetname" "t6" "target" "t7" } { "classname" "path_corner" "origin" "1000 544 296" "targetname" "t7" "target" "t34" } { "classname" "item_health" "origin" "960 704 288" } { "classname" "item_shells" "origin" "952 512 288" } { "classname" "path_corner" "origin" "1344 -128 304" "targetname" "t9" "target" "t8" } { "classname" "path_corner" "origin" "898 -128 304" "targetname" "t8" "target" "t9" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1018 -126 320" "angle" "0" "target" "t8" } { "classname" "item_health" "origin" "1344 -224 296" "spawnflags" "1" } { "classname" "item_health" "origin" "1400 -224 296" "spawnflags" "1" } { "origin" "1528 192 296" "classname" "item_shells" } { "classname" "path_corner" "origin" "1496 1040 184" "targetname" "t22" "target" "t23" } { "classname" "path_corner" "origin" "1496 840 248" "targetname" "t23" "target" "t33" } { "spawnflags" "1" "classname" "item_shells" "origin" "1056 -648 288" } { "classname" "item_health" "origin" "1184 -736 288" } { "spawnflags" "257" "classname" "monster_army" "origin" "1646 -698 360" "angle" "180" "targetname" "t89" } { "classname" "path_corner" "origin" "1400 640 272" "targetname" "t30" "target" "t79" } { "classname" "path_corner" "origin" "1496 752 232" "targetname" "t33" "target" "t77" } { "classname" "path_corner" "origin" "1192 560 296" "targetname" "t34" "target" "t80" } { "classname" "item_shells" "origin" "1616 1280 176" } { "classname" "item_health" "origin" "1056 -840 416" "spawnflags" "1" } { "classname" "item_health" "origin" "1104 -840 416" "spawnflags" "1" } { "spawnflags" "1" "classname" "monster_army" "origin" "262 -458 320" "angle" "0" "target" "t96" } { "spawnflags" "1024" "classname" "item_health" "origin" "136 -296 296" } { "classname" "path_corner" "origin" "-536 -704 472" "targetname" "t42" "target" "t41" } { "classname" "path_corner" "origin" "-576 -416 472" "targetname" "t41" "target" "t42" } { "classname" "monster_knight" "origin" "-578 -654 480" "target" "t41" "spawnflags" "1" } { "classname" "item_shells" "origin" "-368 -752 456" } { "classname" "item_health" "origin" "-16 -520 360" "spawnflags" "1" } { "classname" "item_health" "origin" "-16 -576 360" "spawnflags" "1" } { "classname" "light" "origin" "1848 -568 320" "light" "200" } { "classname" "light" "origin" "1760 -560 408" "light" "200" } { "classname" "light" "origin" "1624 -560 352" "light" "150" } { "targetname" "t43" "angle" "270" "origin" "800 368 312" "classname" "info_teleport_destination" } { "origin" "752 168 296" "classname" "item_health" } { "target" "t43" "classname" "trigger_teleport" "model" "*1" } { "origin" "1712 -568 256" "classname" "item_health" } { "classname" "monster_ogre" "origin" "1494 1134 208" "angle" "270" "target" "t22" } { "light" "300" "origin" "1856 1288 384" "classname" "light" } { "classname" "light" "origin" "1136 1288 384" } { "light" "200" "origin" "1920 328 380" "classname" "light" } { "target" "t122" "spawnflags" "2048" "sounds" "1" "classname" "item_key1" "origin" "880 -300 464" } { "light" "300" "origin" "648 -384 430" "classname" "light_flame_small_yellow" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1104 -224 406" } { "light" "250" "origin" "1456 -128 406" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "988 532 353" "light" "175" } { "light" "125" "origin" "1100 648 328" "classname" "light" } { "origin" "1616 936 310" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "1360 936 310" } { "origin" "1792 504 390" "classname" "light_flame_small_yellow" "light" "300" } { "origin" "1972 -252 332" "classname" "info_null" "targetname" "t47" } { "light" "800" "origin" "1992 -252 336" "classname" "light" "target" "t47" } { "classname" "info_null" "origin" "1948 -292 332" "targetname" "t48" } { "light" "800" "classname" "light" "origin" "1948 -312 336" "target" "t48" } { "origin" "880 -328 562" "classname" "light_flame_small_yellow" "light" "300" } { "classname" "light" "origin" "1056 -1288 504" } { "origin" "1184 -1288 504" "classname" "light" } { "classname" "light" "origin" "1312 -1288 504" } { "origin" "1440 -1288 504" "classname" "light" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t50" "model" "*2" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t50" "model" "*3" } { "classname" "trigger_once" "target" "t50" "model" "*4" } { "classname" "light" "origin" "1368 -1016 504" "light" "200" } { "light" "200" "origin" "1120 -1024 504" "classname" "light" } { "classname" "light" "origin" "1248 -1184 464" "light" "175" } { "classname" "light" "origin" "776 -480 480" "light" "225" } { "classname" "light" "origin" "1904 -144 168" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1706 -206 316" "light" "300" } { "light" "300" "origin" "2134 -34 316" "classname" "light_torch_small_walltorch" } { "origin" "1152 -296 422" "classname" "light_flame_small_yellow" "light" "250" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1152 -760 422" } { "origin" "1528 -556 478" "classname" "light_flame_small_yellow" "light" "250" } { "targetname" "t52" "origin" "1532 -552 328" "classname" "info_null" } { "origin" "1340 -544 384" "classname" "item_armor2" } { "sounds" "1" "targetname" "t53" "lip" "64" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*5" } { "sounds" "1" "targetname" "t53" "classname" "func_door" "angle" "-1" "wait" "-1" "lip" "64" "model" "*6" } { "targetname" "t53" "target" "t54" "classname" "trigger_teleport" "spawnflags" "2" "model" "*7" } { "targetname" "t54" "angle" "180" "origin" "1408 -688 449" "classname" "info_teleport_destination" } { "targetname" "t53" "target" "t57" "classname" "trigger_teleport" "spawnflags" "2" "model" "*8" } { "targetname" "t57" "angle" "180" "origin" "1408 -400 361" "classname" "info_teleport_destination" } { "spawnflags" "768" "targetname" "t53" "angle" "180" "origin" "1912 -856 217" "classname" "monster_wizard" } { "spawnflags" "768" "targetname" "t53" "classname" "monster_wizard" "origin" "1912 -936 217" "angle" "180" } { "targetname" "t50" "angle" "90" "origin" "1320 -1112 441" "classname" "monster_knight" } { "spawnflags" "256" "targetname" "t50" "angle" "0" "origin" "1056 -1144 441" "classname" "monster_knight" } { "sounds" "1" "targetname" "t61" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*9" } { "sounds" "3" "lip" "64" "spawnflags" "1" "targetname" "t58" "angle" "270" "wait" "-1" "classname" "func_door" "model" "*10" } { "sounds" "1" "wait" "-1" "angle" "270" "target" "t58" "classname" "func_button" "model" "*11" } { "target" "t61" "classname" "trigger_once" "model" "*12" } { "light" "225" "origin" "984 -480 480" "classname" "light" } { "light" "175" "origin" "880 -368 176" "classname" "light" } { "classname" "light" "origin" "880 -592 240" "light" "175" } { "light" "200" "origin" "880 -488 184" "classname" "light" } { "light" "150" "origin" "880 -304 472" "classname" "light" } { "classname" "light" "origin" "-96 308 864" "light" "850" } { "origin" "-32 -440 624" "classname" "light" } { "sounds" "1" "targetname" "t73" "wait" "-1" "lip" "196" "angle" "-1" "classname" "func_door" "model" "*13" } { "light" "300" "origin" "104 144 688" "classname" "light" } { "classname" "light" "origin" "-264 144 688" "light" "300" } { "sounds" "1" "targetname" "t73" "wait" "-1" "classname" "func_door" "angle" "-1" "lip" "196" "model" "*14" } { "classname" "light_flame_small_yellow" "origin" "-24 -232 414" "light" "250" } { "lip" "-2" "sounds" "3" "speed" "350" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*15" } { "target" "t63" "targetname" "t62" "origin" "-12 312 264" "classname" "path_corner" } { "target" "t64" "targetname" "t63" "origin" "-12 312 356" "classname" "path_corner" } { "wait" "-1" "target" "t66" "targetname" "t64" "classname" "path_corner" "origin" "-13 440 355" } { "sounds" "1" "targetname" "t71" "wait" "-1" "target" "t65" "angle" "-2" "classname" "func_button" "model" "*16" } { "target" "t64" "targetname" "t66" "origin" "-13 440 355" "classname" "path_corner" } { "light" "200" "origin" "-96 440 376" "classname" "light" } { "light" "150" "origin" "8 456 376" "classname" "light" } { "targetname" "t70" "target" "t67" "classname" "path_corner" "origin" "-220 312 264" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "-220 312 356" } { "wait" "-1" "target" "t69" "targetname" "t68" "origin" "-221 440 355" "classname" "path_corner" } { "target" "t68" "targetname" "t69" "classname" "path_corner" "origin" "-221 440 355" } { "classname" "light" "origin" "-200 456 376" "light" "150" } { "targetname" "t65" "target" "t62" "classname" "func_train" "speed" "50" "sounds" "1" "model" "*17" } { "light" "250" "origin" "-96 632 406" "classname" "light_flame_small_yellow" } { "targetname" "t72" "origin" "-96 288 304" "classname" "info_null" } { "light" "450" "target" "t72" "origin" "-96 288 368" "classname" "light" } { "target" "t70" "targetname" "t65" "speed" "50" "classname" "func_train" "sounds" "1" "model" "*18" } { "lip" "-2" "sounds" "0" "speed" "350" "classname" "func_door" "wait" "-1" "angle" "0" "model" "*19" } { "targetname" "t65" "delay" "4.7" "target" "t73" "classname" "trigger_once" "model" "*20" } { "targetname" "t73" "angle" "270" "origin" "-96 552 320" "classname" "monster_demon1" "spawnflags" "1024" } { "targetname" "t74" "angle" "90" "origin" "132 -192 476" "classname" "info_teleport_destination" } { "targetname" "t75" "classname" "info_teleport_destination" "origin" "-328 -196 476" "angle" "90" } { "target" "t75" "classname" "trigger_teleport" "spawnflags" "1" "model" "*21" } { "target" "t74" "classname" "trigger_teleport" "spawnflags" "1" "model" "*22" } { "light" "200" "origin" "-418 306 356" "classname" "light" } { "classname" "light" "origin" "260 308 356" "light" "200" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*23" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "0" "classname" "func_door" "model" "*24" } { "sounds" "0" "wait" "-1" "angle" "0" "targetname" "t73" "classname" "func_door" "model" "*25" } { "sounds" "0" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*26" } { "sounds" "3" "wait" "-1" "angle" "-2" "targetname" "t73" "classname" "func_door" "model" "*27" } { "classname" "light" "origin" "-96 24 360" "light" "100" } { "light" "100" "origin" "-96 -40 360" "classname" "light" } { "classname" "light" "origin" "-160 -568 624" } { "origin" "-160 -440 624" "classname" "light" } { "classname" "light" "origin" "-32 -568 624" } { "classname" "light" "origin" "-96 -88 484" "light" "150" } { "classname" "light" "origin" "-440 -408 804" "light" "450" } { "classname" "light" "origin" "600 -128 352" "light" "200" } { "classname" "light" "origin" "576 -608 504" "light" "250" } { "classname" "light" "origin" "384 -504 392" "light" "250" } { "classname" "light" "origin" "1264 240 295" "light" "250" } { "light" "250" "origin" "944 240 295" "classname" "light" } { "classname" "path_corner" "origin" "1480 704 264" "targetname" "t77" "target" "t78" } { "classname" "path_corner" "origin" "1448 656 264" "targetname" "t78" "target" "t30" } { "classname" "path_corner" "origin" "1264 640 304" "targetname" "t80" "target" "t5" } { "classname" "path_corner" "origin" "1328 640 304" "targetname" "t79" "target" "t80" } { "light" "200" "origin" "1488 -392 216" "classname" "light" } { "classname" "path_corner" "origin" "816 80 304" "targetname" "t83" "target" "t82" "spawnflags" "256" } { "origin" "816 312 304" "classname" "path_corner" "targetname" "t82" "target" "t83" "spawnflags" "256" } { "classname" "monster_army" "origin" "806 206 320" "angle" "90" "target" "t82" "spawnflags" "256" } { "classname" "trigger_once" "target" "t84" "model" "*28" } { "classname" "monster_ogre" "origin" "1790 -146 312" "angle" "90" "targetname" "t84" } { "classname" "path_corner" "origin" "1088 -672 296" "target" "t85" "targetname" "t88" } { "origin" "1088 -376 296" "classname" "path_corner" "targetname" "t85" "target" "t86" } { "classname" "path_corner" "origin" "1088 -376 296" "targetname" "t87" "target" "t88" } { "origin" "1448 -376 296" "classname" "path_corner" "targetname" "t86" "target" "t87" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1086 -498 312" "angle" "270" "target" "t88" } { "spawnflags" "256" "classname" "trigger_once" "target" "t89" "model" "*29" } { "classname" "item_health" "origin" "352 -752 408" "spawnflags" "1025" } { "spawnflags" "1025" "origin" "352 -792 408" "classname" "item_health" } { "classname" "item_health" "origin" "352 -832 408" "spawnflags" "1" } { "classname" "path_corner" "origin" "408 -776 416" "targetname" "t94" "target" "t95" } { "origin" "400 -1088 416" "classname" "path_corner" "targetname" "t95" "target" "t94" } { "classname" "path_corner" "origin" "584 -1096 416" "targetname" "t92" "target" "t93" } { "origin" "584 -792 416" "classname" "path_corner" "targetname" "t93" "target" "t92" } { "classname" "monster_army" "origin" "390 -970 432" "angle" "0" "target" "t94" } { "classname" "monster_army" "origin" "566 -970 432" "angle" "270" "target" "t92" } { "classname" "path_corner" "origin" "208 -304 304" "targetname" "t97" "target" "t96" } { "classname" "path_corner" "origin" "208 -464 304" "targetname" "t96" "target" "t97" } { "spawnflags" "1280" "classname" "path_corner" "origin" "-344 160 304" "targetname" "t100" "target" "t99" } { "spawnflags" "1280" "origin" "168 152 304" "classname" "path_corner" "targetname" "t99" "target" "t100" } { "spawnflags" "1280" "classname" "monster_ogre" "origin" "240 152 320" "angle" "180" "target" "t99" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "-392 80 320" "angle" "0" "targetname" "t101" } { "spawnflags" "768" "classname" "trigger_once" "target" "t101" "model" "*30" } { "classname" "item_health" "origin" "40 -16 464" } { "origin" "80 -48 464" "classname" "item_health" } { "origin" "520 -72 296" "classname" "item_shells" } { "spawnflags" "1" "origin" "-424 -216 296" "classname" "item_shells" } { "spawnflags" "769" "angle" "270" "origin" "880 -400 568" "classname" "monster_wizard" } { "light" "200" "origin" "432 176 152" "classname" "light" } { "light" "150" "origin" "432 -56 256" "classname" "light" } { "origin" "264 -96 300" "classname" "item_health" } { "classname" "item_health" "origin" "264 -140 300" } { "spawnflags" "1" "origin" "1184 1568 240" "classname" "item_health" } { "classname" "item_health" "origin" "1184 1616 240" "spawnflags" "1" } { "light" "150" "origin" "1496 1112 108" "classname" "light" } { "light" "200" "origin" "1120 1152 96" "classname" "light" } { "light" "200" "origin" "1080 692 184" "classname" "light" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "832 1184 294" } { "origin" "464 536 358" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "600 704 334" } { "light" "150" "origin" "1736 1096 110" "classname" "light" } { "light" "100" "origin" "832 1056 134" "classname" "light" } { "light" "150" "origin" "784 704 294" "classname" "light" } { "origin" "856 592 182" "classname" "item_health" } { "classname" "item_health" "origin" "824 552 182" } { "classname" "info_player_deathmatch" "origin" "-416 -144 320" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "168 -480 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1496 1328 200" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "1936 -136 312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "936 -1216 432" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "792 -992 440" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1080 -720 312" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "408 -752 432" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "792 -208 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "784 808 206" "angle" "225" } { "sounds" "3" "wait" "3" "angle" "90" "classname" "func_door" "model" "*31" } { "sounds" "0" "wait" "3" "angle" "270" "classname" "func_door" "model" "*32" } { "spawnflags" "1" "origin" "680 832 182" "classname" "item_shells" } { "origin" "1392 240 300" "classname" "weapon_supershotgun" } { "spawnflags" "769" "angle" "270" "origin" "954 -754 444" "classname" "monster_ogre" } { "spawnflags" "1" "origin" "520 -1280 408" "classname" "item_shells" } { "light" "200" "origin" "-612 -500 548" "classname" "light" } { "classname" "func_door" "angle" "91" // svdijk -- changed to prevent z-fighting (was "90") "targetname" "t110" "wait" "-1" "model" "*33" } { "sounds" "3" "classname" "func_door" "angle" "269" // svdijk -- changed to prevent z-fighting (was "270") "wait" "-1" "model" "*34" } { "classname" "trigger_once" "target" "t110" "model" "*35" } { "classname" "trigger_changelevel" "map" "e1m3" "model" "*36" } { "spawnflags" "1792" "origin" "680 728 184" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1496 1256 176" "classname" "weapon_nailgun" } { "angle" "180" "spawnflags" "1792" "origin" "-96 -496 360" "classname" "weapon_supernailgun" } { "spawnflags" "1794" "origin" "-112 -8 464" "classname" "item_health" } { "spawnflags" "1793" "origin" "-112 -568 360" "classname" "item_spikes" } { "spawnflags" "1792" "origin" "1616 1424 176" "classname" "item_spikes" } { "spawnflags" "1792" "classname" "item_spikes" "origin" "1656 1424 176" } { "spawnflags" "1792" "origin" "1696 1424 176" "classname" "item_spikes" } { "spawnflags" "768" "target" "t34" "angle" "315" "origin" "1070 646 312" "classname" "monster_ogre" } { "spawnflags" "768" "targetname" "t84" "angle" "90" "origin" "1624 88 376" "classname" "monster_wizard" } { "spawnflags" "768" "angle" "90" "targetname" "t84" "origin" "1866 -378 312" "classname" "monster_ogre" } { "angle" "45" "origin" "1088 -1096 440" "classname" "monster_knight" "targetname" "t50" } { "spawnflags" "768" "classname" "monster_knight" "origin" "1400 -1144 440" "angle" "90" "targetname" "t50" } { "spawnflags" "256" "target" "t111" "targetname" "t112" "origin" "896 -1216 416" "classname" "path_corner" } { "spawnflags" "256" "target" "t112" "targetname" "t111" "classname" "path_corner" "origin" "704 -1216 416" } { "spawnflags" "257" "target" "t111" "angle" "180" "origin" "758 -1218 432" "classname" "monster_army" } { "spawnflags" "768" "target" "t114" "targetname" "t113" "origin" "-96 -520 368" "classname" "path_corner" } { "spawnflags" "768" "target" "t113" "targetname" "t114" "origin" "-96 -152 304" "classname" "path_corner" } { "targetname" "t116" "spawnflags" "769" "target" "t113" "angle" "270" "origin" "-98 -194 320" "classname" "monster_ogre" } { "spawnflags" "1536" "origin" "1936 -96 289" "classname" "item_health" } { "spawnflags" "1025" "origin" "1040 -1200 417" "classname" "item_health" } { "spawnflags" "769" "target" "t117" "angle" "315" "origin" "-560 -312 592" "classname" "monster_wizard" } { "spawnflags" "768" "target" "t118" "targetname" "t117" "origin" "-528 -344 576" "classname" "path_corner" } { "spawnflags" "768" "target" "t117" "targetname" "t118" "origin" "-352 -656 576" "classname" "path_corner" } { "classname" "light" "origin" "1360 976 224" "light" "150" } { "light" "150" "origin" "1616 976 224" "classname" "light" } { "classname" "light" "origin" "1208 1296 368" "light" "250" } { "origin" "1784 1288 368" "classname" "light" "light" "250" } { "classname" "light" "origin" "1496 1664 336" "light" "250" } { "classname" "light" "origin" "1752 1176 112" "light" "150" } { "light" "200" "origin" "1776 976 112" "classname" "light" } { "classname" "light" "origin" "1216 976 112" "light" "200" } { "light" "150" "origin" "1224 1176 112" "classname" "light" } { "classname" "light" "origin" "1496 1432 520" "light" "250" } { "classname" "light" "origin" "1496 1304 264" "light" "200" } { "classname" "light" "origin" "1496 1432 288" "light" "200" } { "classname" "light" "origin" "1608 1120 88" "light" "150" } { "light" "150" "origin" "1384 1120 88" "classname" "light" } { "classname" "light" "origin" "1496 864 368" "light" "150" } { "light" "175" "origin" "980 764 353" "classname" "light" } { "classname" "light" "origin" "1228 764 353" "light" "175" } { "classname" "light" "origin" "1104 464 353" "light" "200" } { "classname" "light" "origin" "1104 -40 423" "light" "200" } { "light" "150" "origin" "1416 -128 367" "classname" "light" } { "classname" "light" "origin" "1104 -184 367" "light" "200" } { "classname" "light" "origin" "1184 56 423" "light" "150" } { "light" "150" "origin" "1024 56 423" "classname" "light" } { "classname" "light" "origin" "1272 -64 399" "light" "150" } { "light" "150" "origin" "888 -64 399" "classname" "light" } { "classname" "light" "origin" "1104 152 129" "light" "300" } { "classname" "light" "origin" "976 392 129" "light" "200" } { "classname" "light" "origin" "1104 656 120" } { "classname" "light" "origin" "896 712 144" "light" "200" } { "classname" "light" "origin" "640 704 280" "light" "200" } { "classname" "light" "origin" "464 496 296" "light" "150" } { "classname" "light" "origin" "888 1152 96" "light" "200" } { "classname" "light" "origin" "840 880 240" "light" "200" } { "classname" "light" "origin" "848 584 240" "light" "150" } { "classname" "light" "origin" "784 160 144" "light" "200" } { "classname" "light" "origin" "440 336 144" "light" "150" } { "light" "150" "origin" "584 336 144" "classname" "light" } { "classname" "light" "origin" "432 24 136" "light" "150" } { "classname" "light" "origin" "656 328 224" "light" "200" } { "classname" "light" "origin" "432 -128 312" "light" "200" } { "classname" "light" "origin" "600 -384 360" "light" "200" } { "origin" "520 -128 406" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light" "origin" "424 -320 352" "light" "200" } { "classname" "light" "origin" "664 -1216 472" "light" "150" } { "classname" "light" "origin" "336 -1208 504" "light" "150" } { "light" "150" "origin" "336 -1008 504" "classname" "light" } { "classname" "light" "origin" "336 -816 504" "light" "150" } { "classname" "light" "origin" "880 -1000 496" "light" "200" } { "classname" "light" "origin" "880 -792 496" "light" "200" } { "classname" "light" "origin" "880 -376 304" "light" "200" } { "classname" "light" "origin" "1048 -912 480" "light" "225" } { "classname" "light" "origin" "1120 -1192 468" "light" "150" } { "light" "150" "origin" "1376 -1192 468" "classname" "light" } { "classname" "light" "origin" "1472 -912 464" "light" "175" } { "classname" "light" "origin" "880 -304 480" "light" "100" } { "classname" "light" "origin" "880 -680 480" "light" "175" } { "classname" "light" "origin" "1600 -704 484" "light" "150" } { "classname" "light" "origin" "1504 -704 348" "light" "175" } { "light" "175" "origin" "1336 -704 348" "classname" "light" } { "classname" "light" "origin" "1152 -640 332" "light" "200" } { "classname" "light" "origin" "1096 -552 348" "light" "150" } { "light" "200" "origin" "1160 -456 332" "classname" "light" } { "light" "150" "origin" "1216 -384 348" "classname" "light" } { "classname" "light" "origin" "1344 -384 348" "light" "150" } { "classname" "light" "origin" "1544 392 156" "light" "225" } { "light" "225" "origin" "1848 248 156" "classname" "light" } { "classname" "light" "origin" "1936 136 156" "light" "225" } { "light" "200" "origin" "2096 -80 156" "classname" "light" } { "light" "200" "origin" "2048 -408 156" "classname" "light" } { "classname" "light" "origin" "1456 -392 444" "light" "225" } { "classname" "light" "origin" "1640 -384 352" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "2134 -474 316" "light" "250" } { "light" "200" "origin" "168 216 496" "classname" "light" } { "classname" "light" "origin" "-328 208 496" "light" "200" } { "light" "200" "origin" "-96 360 432" "classname" "light" } { "classname" "light" "origin" "-96 144 432" "light" "200" } { "light" "200" "origin" "-376 32 432" "classname" "light" } { "light" "200" "origin" "208 -72 432" "classname" "light" } { "light" "150" "origin" "-96 72 360" "classname" "light" } { "light" "150" "origin" "-64 -232 368" "classname" "light" } { "light" "150" "origin" "-96 -320 560" "classname" "light" } { "light" "250" "origin" "-96 -496 448" "classname" "light" } { "light" "150" "origin" "-416 -104 392" "classname" "light" } { "light" "150" "origin" "-344 -152 528" "classname" "light" } { "classname" "light" "origin" "160 -152 528" "light" "150" } { "light" "150" "origin" "-96 8 528" "classname" "light" } { "light" "200" "origin" "-560 -504 688" "classname" "light" } { "classname" "light" "origin" "-440 -368 688" "light" "200" } { "light" "200" "origin" "-440 -656 688" "classname" "light" } { "classname" "light" "origin" "-336 -504 688" "light" "200" } { "classname" "light" "origin" "2084 -208 336" "light" "100" } { "light" "100" "origin" "2012 -252 332" "classname" "light" } { "classname" "light" "origin" "1948 -328 332" "light" "100" } { "light" "150" "origin" "1892 -452 332" "classname" "light" } { "sounds" "1" "targetname" "t120" "wait" "-1" "angle" "-2" "classname" "func_door" "lip" "4" "model" "*37" } { "target" "t120" "classname" "trigger_once" "model" "*38" } { "light" "100" "origin" "2076 -312 336" "classname" "light" } { "sounds" "3" "spawnflags" "2064" "angle" "0" "wait" "-1" "classname" "func_door" "model" "*39" } { "spawnflags" "2064" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*40" } { "light" "100" "origin" "332 -264 356" "classname" "light" } { "classname" "light" "origin" "144 -264 356" "light" "100" } { "light" "100" "origin" "1104 572 316" "classname" "light" } { "target" "t121" "wait" ".8" "classname" "trigger_multiple" "model" "*41" } { "targetname" "t121" "angle" "180" "origin" "2120 -256 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "targetname" "t121" "angle" "90" "origin" "1944 -456 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "light" "150" "origin" "1312 -856 472" "classname" "light" } { "classname" "light" "origin" "1184 -856 472" "light" "175" } { "classname" "light" "origin" "1560 -568 224" "light" "200" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "50" "sounds" "1" "targetname" "t123" "lip" "6" "model" "*42" } { "classname" "trigger_once" "target" "t123" "model" "*43" } { "classname" "light" "origin" "1496 -552 330" "light" "700" "target" "t52" } { "classname" "light" "origin" "1288 80 140" "light" "250" } { "classname" "light" "origin" "1288 400 80" "light" "200" } { "classname" "light" "origin" "1328 -664 160" "light" "200" } { "classname" "item_armor1" "origin" "784 56 304" } { "classname" "light" "origin" "1544 464 352" "light" "75" } { "classname" "func_plat" "model" "*44" } { "classname" "light" "origin" "1496 1192 280" "light" "200" } { "classname" "light" "origin" "1608 1192 136" "light" "100" } { "light" "100" "origin" "1384 1184 136" "classname" "light" } { "light" "100" "origin" "1608 1048 136" "classname" "light" } { "classname" "light" "origin" "1384 1048 136" "light" "100" } { "classname" "light" "origin" "1200 1148 92" "light" "150" } { "light" "150" "origin" "876 -184 367" "classname" "light" } { "classname" "light" "origin" "768 -128 384" "light" "200" } { "classname" "light" "origin" "1104 388 552" "light" "250" } { "light" "200" "origin" "1392 240 384" "classname" "light" } { "classname" "light" "origin" "1392 80 368" "light" "200" } { "classname" "light" "origin" "1272 400 367" "light" "150" } { "light" "150" "origin" "920 400 367" "classname" "light" } { "classname" "light" "origin" "816 208 368" "light" "200" } { "classname" "light" "origin" "800 24 368" "light" "200" } { "classname" "light" "origin" "800 376 385" "light" "150" } { "light" "150" "origin" "1400 400 385" "classname" "light" } { "classname" "path_corner" "origin" "1104 336 300" "target" "t126" "targetname" "t127" } { "origin" "1104 24 300" "classname" "path_corner" "targetname" "t126" "target" "t127" } { "classname" "monster_army" "origin" "1104 424 316" "angle" "270" "target" "t127" } { "targetname" "t128" "origin" "1392 240 308" "classname" "info_null" } { "light" "300" "target" "t128" "origin" "1392 240 376" "classname" "light" } { "targetname" "t129" "angle" "0" "origin" "552 -128 320" "classname" "monster_army" } { "target" "t129" "classname" "trigger_once" "model" "*45" } { "sounds" "1" "classname" "trigger_secret" "model" "*46" } { "sounds" "1" "classname" "trigger_secret" "model" "*47" } { "classname" "light" "origin" "1104 24 536" "light" "350" } { "spawnflags" "1" "classname" "func_door_secret" "angle" "270" "model" "*48" } { "light" "200" "origin" "1680 1552 320" "classname" "light" } { "classname" "light" "origin" "1312 1552 320" "light" "200" } { "classname" "item_spikes" "origin" "1480 1104 68" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1760 -568 256" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1232 -1200 416" } { "message" "This door is opened elsewhere..." "classname" "func_door" "sounds" "3" "angle" "180" "wait" "-1" "targetname" "t122" "speed" "35" "spawnflags" "2048" "model" "*49" } { "classname" "func_door" "angle" "0" "wait" "-1" "speed" "30" "spawnflags" "2048" "model" "*50" } { "classname" "light" "origin" "1496 1600 296" "light" "150 " } { "light" "150" "origin" "1568 1664 296" "classname" "light" } { "classname" "light" "origin" "1424 1664 296" "light" "150" } { "classname" "light" "origin" "1328 1424 296" "light" "200" } { "light" "250" "origin" "1696 1416 296" "classname" "light" } { "classname" "monster_army" "origin" "1592 1296 200" "angle" "270" } { "spawnflags" "768" "classname" "monster_demon1" "origin" "-96 576 320" "angle" "270" "targetname" "t73" "target" "t143" } { "classname" "path_corner" "origin" "1392 416 304" "targetname" "t131" "target" "t130" "spawnflags" "768" } { "origin" "1392 296 304" "classname" "path_corner" "targetname" "t130" "target" "t131" "spawnflags" "768" } { "classname" "monster_army" "origin" "1392 352 320" "angle" "270" "target" "t130" "spawnflags" "768" } { "target" "t132" "targetname" "t133" "origin" "296 -328 304" "classname" "path_corner" } { "target" "t133" "targetname" "t132" "classname" "path_corner" "origin" "472 -416 304" } { "spawnflags" "1" "target" "t132" "angle" "90" "origin" "472 -456 320" "classname" "monster_army" } { "spawnflags" "1" "targetname" "t89" "angle" "135" "origin" "1712 -784 376" "classname" "monster_army" } { "target" "t135" "spawnflags" "256" "targetname" "t134" "origin" "400 -1128 416" "classname" "path_corner" } { "target" "t134" "spawnflags" "256" "targetname" "t135" "classname" "path_corner" "origin" "400 -1248 416" } { "target" "t134" "spawnflags" "257" "angle" "90" "origin" "408 -1208 432" "classname" "monster_army" } { "targetname" "t101" "angle" "90" "origin" "-288 -24 488" "classname" "monster_army" "spawnflags" "768" } { "spawnflags" "768" "targetname" "t101" "classname" "monster_army" "origin" "136 -128 488" "angle" "90" } { "spawnflags" "1792" "origin" "-264 -24 464" "classname" "item_rockets" } { "spawnflags" "2048" "origin" "-240 -8 464" "classname" "item_spikes" } { "classname" "monster_ogre" "origin" "-304 -304 488" "angle" "225" "spawnflags" "769" } { "classname" "func_wall" "spawnflags" "768" "model" "*51" } { "classname" "trap_spikeshooter" "origin" "2048 -48 332" "angle" "270" "spawnflags" "769" "targetname" "t121" } { "origin" "2048 -476 332" "classname" "info_null" "targetname" "t136" } { "style" "32" "origin" "2048 -456 336" "classname" "light" "light" "800" "spawnflags" "1" "target" "t136" "targetname" "t137" } { "style" "32" "classname" "trigger_once" "spawnflags" "768" "target" "t137" "model" "*52" } { "classname" "monster_wizard" "origin" "672 328 384" "angle" "180" "spawnflags" "768" "targetname" "t138" } { "classname" "trigger_once" "target" "t138" "model" "*53" } { "style" "32" "classname" "light" "origin" "2004 -52 332" "light" "100" "spawnflags" "1" "targetname" "t137" } { "classname" "item_shells" "origin" "1416 224 300" "spawnflags" "768" } { "classname" "path_corner" "origin" "-344 136 304" "targetname" "t139" "target" "t140" "spawnflags" "768" } { "origin" "168 128 304" "classname" "path_corner" "target" "t139" "targetname" "t140" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "-400 168 320" "spawnflags" "768" "target" "t139" } { "classname" "trap_spikeshooter" "origin" "2120 -256 332" "angle" "180" "spawnflags" "769" "targetname" "t121" } { "classname" "trap_spikeshooter" "origin" "1944 -456 332" "targetname" "t121" "angle" "90" "spawnflags" "769" } { "classname" "item_spikes" "origin" "-336 -80 470" "spawnflags" "768" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t142" "spawnflags" "2" "model" "*54" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t141" "spawnflags" "2" "model" "*55" } { "classname" "monster_demon1" "origin" "32 840 359" "angle" "270" "targetname" "t143" "spawnflags" "768" } { "angle" "270" "origin" "-192 840 359" "classname" "monster_demon1" "targetname" "t143" "spawnflags" "768" } { "classname" "info_teleport_destination" "origin" "80 216 303" "angle" "270" "targetname" "t141" } { "angle" "270" "origin" "-264 224 303" "classname" "info_teleport_destination" "targetname" "t142" } { "wait" "-1" "target" "t53" "health" "1" "classname" "func_button" "model" "*56" } { "spawnflags" "768" "angle" "270" "origin" "1408 1296 200" "classname" "monster_army" } { "classname" "item_shells" "origin" "772 -856 420" "spawnflags" "768" } { "spawnflags" "1792" "origin" "1248 -1128 420" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "864 -312 440" "classname" "item_rockets" } { "classname" "trigger_once" "message" "Pass through the arch to exit..." "model" "*57" } { "mangle" "20 300 0" "classname" "info_intermission" "origin" "-224 424 512" } { "mangle" "20 45 0" "origin" "1048 -744 488" "classname" "info_intermission" } { "mangle" "20 270 0" "origin" "1104 424 528" "classname" "info_intermission" } { "mangle" "20 45 0" "origin" "1240 984 416" "classname" "info_intermission" } { "sounds" "1" "speed" "20" "classname" "func_button" "angle" "0" "wait" "-1" "target" "t144" "model" "*58" } { "classname" "light" "origin" "400 -1392 480" "light" "150" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "20" "sounds" "1" "targetname" "t144" "model" "*59" } { "classname" "trigger_secret" "model" "*60" } { "classname" "item_artifact_super_damage" "origin" "400 -1360 432" } { "classname" "item_spikes" "origin" "808 -632 192" "spawnflags" "2049" } { "classname" "item_health" "origin" "924 -632 192" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "880 -616 192" "spawnflags" "1792" } { "classname" "ambient_drip" "origin" "842 978 344" } { "classname" "ambient_drip" "origin" "546 330 400" } { "classname" "info_player_coop" "origin" "1608 1664 264" "angle" "270" } { "angle" "270" "origin" "1392 1664 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1496 1560 264" "angle" "270" } { "spawnflags" "256" "angle" "270" "origin" "232 -176 320" "classname" "monster_ogre" } { "spawnflags" "1280" "angle" "225" "origin" "-368 -312 480" "classname" "monster_knight" } { "spawnflags" "1792" "classname" "func_wall" "model" "*61" } { "origin" "1810 274 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1802 -102 200" } { "origin" "2050 -214 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "2002 -390 200" } { "origin" "1738 -398 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1346 -398 200" } { "origin" "1138 -542 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "882 -494 200" } { "classname" "ambient_swamp1" "origin" "1722 1090 176" } { "origin" "1242 1090 176" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "1106 642 192" } { "origin" "1346 242 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "866 210 192" } { "classname" "ambient_swamp1" "origin" "1802 90 192" } { "origin" "1546 -398 192" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "2042 -310 192" } { "origin" "1178 -398 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp2" "origin" "1202 -678 192" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m1.ent0000644000000000000000000006333612403131422017042 0ustar rootroot{ "worldtype" "2" "sounds" "6" "classname" "worldspawn" "wad" "gfx/base.wad" "message" "the Slipgate Complex" } { "classname" "info_player_start" "origin" "480 -352 88" "angle" "90" } { "classname" "light" "origin" "480 96 168" "light" "250" } { "classname" "light" "origin" "480 288 168" "light" "250" } { "classname" "light" "origin" "272 96 80" } { "origin" "272 288 80" "classname" "light" } { "classname" "light" "origin" "272 192 80" } { "origin" "688 192 80" "classname" "light_fluorospark" "style" "10" } { "style" "10" "classname" "light" "origin" "688 288 80" } { "origin" "688 96 80" "classname" "light" "style" "10" } { "classname" "light" "origin" "480 -280 168" "light" "200" } { "origin" "480 -144 168" "classname" "light" "light" "200" } { "classname" "light" "origin" "480 -376 120" "light" "200" } { "light" "160" "origin" "480 -40 168" "classname" "light" } { "speed" "400" "sounds" "2" "angle" "270" "classname" "func_door" "model" "*1" } { "speed" "400" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "250" "origin" "592 544 88" "classname" "light_fluoro" } { "origin" "456 600 104" "classname" "light" } { "light" "180" "origin" "688 648 136" "classname" "light" } { "classname" "light" "origin" "688 520 136" "light" "180" } { "origin" "688 480 80" "classname" "item_armor1" } { "angle" "180" "spawnflags" "768" "origin" "616 72 40" "classname" "monster_army" } { "light" "250" "origin" "0 576 120" "classname" "light" } { "light" "180" "origin" "160 576 72" "classname" "light" } { "light" "200" "origin" "560 -32 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "400 -32 72" } { "light" "200" "origin" "0 712 72" "classname" "light" } { "classname" "light" "origin" "0 728 -136" "light" "200" } { "light" "200" "origin" "0 592 -136" "classname" "light" } { "wait" "5" "angle" "-2" "sounds" "2" "targetname" "t1" "classname" "func_door" "dmg" "10" "model" "*3" } { "sounds" "1" "target" "t1" "angle" "180" "classname" "func_button" "model" "*4" } { "light" "200" "origin" "412 780 136" "classname" "light" } { "light" "200" "classname" "light" "origin" "328 904 72" } { "light" "200" "origin" "168 800 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "-72 864 72" } { "origin" "264 888 -136" "classname" "light" } { "classname" "light" "origin" "-8 992 -136" "light" "200" } { "light" "250" "classname" "light" "origin" "272 1064 -136" } { "light" "250" "origin" "-8 1232 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "256 1272 -136" } { "light" "250" "origin" "312 1464 -136" "classname" "light" } { "light" "200" "origin" "128 968 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-48 1168 72" } { "light" "250" "origin" "312 1168 72" "classname" "light" } { "light" "220" "classname" "light" "origin" "128 1504 -120" } { "light" "250" "classname" "light" "origin" "-56 1464 -136" } { "sounds" "2" "classname" "func_door" "angle" "180" "speed" "400" "model" "*5" } { "classname" "func_door" "angle" "0" "speed" "400" "model" "*6" } { "classname" "light_fluoro" "origin" "176 1744 -152" } { "origin" "80 1744 -152" "classname" "light_fluoro" } { "light" "250" "origin" "-232 1600 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "488 1600 -136" } { "origin" "-56 1448 72" "classname" "light" "light" "250" } { "light" "250" "classname" "light" "origin" "312 1448 72" } { "light" "260" "classname" "light_fluoro" "origin" "416 2064 -112" } { "light" "260" "origin" "416 1968 -112" "classname" "light_fluoro" } { "light" "250" "origin" "128 1880 -112" "classname" "light" } { "origin" "616 1944 -88" "classname" "light" } { "style" "10" "classname" "light_fluorospark" "origin" "344 2216 -88" } { "light" "180" "origin" "352 2016 -112" "classname" "light" } { "classname" "light" "origin" "128 2056 -112" "light" "250" } { "light" "250" "origin" "-112 1984 -112" "classname" "light" } { "light" "350" "origin" "-472 2064 -88" "classname" "light_fluoro" } { "classname" "light" "origin" "-192 2208 8" "light" "250" } { "light" "250" "origin" "-424 2208 8" "classname" "light" } { "light" "250" "origin" "-248 2088 -96" "classname" "light" } { "origin" "-200 2384 -72" "classname" "light" } { "classname" "light" "origin" "-424 2384 -72" } { "light" "200" "origin" "-448 2408 -128" "classname" "light" } { "classname" "light" "origin" "-176 2408 -128" "light" "200" } { "sounds" "1" "classname" "func_plat" "model" "*7" } { "light" "350" "origin" "-352 2656 184" "classname" "light" } { "light" "350" "classname" "light" "origin" "-352 2464 184" } { "origin" "-576 2800 -40" "classname" "light" } { "light" "500" "origin" "160 2920 232" "classname" "light" } { "classname" "light" "origin" "160 2720 232" "light" "500" } { "origin" "-288 2992 8" "classname" "light" } { "classname" "light" "origin" "-168 2776 -40" } { "classname" "light" "origin" "160 2824 104" "light" "200" } { "light" "150" "origin" "-64 2760 136" "classname" "light" } { "light" "200" "origin" "16 2832 -152" "classname" "light" } { "classname" "light" "origin" "304 2832 -152" "light" "200" } { "origin" "504 2816 16" "classname" "light" } { "sounds" "3" "wait" "-1" "speed" "600" "targetname" "t2" "spawnflags" "1" "angle" "270" "classname" "func_door" "model" "*8" } { "classname" "light" "origin" "160 2840 -152" "light" "200" } { "light" "80" "origin" "16 2904 -88" "classname" "light" } { "classname" "light" "origin" "304 2904 -88" "light" "80" } { "classname" "light" "origin" "160 2904 -88" "light" "80" } { "wait" "-1" "sounds" "1" "target" "t2" "speed" "50" "angle" "270" "classname" "func_button" "model" "*9" } { "light" "100" "origin" "0 1800 -32" "classname" "light" } { "classname" "light" "origin" "248 1800 -32" "light" "100" } { "style" "32" "targetname" "t3" "origin" "8 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2392 200" } { "style" "32" "targetname" "t3" "origin" "56 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2312 200" } { "style" "32" "targetname" "t3" "light" "200" "origin" "32 2352 88" "classname" "light" } { "spawnflags" "2048" "origin" "112 2352 16" "classname" "weapon_nailgun" } { "sounds" "3" "targetname" "t3" "spawnflags" "3" "angle" "270" "classname" "func_door_secret" "model" "*10" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*11" } { "origin" "304 2368 96" "classname" "light" } { "angle" "180" "origin" "248 2392 40" "classname" "monster_army" } { "origin" "272 2352 64" "classname" "item_spikes" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*12" } { "origin" "832 2608 16" "classname" "light" "light" "220" } { "light" "220" "classname" "light" "origin" "832 2480 0" } { "light" "240" "origin" "800 2816 24" "classname" "light" } { "style" "33" "targetname" "t11" "spawnflags" "1" "classname" "light" "origin" "752 2000 -88" "light" "400" } { "style" "34" "spawnflags" "1" "targetname" "t12" "origin" "1280 2000 -152" "classname" "light" "light" "400" } { "style" "35" "spawnflags" "1" "targetname" "t13" "classname" "light" "origin" "1280 2496 -216" "light" "400" } { "style" "36" "spawnflags" "1" "targetname" "t14" "origin" "784 2496 -280" "classname" "light" } { "classname" "light" "origin" "1368 2584 -488" "light" "200" } { "origin" "1368 1944 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2584 -488" "light" "150" } { "origin" "1016 2584 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "1016 1944 -488" "light" "200" } { "origin" "1368 2272 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2272 -488" "light" "200" } { "classname" "light" "origin" "960 2296 -488" "light" "200" } { "light" "200" "origin" "1032 2352 -488" "classname" "light" } { "classname" "light" "origin" "888 2352 -488" "light" "200" } { "light" "200" "origin" "960 2408 -488" "classname" "light" } { "light" "100" "classname" "light" "origin" "984 2448 -304" } { "classname" "light" "origin" "832 2360 112" "light" "400" } { "classname" "light" "origin" "1144 2448 -488" } { "origin" "1232 2360 -488" "classname" "light" } { "classname" "light" "origin" "1320 2448 -488" "light" "200" } { "light" "200" "origin" "1232 2536 -488" "classname" "light" } { "classname" "light" "origin" "1232 2136 -488" } { "origin" "1144 2048 -488" "classname" "light" } { "classname" "light" "origin" "1232 1960 -488" "light" "200" } { "light" "200" "origin" "1320 2048 -488" "classname" "light" } { "classname" "light" "origin" "832 2336 -200" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "sounds" "3" "model" "*13" } { "classname" "func_door_secret" "angle" "180" "sounds" "3" "model" "*14" } { "classname" "light" "origin" "552 2480 -56" "light" "200" } { "light" "200" "origin" "544 2296 -56" "classname" "light" } { "classname" "light" "origin" "664 2480 -56" "light" "200" } { "classname" "func_door" "targetname" "t4" "angle" "-2" "spawnflags" "1" "sounds" "2" "model" "*15" "lip" "7" // svdijk -- added to prevent z-fighting } { "classname" "trigger_multiple" "target" "t4" "health" "1" "model" "*16" } { "spawnflags" "2048" "classname" "func_door" "angle" "90" "targetname" "t5" "wait" "-1" "sounds" "2" "model" "*17" } { "spawnflags" "2048" "classname" "trigger_once" "target" "t5" "model" "*18" } { "classname" "item_artifact_super_damage" "origin" "544 2480 -88" } { "classname" "light" "origin" "832 2104 -208" } { "classname" "light" "origin" "832 2048 -368" "light" "150" } { "classname" "light" "origin" "1120 2464 112" } { "origin" "1120 2080 112" "classname" "light" } { "classname" "light" "origin" "752 2080 112" "light" "200" } { "classname" "light" "origin" "1048 2280 -72" } { "classname" "func_button" "angle" "270" "target" "t1" "model" "*19" } { "classname" "light" "origin" "1136 1848 -504" "light" "220" } { "origin" "1136 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1008 1672 -504" "light" "220" } { "origin" "1008 1848 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1288 1848 -504" "light" "220" } { "origin" "1400 1584 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1584 -504" "light" "220" } { "origin" "1400 1736 -504" "classname" "light" "light" "220" } { "origin" "880 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "744 1672 -504" "light" "220" } { "classname" "light" "origin" "1312 1648 -392" "light" "220" } { "light" "170" "origin" "1312 1520 -392" "classname" "light" } { "classname" "light" "origin" "1200 1760 -392" "light" "220" } { "light" "170" "origin" "1072 1760 -392" "classname" "light" } { "classname" "light" "origin" "944 1760 -392" "light" "170" } { "origin" "832 1992 -208" "classname" "light" "light" "220" } { "origin" "744 1832 -504" "classname" "light" } { "light" "170" "origin" "832 1760 -392" "classname" "light" } { "light" "220" "origin" "680 1936 -504" "classname" "light" } { "classname" "light" "origin" "1312 1392 -352" "light" "170" } { "light" "170" "origin" "1312 1264 -288" "classname" "light" } { "classname" "light" "origin" "1312 1136 -232" } { "origin" "1224 1456 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1456 -504" "light" "220" } { "origin" "1400 1328 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1328 -504" "light" "220" } { "origin" "1224 1200 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1200 -504" "light" "220" } { "origin" "1312 960 -208" "classname" "light" } { "classname" "trigger_teleport" "target" "t6" "model" "*20" } { "classname" "light" "origin" "1312 912 -472" } { "classname" "light" "origin" "1312 1080 -368" } { "classname" "light" "origin" "1128 1064 -504" "light" "170" } { "origin" "1128 856 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1496 856 -504" "light" "170" } { "origin" "1496 1064 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1312 776 -504" "light" "170" } { "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*21" } { "origin" "1072 1024 -168" "classname" "light" } { "spawnflags" "1" "height" "400" "angle" "-1" "sounds" "1" "classname" "func_plat" "model" "*22" } { "targetname" "t8" "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*23" } { "target" "t8" "classname" "trigger_multiple" "model" "*24" } { "light" "220" "origin" "792 888 -248" "classname" "light" } { "light" "180" "classname" "light" "origin" "944 608 -248" } { "light" "150" "origin" "792 512 -248" "classname" "light" } { "classname" "light" "origin" "792 512 -56" "light" "150" } { "classname" "light" "origin" "624 928 -240" "light" "220" } { "light" "220" "origin" "504 1200 -248" "classname" "light" } { "origin" "936 800 -248" "classname" "light" "light" "180" } { "light" "180" "classname" "light" "origin" "960 984 -208" } { "classname" "light" "origin" "792 512 128" "light" "150" } { "spawnflags" "2" "origin" "944 1008 -272" "classname" "item_health" } { "spawnflags" "1792" "origin" "144 2352 16" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1216 1040 -432" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "1392 1024 -432" "classname" "item_rockets" } { "targetname" "t6" "origin" "-32 1800 -56" "classname" "info_teleport_destination" } { "spawnflags" "1792" "origin" "832 2448 -368" "classname" "weapon_supernailgun" } { "spawnflags" "1792" "origin" "128 1216 -208" "classname" "weapon_supershotgun" } { "origin" "296 2136 -192" "classname" "item_shells" } { "spawnflags" "1" "origin" "1424 904 -432" "classname" "item_health" } { "classname" "item_health" "origin" "1376 808 -432" } { "origin" "1176 936 -432" "classname" "item_health" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*25" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*26" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*27" } { "target" "t10" "targetname" "t9" "count" "3" "classname" "trigger_counter" "model" "*28" } { "message" "You must press the three buttons..." "spawnflags" "2048" "sounds" "2" "wait" "-1" "targetname" "t10" "angle" "180" "classname" "func_door" "model" "*29" } { "light" "150" "origin" "832 1928 -384" "classname" "light" } { "style" "33" "sounds" "3" "target" "t11" "classname" "trigger_once" "model" "*30" } { "style" "34" "sounds" "3" "target" "t12" "classname" "trigger_once" "model" "*31" } { "style" "35" "sounds" "3" "target" "t13" "classname" "trigger_once" "model" "*32" } { "style" "36" "sounds" "3" "target" "t14" "classname" "trigger_once" "model" "*33" } { "sounds" "1" "wait" "-1" "targetname" "t11" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*34" } { "targetname" "t12" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*35" } { "targetname" "t13" "sounds" "1" "wait" "-1" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*36" } { "targetname" "t14" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*37" } { "angle" "90" "origin" "1312 880 -248" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "1376 1024 -272" "classname" "item_spikes" } { "origin" "1184 992 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1376 856 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1256 1704 -432" "classname" "item_health" } { "angle" "90" "origin" "480 48 24" "classname" "info_player_deathmatch" } { "angle" "180" "origin" "528 1888 -168" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "-272 2928 -56" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "832 2048 -152" "classname" "info_player_deathmatch" } { "speed" "300" "message" "This door opens elsewhere..." "spawnflags" "2048" "targetname" "t15" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*38" } { "spawnflags" "2048" "target" "t15" "classname" "trigger_once" "model" "*39" } { "spawnflags" "1792" "origin" "480 576 0" "classname" "weapon_nailgun" } { "spawnflags" "1793" "origin" "464 728 64" "classname" "item_spikes" } { "origin" "328 848 -224" "classname" "item_health" } { "classname" "item_health" "origin" "344 920 -224" } { "spawnflags" "1" "origin" "-16 2064 -208" "classname" "item_health" } { "spawnflags" "1792" "origin" "-480 2240 -160" "classname" "item_rockets" } { "spawnflags" "1793" "origin" "-96 2456 16" "classname" "item_shells" } { "classname" "item_rockets" "origin" "-104 2216 16" "spawnflags" "1793" } { "classname" "item_artifact_invulnerability" "origin" "256 1808 -40" "spawnflags" "1792" } { "classname" "monster_army" "origin" "0 576 24" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "8 1520 -200" "angle" "270" } { "classname" "monster_dog" "origin" "88 1520 -200" "angle" "270" } { "classname" "monster_army" "origin" "224 1552 -200" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "-8 936 -200" "classname" "monster_army" } { "classname" "monster_army" "origin" "648 736 104" "spawnflags" "768" "angle" "180" } { "classname" "item_artifact_envirosuit" "origin" "712 2040 -408" "angle" "90" } { "classname" "light" "origin" "712 2040 -360" "light" "100" } { "classname" "item_rockets" "origin" "1328 2536 -528" "spawnflags" "1793" } { "classname" "item_health" "origin" "916 2416 -136" "spawnflags" "2" } { "spawnflags" "1" "classname" "monster_army" "origin" "1312 936 -248" "angle" "90" } { "classname" "monster_dog" "origin" "1336 1784 -408" "angle" "180" "spawnflags" "257" } { "spawnflags" "257" "angle" "90" "origin" "1392 928 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1384 1008 -248" "angle" "90" "spawnflags" "768" } { "spawnflags" "768" "angle" "90" "origin" "1240 1008 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1256 1760 -408" "angle" "180" "spawnflags" "257" } { "classname" "monster_army" "origin" "824 1784 -408" "spawnflags" "257" "angle" "90" } { "classname" "monster_dog" "origin" "1128 1760 -408" "angle" "180" "spawnflags" "769" } { "classname" "path_corner" "origin" "880 2048 -168" "target" "t16" "targetname" "t17" } { "origin" "1232 2048 -232" "classname" "path_corner" "targetname" "t16" "target" "t17" } { "classname" "monster_army" "origin" "1232 2088 -216" "target" "t16" } { "classname" "monster_army" "origin" "1232 2448 -280" "angle" "270" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2464 -344" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2072 -408" "angle" "90" } { "classname" "monster_dog" "origin" "840 1960 -408" "angle" "90" "spawnflags" "768" } { "classname" "trigger_multiple" "target" "t18" "health" "1" "model" "*40" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t18" "model" "*41" } { "classname" "weapon_supershotgun" "origin" "-360 2912 -80" } { "classname" "trigger_multiple" "target" "t18" "model" "*42" } { "classname" "light" "origin" "-352 2912 -24" "light" "120" } { "classname" "light" "origin" "160 3024 0" "light" "120" } { "classname" "item_shells" "origin" "528 720 80" } { "classname" "monster_army" "origin" "416 1912 -168" "angle" "180" "spawnflags" "768" } { "classname" "monster_dog" "origin" "432 2120 -168" "angle" "180" "spawnflags" "256" } { "classname" "path_corner" "origin" "248 1992 -200" "targetname" "t19" "target" "t20" } { "origin" "-200 1992 -200" "classname" "path_corner" "targetname" "t20" "target" "t21" } { "classname" "path_corner" "origin" "-136 1912 -200" "targetname" "t21" "target" "t22" } { "origin" "248 1912 -200" "classname" "path_corner" "target" "t19" "targetname" "t22" } { "classname" "monster_army" "origin" "80 2024 -184" "target" "t20" } { "classname" "monster_army" "origin" "-16 1888 -184" "spawnflags" "256" "target" "t22" } { "classname" "monster_dog" "origin" "-248 2144 -136" "spawnflags" "768" "angle" "315" } { "classname" "path_corner" "origin" "-560 2352 40" "targetname" "t23" "target" "t24" } { "origin" "-104 2352 40" "classname" "path_corner" "target" "t23" "targetname" "t24" } { "classname" "monster_army" "origin" "-432 2352 56" "spawnflags" "768" "target" "t23" } { "angle" "0" "classname" "monster_dog" "origin" "-544 2584 56" "spawnflags" "256" } { "classname" "monster_army" "origin" "-344 2656 -104" "angle" "270" } { "classname" "monster_dog" "origin" "-72 2896 -56" "spawnflags" "256" "angle" "225" } { "classname" "monster_army" "origin" "432 2920 -56" "target" "t25" } { "classname" "monster_army" "origin" "424 2832 -56" "spawnflags" "256" "angle" "180" } { "classname" "path_corner" "origin" "368 2936 -72" "targetname" "t25" "target" "t26" } { "origin" "368 2696 -72" "classname" "path_corner" "targetname" "t26" "target" "t27" } { "classname" "path_corner" "origin" "480 2696 -72" "targetname" "t27" "target" "t28" } { "origin" "480 2936 -72" "classname" "path_corner" "target" "t25" "targetname" "t28" } { "classname" "monster_army" "origin" "424 2672 -56" "target" "t27" } { "classname" "monster_army" "origin" "424 2880 -56" "angle" "180" "spawnflags" "768" } { "classname" "monster_army" "origin" "424 2760 -56" "spawnflags" "768" "angle" "180" } { "classname" "path_corner" "origin" "832 2712 -88" "targetname" "t29" "target" "t30" } { "origin" "832 2416 -104" "classname" "path_corner" "target" "t29" "targetname" "t30" } { "classname" "monster_army" "origin" "848 2584 -72" "spawnflags" "257" "target" "t29" } { "classname" "monster_army" "origin" "824 2008 -152" "angle" "90" "spawnflags" "768" } { "classname" "item_health" "origin" "-376 1704 -224" "spawnflags" "1" } { "angle" "180" "spawnflags" "768" "origin" "248 2352 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "270" "origin" "-72 2464 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "225" "origin" "904 1024 -248" "classname" "monster_army" } { "light" "100" "style" "10" "classname" "light" "origin" "688 0 80" } { "message" "Shoot this secret door..." "spawnflags" "1" "angle" "0" "classname" "func_door_secret" "model" "*43" } { "origin" "672 -40 48" "classname" "item_shells" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_secret" "model" "*45" } { "classname" "trigger_secret" "model" "*46" } { "classname" "trigger_secret" "model" "*47" } { "classname" "trigger_secret" "model" "*48" } { "classname" "trigger_secret" "model" "*49" } { "light" "100" "origin" "0 632 -88" "classname" "light" } { "classname" "item_health" "origin" "600 2200 -128" "spawnflags" "1" } { "light" "220" "classname" "light" "origin" "832 1880 -504" } { "origin" "72 2056 -208" "classname" "misc_explobox" } { "light" "200" "origin" "-128 584 72" "classname" "light" } { "light" "200" "origin" "-128 568 -136" "classname" "light" } { "light" "100" "origin" "-56 632 -168" "classname" "light" } { "light" "200" "origin" "-56 864 -136" "classname" "light" } { "light" "200" "origin" "40 1672 -40" "classname" "light" } { "classname" "light" "origin" "216 1672 -40" "light" "200" } { "classname" "light" "origin" "128 1080 -152" "light" "200" } { "light" "200" "origin" "128 1096 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-352 1656 72" } { "origin" "608 1640 72" "classname" "light" "light" "250" } { "origin" "-48 1144 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "-48 1256 -320" } { "origin" "320 1256 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "312 1128 -320" } { "origin" "136 1128 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "136 1272 -320" } { "spawnflags" "3072" "wait" "5" "sounds" "2" "message" "You can jump across..." "classname" "trigger_multiple" "targetname" "t32" "model" "*50" } { "spawnflags" "3072" "wait" "5" "message" "You can jump up here..." "sounds" "2" "classname" "trigger_multiple" "targetname" "t31" "model" "*51" } { "light" "150" "origin" "1008 2128 -408" "classname" "light" } { "light" "250" "origin" "1312 544 -184" "classname" "light" } { "light" "200" "classname" "light" "origin" "1208 456 -184" } { "origin" "1416 456 -184" "classname" "light" "light" "200" } { "light" "170" "origin" "1312 728 -56" "classname" "light" } { "map" "e1m2" "classname" "trigger_changelevel" "model" "*52" } { "classname" "item_health" "origin" "1224 2464 -304" "spawnflags" "1" } { "classname" "light" "origin" "688 1680 -160" "light" "160" } { "light" "160" "origin" "-392 1688 -160" "classname" "light" } { "spawnflags" "768" "angle" "270" "origin" "288 1536 -200" "classname" "monster_army" } { "spawnflags" "768" "origin" "968 2432 -112" "classname" "monster_army" } { "wait" "5" "message" "Walk into the slipgate to exit." "classname" "trigger_multiple" "sounds" "2" "angle" "270" "model" "*53" } { "classname" "trigger_once" "killtarget" "t31" "target" "t31" "spawnflags" "3072" "model" "*54" } { "classname" "trigger_once" "spawnflags" "3072" "target" "t32" "killtarget" "t32" "model" "*55" } { "classname" "item_armor2" "origin" "1312 1048 -432" } { "classname" "ambient_comp_hum" "origin" "250 194 72" } { "origin" "714 194 72" "classname" "ambient_comp_hum" } { "classname" "ambient_comp_hum" "origin" "626 2058 -104" } { "origin" "466 2226 -104" "classname" "ambient_comp_hum" } { "classname" "info_intermission" "origin" "-112 704 56" "mangle" "20 45 0" } { "classname" "info_intermission" "origin" "-208 2736 192" "mangle" "20 225 0" } { "classname" "info_intermission" "origin" "240 2664 104" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1376 1936 64" "mangle" "20 135 0" } { "angle" "90" "origin" "528 -296 72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "432 -296 72" "angle" "90" } { "angle" "90" "origin" "480 -240 72" "classname" "info_player_coop" } { "classname" "func_wall" "spawnflags" "1792" "model" "*56" } { "classname" "func_wall" "spawnflags" "1792" "model" "*57" } { "classname" "ambient_drone" "origin" "1314 450 -200" } quakespasm-0.93.0/Misc/qs_pak/maps/e2m3.ent0000644000000000000000000011407512425507313017054 0ustar rootroot{ "classname" "worldspawn" "wad" "gfx/jr_med.wad" "worldtype" "0" "sounds" "9" "message" "the Crypt of Decay" } { "classname" "light" "origin" "192 -648 128" } { "classname" "info_player_start" "origin" "688 -1600 -312" "angle" "180" } { "classname" "light_flame_large_yellow" "origin" "650 -438 4" } { "origin" "386 -438 4" "classname" "light_flame_large_yellow" } { "origin" "66 -886 4" "classname" "light_flame_large_yellow" } { "origin" "322 -886 4" "classname" "light_flame_large_yellow" } { "light" "250" "origin" "192 -1408 288" "classname" "light" } { "light" "250" "classname" "light" "origin" "192 -1088 288" } { "light" "250" "origin" "112 -1248 272" "classname" "light" } { "light" "250" "classname" "light" "origin" "272 -1248 272" } { "light" "200" "origin" "192 -1056 32" "classname" "light" } { "light" "150" "origin" "192 -1248 24" "classname" "light" } { "origin" "194 -1462 108" "classname" "light_flame_large_yellow" } { "origin" "194 -1030 164" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "192 -1440 32" "classname" "light" } { "sounds" "2" "classname" "func_plat" "spawnflags" "1" "model" "*1" } { "origin" "226 -1670 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "88 -1552 -184" "classname" "light" } { "origin" "-22 -1374 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "328 -1256 -184" "classname" "light" } { "light" "150" "classname" "light" "origin" "56 -1256 -184" } { "classname" "light" "origin" "248 -1480 -184" "light" "150" } { "light" "250" "origin" "552 -1608 -72" "classname" "light" } { "light" "150" "origin" "432 -1656 -224" "classname" "light" } { "light" "150" "origin" "432 -1496 -224" "classname" "light" } { "light" "100" "origin" "192 -1248 -40" "classname" "light" } { "origin" "10 -438 4" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-254 -438 4" } { "light" "150" "origin" "192 -704 -136" "classname" "light" } { "light" "250" "origin" "192 -512 -136" "classname" "light" } { "classname" "light" "origin" "416 -512 -136" "light" "150" } { "light" "150" "origin" "-32 -512 -136" "classname" "light" } { "classname" "light" "origin" "-208 -512 -136" "light" "150" } { "light" "150" "origin" "592 -512 -136" "classname" "light" } { "classname" "light" "origin" "192 -840 -136" "light" "150" } { "light" "150" "origin" "-352 -672 -168" "classname" "light" } { "classname" "light" "origin" "-320 -832 -168" "light" "150" } { "light" "150" "origin" "-320 -512 -168" "classname" "light" } { "classname" "light" "origin" "696 -512 -168" "light" "150" } { "light" "150" "origin" "736 -672 -168" "classname" "light" } { "classname" "light" "origin" "704 -832 -168" "light" "150" } { "light" "150" "origin" "512 -864 -168" "classname" "light" } { "classname" "light" "origin" "-128 -864 -168" "light" "150" } { "light" "200" "origin" "-128 -320 8" "classname" "light" } { "classname" "light" "origin" "512 -320 8" "light" "200" } { "origin" "384 -24 32" "classname" "light" } { "classname" "light" "origin" "0 -24 32" } { "light" "200" "origin" "416 -192 -8" "classname" "light" } { "classname" "light" "origin" "-32 -192 -8" "light" "200" } { "light" "200" "origin" "840 48 72" "classname" "light" } { "light" "150" "origin" "576 -24 -56" "classname" "light" } { "light" "200" "origin" "624 -24 72" "classname" "light" } { "origin" "1002 354 -60" "classname" "light_flame_large_yellow" } { "light" "100" "origin" "1000 352 -128" "classname" "light" } { "classname" "light" "origin" "736 8 72" "light" "200" } { "light" "200" "origin" "936 88 72" "classname" "light" } { "light" "150" "origin" "688 -8 -32" "classname" "light" } { "classname" "light" "origin" "784 24 -104" "light" "150" } { "light" "150" "origin" "888 72 -32" "classname" "light" } { "light" "200" "origin" "872 208 -56" "classname" "light" } { "classname" "light" "origin" "872 400 -56" "light" "200" } { "light" "200" "origin" "872 592 -56" "classname" "light" } { "classname" "light" "origin" "744 568 88" "light" "150" } { "light" "150" "origin" "744 648 88" "classname" "light" } { "classname" "light" "origin" "704 608 -80" "light" "150" } { "origin" "866 730 -60" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "864 728 -128" "light" "100" } { "sounds" "3" "wait" "-1" "targetname" "t8" "spawnflags" "2049" "angle" "0" "classname" "func_door" "model" "*2" } { "spawnflags" "2048" "angle" "90" "target" "t8" "classname" "func_button" "wait" "-1" "model" "*3" } { "origin" "520 608 -64" "classname" "light" } { "light" "400" "origin" "192 608 -24" "classname" "light" } { "sounds" "1" "wait" "-1" "angle" "270" "spawnflags" "2058" "classname" "func_door_secret" "targetname" "t9" "model" "*4" } { "light" "150" "origin" "1064 640 -112" "classname" "light" } { "targetname" "t9" "angle" "180" "origin" "1024 640 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1120 672 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1088 600 -152" "classname" "monster_zombie" } { "origin" "976 336 -176" "classname" "item_health" } { "light" "150" "origin" "192 608 -104" "classname" "light" } { "origin" "192 288 -64" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "66 106 4" "light" "200" } { "light" "200" "origin" "-30 106 4" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "504 120 -248" "light" "200" } { "light" "200" "origin" "704 224 -248" "classname" "light" } { "classname" "light" "origin" "704 472 -248" "light" "200" } { "light" "200" "origin" "304 112 -248" "classname" "light" } { "classname" "light" "origin" "80 112 -248" "light" "200" } { "light" "200" "origin" "720 608 -248" "classname" "light" } { "spawnflags" "2048" "sounds" "1" "wait" "-1" "targetname" "t3" "classname" "func_door" "angle" "90" "model" "*5" } { "spawnflags" "2048" "wait" "-1" "angle" "270" "classname" "func_door" "message" "This door opens nearby..." "model" "*6" } { "spawnflags" "2048" "target" "t3" "wait" "-1" "classname" "func_button" "angle" "180" "model" "*7" } { "light" "250" "origin" "-448 184 0" "classname" "light" } { "light" "150" "origin" "-552 280 -224" "classname" "light" } { "classname" "light" "origin" "-392 280 -224" "light" "150" } { "light" "150" "origin" "-416 -32 104" "classname" "light" } { "classname" "light" "origin" "-320 -32 104" "light" "150" } { "light" "150" "origin" "-224 -32 104" "classname" "light" } { "light" "250" "origin" "-352 -32 -32" "classname" "light" } { "light" "200" "origin" "-1160 88 -248" "classname" "light" } { "light" "200" "origin" "-1048 88 -32" "classname" "light" } { "light" "200" "classname" "light" "origin" "-1160 224 -32" } { "origin" "-742 658 -44" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-926 658 -44" } { "light" "200" "origin" "-736 632 -96" "classname" "light" } { "classname" "light" "origin" "-928 632 -96" "light" "200" } { "light" "150" "origin" "-600 104 -248" "classname" "light" } { "classname" "light" "origin" "-696 424 -248" "light" "150" } { "light" "150" "origin" "-1152 408 -248" "classname" "light" } { "classname" "light" "origin" "-944 432 -248" "light" "150" } { "light" "150" "origin" "-856 64 -248" "classname" "light" } { "classname" "light" "origin" "-160 152 -248" "light" "150" } { "light" "150" "origin" "-160 448 -248" "classname" "light" } { "classname" "light" "origin" "-328 464 -248" "light" "150" } { "light" "150" "origin" "-256 176 -248" "classname" "light" } { "origin" "-574 410 -172" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-470 410 -172" } { "target" "t4" "classname" "trigger_teleport" "model" "*8" } { "light" "200" "style" "2" "origin" "-760 576 -216" "classname" "light" } { "targetname" "t4" "angle" "180" "origin" "120 -32 -112" "classname" "info_teleport_destination" } { "light" "200" "origin" "-264 384 112" "classname" "light" } { "classname" "light" "origin" "-264 288 112" "light" "200" } { "light" "200" "origin" "-264 192 112" "classname" "light" } { "classname" "light" "origin" "-264 480 112" "light" "200" } { "light" "250" "origin" "-264 304 -56" "classname" "light" } { "classname" "light" "origin" "-520 424 0" "light" "250" } { "light" "200" "origin" "-1120 608 72" "classname" "light" } { "classname" "light" "origin" "-1024 584 72" "light" "200" } { "light" "200" "origin" "-928 584 72" "classname" "light" } { "classname" "light" "origin" "-832 584 72" "light" "200" } { "light" "200" "origin" "-544 584 72" "classname" "light" } { "classname" "light" "origin" "-640 584 72" "light" "200" } { "light" "150" "origin" "-480 768 56" "classname" "light" } { "classname" "light" "origin" "-384 768 56" "light" "150" } { "classname" "light" "origin" "-712 120 -32" "light" "200" } { "sounds" "3" "wait" "-1" "targetname" "t5" "spawnflags" "2049" "angle" "180" "classname" "func_door" "model" "*9" } { "spawnflags" "2048" "angle" "270" "target" "t5" "wait" "-1" "classname" "func_button" "model" "*10" } { "style" "32" "targetname" "t5" "light" "200" "origin" "-352 552 -56" "classname" "light" } { "light" "150" "origin" "-432 768 -56" "classname" "light" } { "light" "150" "origin" "-520 680 -56" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "targetname" "t5" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*11" } { "sounds" "3" "targetname" "t5" "spawnflags" "2049" "wait" "-1" "angle" "90" "classname" "func_door" "model" "*12" } { "origin" "-72 848 -56" "classname" "light" } { "origin" "-120 600 -8" "classname" "light" } { "classname" "light" "origin" "192 904 -8" } { "light" "200" "origin" "192 888 -248" "classname" "light" } { "classname" "light" "origin" "-104 600 -248" "light" "200" } { "light" "200" "origin" "376 984 -120" "classname" "light" } { "classname" "light" "origin" "504 760 -120" "light" "200" } { "light" "200" "origin" "-32 608 200" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*13" } { "origin" "-16 1456 16" "classname" "light" } { "light" "200" "origin" "384 1248 -56" "classname" "light" } { "classname" "light" "origin" "384 1440 -56" "light" "200" } { "light" "150" "origin" "256 1440 -56" "classname" "light" } { "classname" "light" "origin" "192 1248 -56" "light" "200" } { "light" "200" "origin" "384 1344 -88" "classname" "light" } { "classname" "light" "origin" "192 1152 -88" "light" "200" } { "classname" "light" "origin" "8 1456 -120" "light" "200" } { "spawnflags" "2048" "sounds" "1" "classname" "item_key2" "origin" "-16 1456 -152" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*14" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*15" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "spawnflags" "2048" "model" "*16" } { "classname" "trigger_once" "target" "t6" "model" "*17" } { "classname" "light" "origin" "-192 1456 -136" "light" "80" } { "classname" "light" "origin" "-16 1280 -136" "light" "80" } { "spawnflags" "768" "classname" "monster_hell_knight" "origin" "-16 1280 -168" "angle" "90" "targetname" "t6" } { "spawnflags" "256" "angle" "270" "origin" "-16 1632 -168" "classname" "monster_hell_knight" "targetname" "t6" } { "classname" "monster_hell_knight" "origin" "-192 1456 -168" "angle" "0" "targetname" "t6" } { "classname" "light" "origin" "152 1440 -56" "light" "150" } { "classname" "item_shells" "origin" "-104 1512 -192" "spawnflags" "1" } { "wait" "-1" "classname" "func_door" "angle" "0" "spawnflags" "2056" "model" "*18" } { "wait" "-1" "classname" "func_door" "angle" "180" "spawnflags" "2056" "model" "*19" } { "classname" "light" "origin" "-1120 832 -48" "light" "250" } { "classname" "light" "origin" "-1120 976 -24" "light" "250" } { "classname" "light" "origin" "-1240 1296 216" "light" "200" } { "light" "200" "origin" "-1240 1416 216" "classname" "light" } { "classname" "light" "origin" "-1240 1176 216" "light" "200" } { "classname" "light" "origin" "-712 1416 216" "light" "200" } { "light" "200" "origin" "-712 1296 216" "classname" "light" } { "classname" "light" "origin" "-712 1176 216" "light" "200" } { "light" "200" "origin" "-856 1296 216" "classname" "light" } { "classname" "light" "origin" "-1096 1296 216" "light" "200" } { "light" "200" "origin" "-976 1296 216" "classname" "light" } { "classname" "light_flame_small_white" "origin" "-1318 1514 -8" } { "origin" "-1318 1514 64" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-1318 1514 144" } { "origin" "-634 1078 -8" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-634 1078 64" } { "origin" "-634 1078 144" "classname" "light_flame_small_white" } { "classname" "func_plat" "spawnflags" "1" "model" "*20" } { "classname" "light" "origin" "-320 1536 184" "light" "250" } { "classname" "light" "origin" "-376 1312 120" "light" "250" } { "classname" "light" "origin" "-16 1536 184" "light" "200" } { "light" "200" "origin" "-560 1312 56" "classname" "light" } { "classname" "light" "origin" "24 1120 216" "light" "150" } { "classname" "light" "origin" "192 608 176" "light" "200" } { "wait" "-1" "classname" "func_door" "angle" "90" "spawnflags" "2049" "targetname" "t7" "sounds" "1" "model" "*21" } { "light" "150" "origin" "400 1104 224" "classname" "light" } { "light" "150" "origin" "-200 1056 224" "classname" "light" } { "light" "200" "origin" "192 960 224" "classname" "light" } { "light" "200" "origin" "192 784 224" "classname" "light" } { "classname" "light" "origin" "672 264 184" "light" "200" } { "light" "200" "origin" "384 184 184" "classname" "light" } { "classname" "light" "origin" "512 256 8" "light" "200" } { "classname" "light" "origin" "8 184 200" "light" "200" } { "classname" "light" "origin" "584 744 200" "light" "200" } { "classname" "light" "origin" "0 432 200" "light" "200" } { "classname" "trigger_once" "targetname" "t8" "target" "t9" "delay" "10" "model" "*22" } { "classname" "light" "origin" "192 -72 216" "light" "250" } { "classname" "light" "origin" "360 -168 360" "light" "200" } { "classname" "light" "origin" "296 -168 360" "light" "200" } { "origin" "472 -168 368" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "408 -168 360" } { "classname" "light" "origin" "384 -168 248" "light" "200" } { "classname" "light" "origin" "936 -304 328" "light" "200" } { "light" "200" "origin" "1000 -232 360" "classname" "light" } { "classname" "light" "origin" "864 -232 360" "light" "200" } { "light" "200" "origin" "864 -368 360" "classname" "light" } { "classname" "light" "origin" "1000 -368 360" "light" "200" } { "classname" "light" "origin" "736 -248 280" "light" "200" } { "light" "200" "origin" "552 -216 280" "classname" "light" } { "classname" "light" "origin" "16 144 -32" "light" "200" } { "classname" "light" "origin" "0 432 -136" "light" "200" } { "classname" "light" "origin" "192 384 184" "light" "200" } { "light" "200" "origin" "192 192 184" "classname" "light" } { "spawnflags" "2048" "classname" "func_button" "wait" "-1" "target" "t7" "model" "*23" } { "style" "33" "targetname" "t7" "classname" "light" "origin" "176 1152 200" "target" "t10" } { "classname" "info_null" "origin" "292 1152 180" "targetname" "t10" } { "classname" "light" "origin" "192 1224 200" "light" "150" } { "light" "150" "origin" "192 1376 200" "classname" "light" } { "classname" "light" "origin" "192 1536 200" "light" "200" } { "light" "150" "origin" "192 1080 200" "classname" "light" } { "classname" "weapon_nailgun" "origin" "184 -1520 -272" } { "classname" "func_button" "target" "t11" "angle" "-1" "targetname" "t12" "lip" "4" "wait" "0.1" "speed" "300" "health" "1" "model" "*24" } { "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t11" "wait" "10" "model" "*25" "lip" "7" // svdijk -- added to prevent z-fighting } { "classname" "func_door_secret" "angle" "90" "spawnflags" "8" "targetname" "t11" "model" "*26" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "light" "origin" "1248 -288 312" "light" "150" } { "light" "200" "origin" "1176 -400 352" "classname" "light" } { "classname" "item_health" "origin" "1336 -536 256" "spawnflags" "2" } { "classname" "light" "origin" "1320 -488 352" "light" "200" } { "classname" "trigger_multiple" "target" "t11" "wait" "10" "model" "*27" } { "classname" "item_armor1" "origin" "192 -592 -64" } { "classname" "item_shells" "origin" "176 592 -160" "spawnflags" "1" } { "classname" "weapon_nailgun" "origin" "-80 1456 -192" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "56 1144 128" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "-736 608 -280" "spawnflags" "1792" } { "classname" "item_spikes" "origin" "1120 -384 256" "spawnflags" "1" } { "classname" "item_rockets" "origin" "840 -432 192" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-456 312 -80" } { "classname" "monster_zombie" "origin" "-1152 96 -88" "spawnflags" "256" "target" "t36" } { "origin" "-1120 32 -88" "classname" "monster_zombie" "spawnflags" "768" "target" "t36" } { "classname" "monster_zombie" "origin" "-1192 168 -88" "target" "t36" } { "classname" "monster_shambler" "origin" "-1120 1104 -56" "angle" "270" } { "classname" "monster_hell_knight" "origin" "-336 1312 8" "angle" "180" } { "classname" "monster_ogre" "origin" "-698 1446 -56" "angle" "270" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "-888 1128 8" "angle" "90" "spawnflags" "256" } { "classname" "item_health" "origin" "-336 784 -128" } { "origin" "-408 608 -128" "classname" "item_health" } { "classname" "path_corner" "origin" "-1096 584 -120" "targetname" "t13" "target" "t14" } { "origin" "-496 584 -120" "classname" "path_corner" "targetname" "t14" "target" "t13" } { "classname" "monster_demon1" "origin" "-712 576 -104" "angle" "180" "target" "t13" } { "classname" "path_corner" "origin" "-528 472 -96" "targetname" "t15" "target" "t16" } { "origin" "-368 -32 -96" "classname" "path_corner" "target" "t15" "targetname" "t16" } { "classname" "monster_ogre" "origin" "-466 262 -56" "target" "t16" "spawnflags" "256" } { "target" "t22" "targetname" "t21" "origin" "56 -184 -120" "classname" "path_corner" } { "target" "t21" "targetname" "t20" "classname" "path_corner" "origin" "-128 -224 -120" } { "target" "t20" "targetname" "t19" "origin" "-128 -504 -56" "classname" "path_corner" } { "target" "t19" "targetname" "t18" "classname" "path_corner" "origin" "512 -504 -56" } { "target" "t18" "targetname" "t17" "origin" "512 -224 -120" "classname" "path_corner" } { "targetname" "t26" "target" "t17" "classname" "path_corner" "origin" "328 -184 -120" } { "target" "t23" "targetname" "t22" "classname" "path_corner" "origin" "-128 -200 -120" } { "target" "t24" "targetname" "t23" "classname" "path_corner" "origin" "-128 -552 -56" } { "target" "t25" "targetname" "t24" "origin" "512 -552 -56" "classname" "path_corner" } { "target" "t26" "targetname" "t25" "classname" "path_corner" "origin" "512 -200 -120" } { "spawnflags" "256" "target" "t21" "origin" "0 -184 -104" "classname" "monster_hell_knight" } { "spawnflags" "1" "origin" "376 -160 -104" "classname" "monster_hell_knight" } { "spawnflags" "768" "angle" "270" "origin" "190 -706 -40" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "90" "origin" "192 -1408 24" "classname" "monster_hell_knight" } { "origin" "40 -1424 0" "classname" "item_shells" "spawnflags" "1" } { "spawnflags" "1" "origin" "304 -1096 0" "classname" "item_health" } { "origin" "328 -1280 -272" "classname" "item_health" } { "origin" "40 -1256 -272" "classname" "item_spikes" } { "target" "t28" "targetname" "t27" "origin" "864 176 -168" "classname" "path_corner" } { "target" "t27" "targetname" "t28" "classname" "path_corner" "origin" "864 616 -168" } { "target" "t27" "origin" "862 446 -152" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "180" "origin" "526 -26 -104" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "0" "origin" "-72 -24 -104" "classname" "monster_hell_knight" } { "spawnflags" "256" "origin" "-274 -34 -104" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "270" "origin" "-512 760 -104" "classname" "monster_hell_knight" } { "spawnflags" "1793" "origin" "336 1104 128" "classname" "item_rockets" } { "angle" "45" "spawnflags" "256" "origin" "104 256 -136" "classname" "monster_zombie" } { "spawnflags" "769" "angle" "90" "origin" "192 488 -136" "classname" "monster_hell_knight" } { "wait" "1" "speed" "250" "lip" "16" "spawnflags" "5" "targetname" "t29" "dmg" "20" "angle" "180" "classname" "func_door" "model" "*28" } { "wait" "1" "targetname" "t29" "speed" "250" "dmg" "20" "lip" "16" "spawnflags" "5" "classname" "func_door" "sounds" "1" "model" "*29" } { "wait" "2" "target" "t29" "classname" "trigger_multiple" "model" "*30" } { "origin" "506 1762 -124" "classname" "light_torch_small_walltorch" "style" "1" } { "classname" "light_torch_small_walltorch" "origin" "274 2010 -124" } { "light" "200" "origin" "152 1824 -40" "classname" "light" } { "classname" "light" "origin" "288 1824 -40" "light" "200" } { "light" "200" "origin" "288 1696 -40" "classname" "light" } { "classname" "light" "origin" "152 1704 -40" "light" "200" } { "light" "220" "origin" "0 1704 -40" "classname" "light" } { "angle" "180" "classname" "func_door_secret" "targetname" "t35" "spawnflags" "16" "sounds" "1" "model" "*31" } { "spawnflags" "2" "origin" "-16 1816 -192" "classname" "item_health" } { "origin" "432 1672 -320" "classname" "light" "light" "220" } { "spawnflags" "1792" "origin" "-16 1752 -192" "classname" "item_rockets" } { "angle" "270" "origin" "-192 1580 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "236 1536 168" "angle" "180" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "0 1580 168" "angle" "270" "targetname" "t32" } { "angle" "90" "origin" "-96 1492 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "148 1376 168" "angle" "0" "targetname" "t33" } { "angle" "180" "origin" "236 1256 168" "classname" "trap_spikeshooter" "targetname" "t33" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "192 1580 168" "angle" "270" "targetname" "t33" } { "spawnflags" "257" "angle" "90" "origin" "192 -16 153" "classname" "monster_hell_knight" } { "spawnflags" "257" "angle" "180" "origin" "864 -248 217" "classname" "monster_shambler" } { "angle" "180" "origin" "464 -184 185" "classname" "monster_hell_knight" } { "classname" "monster_hell_knight" "origin" "192 -176 153" "angle" "90" "spawnflags" "1" "target" "t31" } { "spawnflags" "769" "angle" "90" "origin" "190 1166 153" "classname" "monster_ogre" } { "spawnflags" "2048" "origin" "48 1456 -192" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "origin" "-96 1376 -192" "classname" "item_rockets" } { "angle" "270" "spawnflags" "768" "classname" "monster_ogre" "origin" "862 662 -152" } { "light" "120" "origin" "192 -352 -264" "classname" "light" } { "spawnflags" "768" "origin" "326 -1490 -248" "classname" "monster_ogre" "angle" "180" "target" "t42" } { "origin" "192 552 128" "classname" "item_health" } { "spawnflags" "1024" "classname" "item_health" "origin" "176 672 128" } { "spawnflags" "1025" "origin" "184 1312 128" "classname" "item_health" } { "classname" "item_health" "origin" "-16 1480 128" "spawnflags" "1025" } { "origin" "672 -328 176" "classname" "item_health" } { "classname" "item_health" "origin" "776 -192 176" } { "origin" "784 312 -176" "classname" "item_shells" } { "spawnflags" "256" "angle" "315" "origin" "-26 1094 152" "classname" "monster_ogre" } { "classname" "monster_ogre" "origin" "406 1094 152" "angle" "225" "spawnflags" "768" } { "spawnflags" "1" "origin" "-200 1128 128" "classname" "item_rockets" } { "origin" "-550 -478 212" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "-550 -166 212" } { "origin" "-214 -326 252" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-120 -176 192" "classname" "light" } { "classname" "light" "origin" "56 -176 192" "light" "150" } { "origin" "-816 1488 -80" "classname" "item_health" } { "classname" "item_health" "origin" "-752 1488 -80" } { "spawnflags" "1536" "origin" "-688 1488 -80" "classname" "item_health" } { "spawnflags" "1" "origin" "176 1264 -160" "classname" "item_shells" } { "classname" "func_door" "angle" "270" "targetname" "t31" "wait" "-1" "spawnflags" "2048" "model" "*32" } { "wait" "-1" "sounds" "1" "speed" "300" "classname" "func_door" "angle" "90" "model" "*33" } { "speed" "300" "classname" "func_door" "angle" "270" "wait" "-1" "model" "*34" } { "classname" "item_health" "origin" "400 1464 -160" "spawnflags" "1024" } { "classname" "item_health" "origin" "-1136 528 -128" } { "classname" "monster_ogre" "origin" "-370 -218 88" "targetname" "t31" "spawnflags" "1" } { "classname" "monster_hell_knight" "origin" "-64 -176 152" "angle" "0" "spawnflags" "768" "targetname" "t31" } { "classname" "item_health" "origin" "-488 -480 64" } { "classname" "light" "origin" "680 -1600 -160" } { "classname" "item_spikes" "origin" "984 -192 192" "spawnflags" "1" } { "classname" "monster_ogre" "origin" "-490 -410 88" "angle" "45" } { "classname" "trigger_multiple" "target" "t32" "wait" "1" "spawnflags" "1024" "targetname" "t44" "model" "*35" } { "classname" "trigger_multiple" "target" "t33" "wait" "1" "spawnflags" "1024" "model" "*36" } { "classname" "item_rockets" "origin" "-96 272 -384" } { "classname" "weapon_supernailgun" "origin" "-1200 208 -112" "spawnflags" "1792" } { "classname" "func_door_secret" "spawnflags" "2051" "targetname" "t36" "angle" "90" "model" "*37" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "light" "origin" "-1288 640 -80" "light" "160" } { "light" "160" "origin" "-1288 128 -80" "classname" "light" } { "classname" "light" "origin" "-1288 264 -80" "light" "160" } { "light" "160" "origin" "-1288 392 -80" "classname" "light" } { "classname" "light" "origin" "-1288 520 -80" "light" "160" } { "classname" "func_door_secret" "targetname" "t36" "angle" "270" "spawnflags" "2049" "model" "*38" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "item_armor2" "origin" "1128 600 -176" "spawnflags" "1024" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "11" "targetname" "t34" "model" "*39" } { "classname" "light_torch_small_walltorch" "origin" "-246 -1310 -204" "light" "250" } { "classname" "trigger_once" "target" "t34" "model" "*40" } { "classname" "item_spikes" "origin" "-240 -1288 -312" "spawnflags" "1" } { "spawnflags" "1" "origin" "-240 -1368 -312" "classname" "item_spikes" } { "sounds" "3" "classname" "func_door" "angle" "-2" "spawnflags" "3585" "wait" "90" "targetname" "t6" "model" "*41" } { "sounds" "3" "classname" "func_door" "targetname" "t6" "spawnflags" "3585" "angle" "-2" "wait" "90" "model" "*42" } { "sounds" "3" "classname" "func_door" "angle" "0" "spawnflags" "1537" "targetname" "t6" "wait" "90" "model" "*43" } { "classname" "light_torch_small_walltorch" "origin" "-42 1642 -108" "light" "200" } { "classname" "item_health" "origin" "-72 560 -384" } { "origin" "-152 560 -384" "classname" "item_health" } { "targetname" "t31" "spawnflags" "513" "angle" "90" "origin" "848 -376 216" "classname" "monster_hell_knight" } { "targetname" "t31" "spawnflags" "768" "origin" "-512 -248 88" "classname" "monster_shambler" } { "spawnflags" "768" "angle" "180" "origin" "382 1246 -136" "classname" "monster_ogre" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "190 1438 -136" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "640 -1664 -312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-400 -240 88" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-216 1088 152" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-512 792 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "888 624 -152" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "-96 1456 -168" "angle" "0" } { "spawnflags" "2048" "classname" "func_wall" "model" "*44" } { "spawnflags" "2048" "classname" "func_wall" "model" "*45" } { "classname" "func_plat" "height" "192" "sounds" "2" "model" "*46" } { "classname" "item_health" "origin" "-1184 72 -352" } { "classname" "light" "origin" "-952 1408 80" "light" "200" } { "classname" "light" "origin" "-1120 1176 48" "light" "250" } { "classname" "light" "origin" "-104 288 -344" "light" "200" } { "classname" "light" "origin" "-512 376 -304" "light" "200" } { "classname" "trigger_multiple" "target" "t35" "model" "*47" } { "classname" "light" "origin" "192 -376 -16" "light" "150" } { "classname" "light" "origin" "192 -328 176" "light" "150" } { "classname" "trigger_multiple" "target" "t32" "spawnflags" "768" "wait" "0.5" "model" "*48" } { "classname" "trigger_multiple" "spawnflags" "768" "wait" "0.5" "target" "t33" "targetname" "t44" "model" "*49" } { "origin" "-1176 112 -112" "classname" "item_rockets" } { "origin" "-736 544 -280" "classname" "item_armorInv" } { "classname" "func_button" "angle" "180" "target" "t36" "model" "*50" } { "classname" "info_null" "origin" "-1332 1116 -36" "targetname" "t37" } { "classname" "light" "origin" "-1296 1120 -32" "target" "t37" "angle" "60" } { "spawnflags" "2048" "classname" "func_wall" "model" "*51" } { "spawnflags" "2048" "classname" "func_wall" "model" "*52" } { "classname" "light" "origin" "192 -152 -344" "light" "160" } { "classname" "light" "origin" "192 32 -344" "light" "160" } { "classname" "light" "origin" "32 168 -344" "light" "140" } { "light" "160" "origin" "-16 408 -344" "classname" "light" } { "classname" "light" "origin" "192 368 -344" "light" "140" } { "light" "140" "origin" "448 368 -344" "classname" "light" } { "classname" "light" "origin" "528 592 -344" "light" "200" } { "light" "200" "origin" "408 808 -344" "classname" "light" } { "classname" "light" "origin" "192 832 -344" "light" "200" } { "light" "200" "origin" "-40 728 -344" "classname" "light" } { "classname" "light" "origin" "-168 632 -344" "light" "200" } { "light" "200" "origin" "-160 976 -344" "classname" "light" } { "classname" "light" "origin" "592 456 -344" "light" "200" } { "classname" "light" "origin" "192 352 -232" "light" "200" } { "light" "200" "origin" "32 264 -232" "classname" "light" } { "classname" "light" "origin" "-184 432 -336" "light" "160" } { "light" "160" "origin" "-192 144 -336" "classname" "light" } { "classname" "light" "origin" "-776 280 -312" "light" "200" } { "light" "200" "origin" "-864 432 -312" "classname" "light" } { "classname" "light" "origin" "-1096 432 -312" "light" "200" } { "light" "200" "origin" "-1168 272 -312" "classname" "light" } { "classname" "light" "origin" "-944 64 -312" "light" "200" } { "light" "200" "origin" "-664 136 -312" "classname" "light" } { "light" "200" "origin" "-1112 592 -96" "classname" "light" } { "classname" "light" "origin" "-1040 1256 -32" "light" "150" } { "light" "200" "origin" "-880 1128 80" "classname" "light" } { "classname" "light" "origin" "-712 1400 -16" "light" "200" } { "classname" "light" "origin" "104 -600 -224" "light" "200" } { "light" "200" "origin" "280 -600 -224" "classname" "light" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*53" } { "light" "160" "origin" "-312 808 -72" "classname" "light" } { "light" "200" "origin" "192 1776 -312" "classname" "light" } { "classname" "light" "origin" "-392 568 -56" "light" "160" } { "classname" "light" "origin" "-1224 1504 8" "light" "170" } { "light" "170" "origin" "-1304 1392 8" "classname" "light" } { "classname" "light" "origin" "-640 1168 -8" "light" "170" } { "light" "160" "origin" "352 -1248 56" "classname" "light" } { "classname" "light" "origin" "40 -1248 56" "light" "160" } { "light" "160" "origin" "192 -1216 -176" "classname" "light" } { "classname" "light" "origin" "24 -1376 -232" "light" "160" } { "light" "160" "origin" "224 -1624 -232" "classname" "light" } { "classname" "light" "origin" "368 -1392 -232" "light" "160" } { "classname" "light" "origin" "8 -464 -40" "light" "140" } { "light" "140" "origin" "384 -464 -40" "classname" "light" } { "classname" "light" "origin" "-544 800 -56" "light" "140" } { "classname" "trigger_secret" "model" "*54" } { "classname" "trigger_secret" "model" "*55" } { "light" "200" "origin" "760 1856 -40" "classname" "light" } { "classname" "light" "origin" "760 1664 -40" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "538 1762 -124" "style" "1" "light" "200" } { "style" "1" "classname" "light_torch_small_walltorch" "origin" "850 1930 -124" "light" "200" } { "origin" "850 1618 -124" "classname" "light_torch_small_walltorch" "style" "1" "light" "200" } { "classname" "light" "origin" "912 1856 -40" "light" "200" } { "light" "200" "origin" "912 1664 -40" "classname" "light" } { "light" "200" "origin" "1064 1776 -172" "classname" "light" } { "light" "200" "origin" "1080 1856 -40" "classname" "light" } { "classname" "light" "origin" "1080 1664 -40" "light" "200" } { "classname" "light" "origin" "1176 1776 -172" "light" "200" } { "light" "170" "origin" "672 1768 -296" "classname" "light" } { "target" "t38" "classname" "trigger_teleport" "model" "*56" } { "targetname" "t38" "origin" "1144 1776 -88" "classname" "info_teleport_destination" } { "map" "e2m7" "classname" "trigger_changelevel" "model" "*57" } { "light" "160" "origin" "840 1768 -200" "classname" "light" } { "light" "140" "origin" "408 608 -344" "classname" "light" } { "classname" "item_spikes" "origin" "-16 240 -160" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "-1256 1448 -80" } { "origin" "432 1160 152" "classname" "item_artifact_super_damage" } { "message" "The portal lies beyond..." "targetname" "t40" "wait" "-1" "speed" "20" "sounds" "4" "angle" "-2" "classname" "func_door" "model" "*58" } { "origin" "432 1672 -368" "classname" "item_armor2" } { "target" "t39" "sounds" "1" "wait" "-1" "classname" "func_button" "model" "*59" } { "message" "The underwater barrier is lowered..." "target" "t40" "targetname" "t39" "spawnflags" "1" "classname" "trigger_once" "model" "*60" } { "classname" "trigger_secret" "model" "*61" } { "light" "200" "origin" "-128 -704 -224" "classname" "light" } { "classname" "light" "origin" "512 -704 -224" "light" "200" } { "light" "200" "origin" "192 -832 -224" "classname" "light" } { "mangle" "20 240 0" "origin" "400 1048 240" "classname" "info_intermission" } { "mangle" "20 145 0" "origin" "-160 144 64" "classname" "info_intermission" } { "mangle" "-20 45 0" "origin" "-320 -824 -144" "classname" "info_intermission" } { "classname" "func_wall" "spawnflags" "1792" "model" "*62" } { "classname" "item_artifact_super_damage" "origin" "928 1768 -240" "spawnflags" "1792" } { "classname" "light" "origin" "8 1800 -120" "light" "220" } { "classname" "weapon_lightning" "origin" "1216 1784 -264" "spawnflags" "1792" } { "classname" "item_cells" "origin" "880 1648 -264" "spawnflags" "1793" } { "spawnflags" "1793" "origin" "880 1864 -264" "classname" "item_cells" } { "spawnflags" "1792" "classname" "func_wall" "model" "*63" } { "spawnflags" "1792" "classname" "func_wall" "model" "*64" } { "spawnflags" "1792" "classname" "func_wall" "model" "*65" } { "classname" "info_player_coop" "origin" "664 -1520 -312" "angle" "180" } { "angle" "180" "origin" "592 -1600 -312" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "680 -1712 -312" "angle" "180" } { "classname" "air_bubbles" "origin" "720 1384 -320" } { "classname" "light" "origin" "680 1376 -312" } { "classname" "func_door_secret" "angle" "180" "spawnflags" "2" "targetname" "t41" "model" "*66" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "trigger_multiple" "target" "t41" "model" "*67" } { "origin" "688 1176 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "480 1408 -312" } { "origin" "448 1552 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 -312" } { "origin" "840 1080 -160" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 0" } { "classname" "light" "origin" "840 1080 232" "light" "200" } { "classname" "item_artifact_envirosuit" "origin" "576 1440 -344" } { "classname" "item_artifact_invulnerability" "origin" "544 1248 -344" } { "light" "200" "origin" "840 936 232" "classname" "light" } { "classname" "light" "origin" "840 760 232" "light" "150" } { "light" "120" "origin" "808 600 232" "classname" "light" } { "classname" "item_health" "origin" "824 960 152" } { "origin" "848 880 152" "classname" "item_health" } { "classname" "item_health" "origin" "808 768 152" } { "classname" "trigger_multiple" "message" "Welcome to the Well of Wishes!" "wait" "5" "sounds" "1" "model" "*68" } { "classname" "trigger_multiple" "sounds" "1" "wait" "3" "message" "The Dopefish Lives!" "model" "*69" } { "classname" "func_wall" "spawnflags" "1792" "model" "*70" } { "classname" "trigger_secret" "model" "*71" } { "spawnflags" "1" "origin" "-1312 1392 -80" "classname" "item_spikes" } { "classname" "func_wall" "spawnflags" "1792" "model" "*72" } { "classname" "light" "origin" "-544 600 -248" "light" "200" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "194 -214 196" "spawnflags" "2048" } { "light" "200" "origin" "352 984 -328" "classname" "light" } { "classname" "light" "origin" "96 976 -328" "light" "200" } { "classname" "light" "origin" "640 776 -336" "light" "200" } { "classname" "func_plat" "spawnflags" "1" "model" "*73" } { "classname" "monster_fish" "origin" "656 352 -336" "spawnflags" "256" } { "spawnflags" "256" "origin" "432 424 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "296 968 -336" "spawnflags" "256" } { "origin" "-48 800 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "-896 248 -312" } { "origin" "-744 328 -312" "classname" "monster_fish" } { "classname" "path_corner" "origin" "272 -1504 -264" "targetname" "t42" "target" "t43" } { "origin" "56 -1352 -264" "classname" "path_corner" "target" "t42" "targetname" "t43" } { "classname" "item_health" "origin" "312 -1336 -272" } { "origin" "544 -1488 -336" "classname" "item_health" } { "classname" "item_health" "origin" "312 1464 -160" } { "origin" "56 1488 -192" "classname" "item_health" } { "classname" "trigger_once" "killtarget" "t44" "spawnflags" "3072" "model" "*74" } { "classname" "item_rockets" "origin" "216 648 120" "spawnflags" "1" } { "classname" "item_shells" "origin" "136 648 120" "spawnflags" "1" } quakespasm-0.93.0/Misc/qs_pak/maps/e2m2.ent.orig0000644000000000000000000006457212425501423020014 0ustar rootroot{ "message" "the Ogre Citadel" "sounds" "8" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" } { "origin" "160 -160 120" "classname" "light" } { "angle" "90" "origin" "-256 -1952 280" "classname" "info_player_start" } { "classname" "light" "origin" "160 -392 248" "light" "200" } { "classname" "light" "origin" "160 -648 184" } { "classname" "light" "origin" "-56 -392 248" "light" "200" } { "classname" "light" "origin" "376 -392 248" "light" "200" } { "classname" "light" "origin" "288 -416 -72" "light" "200" } { "classname" "light" "origin" "32 -416 -72" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "10 -270 148" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "314 -270 148" "light" "250" } { "classname" "light" "origin" "-264 -440 248" "light" "200" } { "classname" "light" "origin" "584 -440 248" "light" "200" } { "classname" "light" "origin" "544 -648 248" } { "light" "150" "origin" "648 -456 -184" "classname" "light" } { "origin" "376 -800 184" "classname" "light" } { "classname" "light" "origin" "-152 -752 184" "light" "250" } { "classname" "light" "origin" "-232 -592 184" "light" "250" } { "classname" "light" "origin" "160 112 184" } { "classname" "light" "origin" "160 352 120" } { "classname" "light" "origin" "160 216 -48" "light" "120" } { "classname" "light" "origin" "160 16 -48" "light" "120" } { "classname" "light" "origin" "160 544 144" "light" "225" } { "classname" "light" "origin" "480 576 88" } { "classname" "light" "origin" "480 448 88" } { "classname" "light" "origin" "480 576 168" "light" "250" } { "classname" "light" "origin" "480 448 168" "light" "250" } { "classname" "light" "origin" "160 896 312" "light" "350" } { "classname" "light" "origin" "288 896 312" "light" "100" } { "classname" "light" "origin" "32 896 312" "light" "100" } { "classname" "light" "origin" "160 1008 312" "light" "100" } { "classname" "light" "origin" "160 784 312" "light" "100" } { "classname" "light" "origin" "392 120 184" "light" "350" } { "classname" "light" "origin" "568 208 184" "light" "350" } { "classname" "light" "origin" "720 480 184" } { "classname" "light" "origin" "640 632 184" } { "classname" "light" "origin" "472 1152 56" } { "classname" "light" "origin" "512 896 152" } { "origin" "800 800 184" "classname" "light" } { "light" "200" "origin" "632 1264 -40" "classname" "light" } { "origin" "800 1032 184" "classname" "light" } { "origin" "760 1472 64" "classname" "light" } { "light" "200" "origin" "544 1416 56" "classname" "light" } { "origin" "672 1256 184" "classname" "light" "light" "200" } { "light" "200" "origin" "1024 1272 184" "classname" "light" } { "light" "200" "origin" "992 1440 184" "classname" "light" } { "origin" "1240 488 184" "classname" "light" } { "origin" "1280 136 176" "classname" "light" } { "origin" "160 1304 136" "classname" "light" } { "origin" "160 1648 256" "classname" "light" "light" "200" } { "origin" "240 1600 256" "classname" "light" "light" "200" } { "origin" "16 1616 168" "classname" "light" "light" "200" } { "light" "200" "origin" "-120 1304 40" "classname" "light" } { "origin" "-352 1144 16" "classname" "light" "light" "250" } { "origin" "-56 1096 64" "classname" "light" } { "light" "200" "origin" "-56 1152 280" "classname" "light" } { "light" "350" "origin" "-440 1144 200" "classname" "light" } { "light" "200" "origin" "-352 1336 -72" "classname" "light" } { "origin" "-488 368 56" "classname" "light" "light" "250" } { "light" "350" "origin" "-488 896 136" "classname" "light" } { "origin" "-216 896 184" "classname" "light" "light" "250" } { "origin" "-128 536 168" "classname" "light" "light" "200" } { "light" "150" "origin" "-104 480 32" "classname" "light" } { "light" "150" "origin" "-208 936 56" "classname" "light" } { "light" "150" "origin" "-208 736 32" "classname" "light" } { "origin" "-344 64 184" "classname" "light" } { "light" "350" "origin" "-648 384 184" "classname" "light" } { "light" "350" "origin" "-488 688 184" "classname" "light" } { "light" "150" "origin" "-680 496 -36" "classname" "light" } { "light" "350" "origin" "-600 1104 96" "classname" "light" } { "origin" "-824 896 168" "classname" "light" } { "origin" "-896 600 160" "classname" "light" } { "target" "t1" "classname" "trigger_teleport" "model" "*1" } { "spawnflags" "1792" "targetname" "t1" "origin" "-448 264 -56" "classname" "info_teleport_destination" "angle" "45" } { "light" "250" "origin" "-168 1320 160" "classname" "light" } { "origin" "-24 896 184" "classname" "light" } { "origin" "-104 896 184" "classname" "light" "light" "250" } { "spawnflags" "2" "origin" "-680 880 48" "classname" "item_health" } { "angle" "90" "origin" "-896 512 24" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "-184 560 128" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "160 1640 160" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "1272 112 80" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "176 -784 24" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "208 -160 0" "classname" "item_rockets" } { "origin" "64 464 0" "classname" "item_health" } { "origin" "120 464 0" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 840 64" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 952 64" "classname" "item_health" } { "spawnflags" "9" "origin" "568 880 0" "classname" "item_weapon" } { "origin" "456 1104 -88" "classname" "item_health" } { "spawnflags" "1" "origin" "664 1272 -88" "classname" "item_shells" } { "angle" "225" "origin" "888 1312 120" "classname" "info_player_deathmatch" } { "origin" "1328 168 56" "classname" "item_health" } { "origin" "1328 128 56" "classname" "item_health" } { "origin" "1328 208 56" "classname" "item_health" } { "spawnflags" "1" "origin" "816 1472 0" "classname" "item_shells" } { "light" "200" "origin" "-64 -120 -56" "classname" "light" } { "light" "200" "origin" "-296 -160 -56" "classname" "light" } { "light" "200" "origin" "-296 -208 -272" "classname" "light" } { "classname" "func_plat" "model" "*2" } { "spawnflags" "2" "origin" "-192 -176 -80" "classname" "item_health" } { "light" "350" "origin" "-816 128 184" "classname" "light" } { "light" "350" "origin" "-664 -80 184" "classname" "light" } { "light" "250" "origin" "-656 112 32" "classname" "light" } { "origin" "-352 208 56" "classname" "light" "light" "250" } { "origin" "-184 504 24" "classname" "light" "light" "250" } { "classname" "item_armor1" "origin" "400 -888 0" } { "spawnflags" "1792" "classname" "item_armorInv" "origin" "-104 448 104" } { "classname" "item_armor2" "origin" "-680 496 32" } { "classname" "weapon_grenadelauncher" "origin" "168 1608 136" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "1232 176 56" "spawnflags" "1792" } { "classname" "weapon_supernailgun" "origin" "-960 944 40" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "752 1464 0" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "160 120 24" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-224 792 104" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-640 120 -56" "spawnflags" "1792" } { "light" "200" "origin" "-70 -1126 212" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "186 -1134 148" } { "light" "200" "origin" "138 -966 100" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "-150 -1318 260" } { "light" "150" "origin" "184 -880 72" "classname" "light" } { "style" "6" "light" "200" "origin" "-46 -1742 340" "classname" "light_torch_small_walltorch" } { "style" "1" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-486 -1726 340" } { "origin" "-272 -1544 384" "classname" "light" "light" "200" } { "style" "6" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-414 -2006 340" } { "style" "1" "light" "200" "origin" "-134 -1958 340" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-472 -1840 328" "classname" "light" } { "classname" "light" "origin" "-72 -1592 328" "light" "150" } { "light" "150" "origin" "-320 -1424 264" "classname" "light" } { "light" "150" "origin" "-256 -1952 280" "classname" "light" } { "light" "200" "classname" "light" "origin" "-272 -1720 312" } { "origin" "-232 -1280 168" "classname" "path_corner" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-64 -1176 120" "targetname" "t6" "target" "t7" } { "origin" "152 -1104 56" "classname" "path_corner" "targetname" "t7" "target" "t8" } { "classname" "path_corner" "origin" "192 -952 8" "targetname" "t8" "target" "t9" } { "origin" "184 -808 8" "classname" "path_corner" "target" "t3" "targetname" "t9" } { "classname" "path_corner" "origin" "512 -776 8" "targetname" "t3" "target" "t10" } { "origin" "-200 -648 8" "classname" "path_corner" "targetname" "t4" "target" "t11" } { "classname" "monster_knight" "origin" "-360 -1616 232" "target" "t5" "angle" "90" } { "origin" "512 -648 8" "classname" "path_corner" "targetname" "t10" "target" "t4" } { "classname" "path_corner" "origin" "-200 -776 8" "targetname" "t11" "target" "t3" } { "classname" "monster_knight" "origin" "24 -632 24" "spawnflags" "256" "target" "t4" } { "classname" "monster_knight" "origin" "336 -752 24" "spawnflags" "256" "target" "t3" } { "classname" "monster_knight" "origin" "56 -712 24" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "160 -712 24" "classname" "monster_knight" } { "classname" "monster_knight" "origin" "264 -712 24" "angle" "270" "spawnflags" "768" } { "classname" "item_health" "origin" "-432 -1640 208" "spawnflags" "1" } { "classname" "item_shells" "origin" "-352 -592 0" } { "classname" "func_door" "angle" "90" "spawnflags" "1" "targetname" "t13" "wait" "-1" "sounds" "3" "dmg" "100" "model" "*3" } { "health" "1" "angle" "90" "classname" "func_button" "target" "t12" "wait" "-1" "sounds" "1" "model" "*4" } { "classname" "func_button" "angle" "90" "health" "1" "target" "t12" "wait" "-1" "sounds" "1" "model" "*5" } { "classname" "trigger_counter" "targetname" "t12" "count" "2" "target" "t13" "model" "*6" } { "classname" "monster_demon1" "origin" "160 -128 24" "angle" "270" "targetname" "t12" } { "classname" "light" "origin" "-8 -288 64" "light" "200" } { "light" "200" "origin" "328 -288 64" "classname" "light" } { "light" "200" "origin" "-296 -448 -248" "classname" "light" } { "classname" "light" "origin" "160 -368 -248" "light" "200" } { "light" "200" "origin" "616 -448 -248" "classname" "light" } { "classname" "light" "origin" "408 -456 -248" "light" "200" } { "light" "200" "origin" "-40 -424 -248" "classname" "light" } { "classname" "light" "origin" "-280 -448 32" "light" "200" } { "light" "200" "origin" "632 -424 32" "classname" "light" } { "classname" "light" "origin" "160 -480 8" "light" "150" } { "classname" "func_door" "angle" "180" "targetname" "t12" "sounds" "3" "speed" "200" "wait" "-1" "lip" "-2" "model" "*7" } { "classname" "func_door" "angle" "0" "speed" "200" "wait" "-1" "lip" "-2" "model" "*8" } { "classname" "light" "origin" "160 -304 112" "light" "170" } { "classname" "light" "origin" "160 640 104" "light" "200" } { "classname" "func_door" "angle" "-1" "targetname" "t14" "sounds" "1" "model" "*9" } { "classname" "trigger_multiple" "target" "t14" "wait" "10" "model" "*10" } { "targetname" "t28" "classname" "func_door" "angle" "-1" "wait" "-1" "sounds" "1" "speed" "200" "spawnflags" "2048" "model" "*11" } { "angle" "270" "classname" "func_button" "target" "t15" "wait" "-1" "lip" "2" "sounds" "1" "spawnflags" "2048" "model" "*12" } { "classname" "light" "origin" "-296 432 0" "light" "200" } { "light" "200" "origin" "-312 416 152" "classname" "light" } { "classname" "light" "origin" "-184 320 146" "light" "200" } { "classname" "item_key2" "origin" "-552 192 -40" "sounds" "1" "spawnflags" "2048" } { "classname" "item_spikes" "origin" "-680 88 -48" } { "classname" "func_door" "angle" "-2" "spawnflags" "33" "speed" "10" "sounds" "3" "wait" "-1" "targetname" "t16" "dmg" "100" "model" "*13" } { "sounds" "3" "classname" "func_door" "angle" "90" "spawnflags" "2056" "wait" "-1" "model" "*14" } { "spawnflags" "2056" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*15" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t16" "lip" "12" "model" "*16" } { "classname" "light" "origin" "888 1080 -104" "light" "160" } { "light" "160" "origin" "888 888 -104" "classname" "light" } { "classname" "light" "origin" "880 696 -104" "light" "160" } { "light" "160" "origin" "696 888 -104" "classname" "light" } { "classname" "light" "origin" "696 1080 -104" "light" "160" } { "light" "200" "origin" "696 672 -104" "classname" "light" } { "classname" "light" "origin" "568 512 -104" "light" "200" } { "light" "160" "origin" "840 504 -104" "classname" "light" } { "classname" "light" "origin" "832 328 -104" "light" "160" } { "classname" "light" "origin" "936 512 96" "light" "200" } { "classname" "light" "origin" "-248 1080 56" "light" "150" } { "classname" "light" "origin" "888 1264 176" "light" "170" } { "classname" "func_door" "angle" "-2" "targetname" "t17" "sounds" "1" "model" "*17" } { "classname" "trigger_multiple" "target" "t17" "wait" "5" "model" "*18" } { "classname" "light" "origin" "160 120 -24" "light" "160" } { "spawnflags" "256" "classname" "trigger_multiple" "target" "t18" "targetname" "t23" "model" "*19" } { "wait" "0.5" "classname" "trap_spikeshooter" "origin" "88 368 40" "angle" "0" "targetname" "t18" } { "classname" "func_wall" "spawnflags" "2048" "model" "*20" } { "classname" "monster_demon1" "origin" "-160 608 128" "targetname" "t19" } { "classname" "trigger_once" "target" "t19" "model" "*21" } { "classname" "light" "origin" "-528 512 -104" "light" "200" } { "light" "200" "origin" "-344 592 -104" "classname" "light" } { "classname" "light" "origin" "-336 760 -104" "light" "200" } { "light" "200" "origin" "-432 856 -104" "classname" "light" } { "classname" "monster_ogre" "origin" "-416 440 -40" "spawnflags" "1536" "target" "t20" } { "classname" "monster_shambler" "origin" "-272 296 -40" "spawnflags" "256" "target" "t20" } { "classname" "path_corner" "origin" "-328 272 -56" "targetname" "t20" "target" "t21" } { "origin" "-400 480 -56" "classname" "path_corner" "target" "t20" "targetname" "t21" } { "classname" "item_health" "origin" "-600 144 -64" } { "classname" "weapon_supershotgun" "origin" "440 512 0" "spawnflags" "2048" } { "classname" "light" "origin" "-120 176 184" "light" "250" } { "origin" "-96 0 184" "classname" "light" "light" "200" } { "classname" "light" "origin" "312 116 -48" "light" "200" } { "classname" "path_corner" "origin" "8 112 32" "target" "t25" "targetname" "t24" } { "origin" "312 112 32" "classname" "path_corner" "targetname" "t25" "target" "t24" } { "classname" "monster_ogre" "origin" "112 112 48" "target" "t24" "spawnflags" "256" } { "classname" "item_shells" "origin" "88 -160 0" "spawnflags" "1" } { "light" "250" "classname" "light" "origin" "-352 144 56" } { "classname" "monster_demon1" "origin" "-80 -440 -296" "spawnflags" "256" "target" "t26" } { "classname" "path_corner" "origin" "-216 -456 -312" "targetname" "t26" "target" "t27" } { "origin" "536 -448 -312" "classname" "path_corner" "target" "t26" "targetname" "t27" } { "light" "200" "origin" "320 232 -104" "classname" "light" } { "classname" "light" "origin" "312 0 -104" "light" "200" } { "light" "160" "origin" "-8 0 -104" "classname" "light" } { "classname" "light" "origin" "-8 232 -104" "light" "200" } { "light" "200" "origin" "-16 112 -104" "classname" "light" } { "classname" "light" "origin" "-248 -8 -104" "light" "200" } { "light" "200" "origin" "-472 120 -104" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "-1" "targetname" "t15" "sounds" "1" "spawnflags" "2048" "model" "*22" } { "classname" "func_door" "angle" "-2" "spawnflags" "2081" "targetname" "t28" "wait" "10" "speed" "200" "sounds" "1" "model" "*23" } { "classname" "trigger_once" "target" "t28" "model" "*24" } { "classname" "trigger_once" "target" "t12" "model" "*25" } { "classname" "monster_ogre" "origin" "160 1432 128" "angle" "270" } { "classname" "monster_ogre" "origin" "-216 784 128" "angle" "90" } { "classname" "info_teleport_destination" "origin" "-360 888 24" "spawnflags" "2048" "targetname" "t1" } { "classname" "monster_zombie" "origin" "-288 -232 -296" "angle" "270" } { "classname" "monster_zombie" "origin" "168 -8 -104" "angle" "90" } { "classname" "monster_ogre" "origin" "648 1232 -64" "angle" "180" } { "sounds" "1" "classname" "func_door" "angle" "-2" "targetname" "t29" "model" "*26" } { "classname" "monster_ogre" "origin" "520 752 24" "angle" "90" "spawnflags" "256" "targetname" "t29" } { "classname" "trigger_once" "target" "t29" "model" "*27" } { "classname" "weapon_nailgun" "origin" "152 1608 136" "spawnflags" "2048" } { "classname" "path_corner" "origin" "544 896 8" "targetname" "t30" "target" "t31" } { "origin" "56 896 72" "classname" "path_corner" "target" "t30" "targetname" "t31" } { "classname" "path_corner" "origin" "168 552 8" "targetname" "t32" "target" "t33" } { "origin" "168 1392 8" "classname" "path_corner" "target" "t32" "targetname" "t33" } { "classname" "monster_knight" "origin" "240 584 24" "target" "t32" } { "classname" "monster_knight" "origin" "504 960 24" "target" "t30" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-16 896 88" "angle" "0" } { "classname" "monster_knight" "origin" "256 1224 24" "angle" "225" "spawnflags" "256" } { "target" "t49" "origin" "-24 1064 16" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "45" "origin" "-184 1080 128" "classname" "monster_ogre" } { "angle" "45" "origin" "-256 1216 128" "classname" "monster_knight" } { "targetname" "t16" "angle" "270" "origin" "784 520 56" "classname" "monster_demon1" } { "classname" "path_corner" "origin" "-352 888 16" "targetname" "t34" "target" "t35" } { "origin" "-120 888 8" "classname" "path_corner" "target" "t34" "targetname" "t35" } { "classname" "monster_ogre" "origin" "-184 912 24" "target" "t34" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "-720 896 64" } { "classname" "monster_knight" "origin" "-344 760 24" "angle" "135" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-392 584 24" "angle" "90" "spawnflags" "256" } { "angle" "90" "classname" "monster_knight" "origin" "-528 528 24" "spawnflags" "768" } { "targetname" "t37" "target" "t36" "origin" "-896 968 48" "classname" "path_corner" } { "target" "t37" "targetname" "t36" "classname" "path_corner" "origin" "-896 568 48" } { "spawnflags" "256" "target" "t36" "origin" "-840 600 24" "classname" "monster_ogre" } { "style" "32" "targetname" "t15" "light" "200" "origin" "-56 304 152" "classname" "light" } { "dmg" "100" "speed" "200" "targetname" "t15" "target" "t38" "classname" "func_train" "model" "*28" } { "target" "t39" "targetname" "t40" "origin" "-216 280 104" "classname" "path_corner" } { "target" "t40" "targetname" "t38" "classname" "path_corner" "origin" "-16 280 104" } { "target" "t38" "wait" "-1" "targetname" "t39" "origin" "-16 280 104" "classname" "path_corner" } { "angle" "180" "origin" "680 1472 24" "classname" "monster_ogre" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "864 1448 24" "classname" "monster_knight" } { "target" "t42" "targetname" "t41" "origin" "1024 1264 104" "classname" "path_corner" } { "targetname" "t42" "target" "t41" "classname" "path_corner" "origin" "704 1264 104" } { "target" "t41" "origin" "1056 1320 120" "classname" "monster_knight" } { "spawnflags" "257" "origin" "552 1280 128" "classname" "monster_knight" } { "spawnflags" "1" "origin" "432 1280 128" "classname" "monster_knight" } { "spawnflags" "2048" "angle" "0" "wait" "-1" "sounds" "0" "health" "1" "target" "t28" "classname" "func_button" "model" "*29" } { "origin" "-72 296 104" "classname" "item_health" } { "classname" "item_health" "origin" "-64 504 104" } { "origin" "-312 192 -64" "classname" "item_health" } { "spawnflags" "1" "origin" "-80 384 -64" "classname" "item_spikes" } { "origin" "-224 976 104" "classname" "item_shells" } { "target" "t666" "classname" "trigger_multiple" "wait" "10" "model" "*30" } { "target" "t45" "targetname" "t44" "origin" "984 568 8" "classname" "path_corner" } { "target" "t46" "targetname" "t45" "classname" "path_corner" "origin" "976 464 8" } { "target" "t47" "targetname" "t46" "origin" "1224 448 40" "classname" "path_corner" } { "target" "t44" "targetname" "t43" "classname" "path_corner" "origin" "1272 520 40" } { "target" "t48" "targetname" "t47" "classname" "path_corner" "origin" "1224 144 64" } { "targetname" "t48" "target" "t43" "origin" "1344 120 64" "classname" "path_corner" } { "target" "t45" "origin" "968 520 24" "classname" "monster_ogre" } { "spawnflags" "256" "target" "t43" "origin" "1312 328 80" "classname" "monster_knight" } { "spawnflags" "768" "target" "t47" "origin" "1240 296 80" "classname" "monster_ogre" } { "origin" "-360 1064 -8" "classname" "weapon_grenadelauncher" } { "target" "t50" "targetname" "t49" "origin" "-16 1168 0" "classname" "path_corner" } { "targetname" "t50" "target" "t49" "classname" "path_corner" "origin" "-232 1168 0" } { "origin" "1448 -128 -56" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "270" "model" "*31" } { "wait" "-1" "targetname" "t52" "sounds" "3" "classname" "func_door" "angle" "90" "model" "*32" } { "classname" "light" "origin" "1984 -184 288" "light" "350" } { "classname" "light" "origin" "1760 -312 -184" "light" "200" } { "origin" "1832 -64 -184" "classname" "light" "light" "200" } { "light" "160" "origin" "1808 -296 -24" "classname" "light" } { "classname" "light" "origin" "1224 -8 -184" "light" "160" } { "light" "160" "origin" "1240 -208 -184" "classname" "light" } { "classname" "light" "origin" "1680 -104 -184" "light" "160" } { "light" "160" "origin" "1584 -304 -184" "classname" "light" } { "origin" "1592 -72 288" "classname" "light" } { "classname" "light" "origin" "1328 -232 288" } { "classname" "light" "origin" "1280 -16 327" "light" "250" } { "classname" "light" "origin" "1792 -152 88" "light" "160" } { "light" "160" "origin" "1632 -296 88" "classname" "light" } { "origin" "1696 -280 288" "classname" "light" } { "classname" "light" "origin" "1240 -224 -8" "light" "200" } { "classname" "func_wall" "spawnflags" "3072" "model" "*33" } { "classname" "func_wall" "spawnflags" "3072" "model" "*34" } { "classname" "func_wall" "spawnflags" "3072" "model" "*35" } { "classname" "func_wall" "spawnflags" "3072" "model" "*36" } { "classname" "func_wall" "spawnflags" "3072" "model" "*37" } { "classname" "monster_ogre" "origin" "1992 -192 200" "angle" "180" "spawnflags" "256" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t51" "model" "*38" } { "classname" "trigger_multiple" "target" "t51" "model" "*39" } { "classname" "func_plat" "model" "*40" } { "classname" "light" "origin" "1480 56 -168" "light" "160" } { "origin" "1432 280 -64" "classname" "light" "light" "160" } { "light" "160" "classname" "light" "origin" "1424 280 96" } { "light" "160" "origin" "1472 232 -168" "classname" "light" } { "classname" "monster_zombie" "origin" "1592 -24 160" "angle" "180" } { "classname" "monster_zombie" "origin" "1432 -304 112" "angle" "135" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1304 -288 112" "angle" "90" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1576 -216 136" "angle" "180" "spawnflags" "768" } { "classname" "monster_zombie" "origin" "1928 -80 200" "spawnflags" "768" "angle" "180" } { "angle" "180" "spawnflags" "768" "origin" "1928 -280 200" "classname" "monster_zombie" } { "classname" "trigger_changelevel" "map" "e2m3" "model" "*41" } { "classname" "light" "origin" "80 1544 40" "light" "160" } { "light" "160" "origin" "-112 1544 40" "classname" "light" } { "classname" "item_shells" "origin" "1056 552 8" } { "classname" "light" "origin" "-112 1672 40" "light" "160" } { "light" "160" "origin" "88 1680 40" "classname" "light" } { "classname" "item_health" "origin" "-168 1688 -8" "spawnflags" "1" } { "classname" "monster_knight" "origin" "-112 1616 16" "angle" "45" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1760 -208 -216" "angle" "180" } { "classname" "trigger_secret" "model" "*42" } { "classname" "trigger_secret" "model" "*43" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_multiple" "target" "t29" "model" "*45" } { "classname" "item_shells" "origin" "-144 -1728 288" "spawnflags" "768" } { "classname" "item_rockets" "origin" "2000 -304 176" "spawnflags" "1793" } { "classname" "item_rockets" "origin" "2000 -112 176" "spawnflags" "1793" } { "origin" "-62 -702 72" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "386 -702 72" } { "origin" "-246 -470 -264" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "554 -454 -264" } { "origin" "162 -430 -264" "classname" "ambient_swamp1" } { "spawnflags" "1" "origin" "-72 576 104" "classname" "item_shells" } { "spawnflags" "2048" "wait" "10" "target" "t16" "classname" "trigger_multiple" "model" "*46" } { "targetname" "t16" "sounds" "4" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*47" } { "origin" "-184 1480 168" "classname" "light" "light" "160" } { "classname" "light" "origin" "-224 1592 168" "light" "100" } { "target" "t52" "classname" "trigger_once" "model" "*48" } { "mangle" "20 30 0" "origin" "1224 -288 336" "classname" "info_intermission" } { "mangle" "20 180 0" "origin" "-352 760 240" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "480 -440 208" "classname" "info_intermission" } { "angle" "90" "origin" "-176 -1904 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-128 -1848 264" "angle" "90" } { "angle" "90" "origin" "-192 -1808 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-320 -1824 264" "angle" "90" } { "spawnflags" "1792" "classname" "func_wall" "model" "*49" } { "spawnflags" "1792" "origin" "200 -664 0" "classname" "weapon_lightning" } { "origin" "-184 1512 144" "classname" "item_artifact_super_damage" } { "classname" "item_cells" "origin" "240 -664 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "392 640 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "-168 456 104" "spawnflags" "1793" } { "classname" "func_door" "angle" "-1" "spawnflags" "1" "wait" "6" "speed" "1000" "sounds" "3" "targetname" "t15" "model" "*50" } { "classname" "weapon_grenadelauncher" "origin" "1312 280 56" "spawnflags" "3584" } { "sounds" "2" "wait" "5" "message" "Shoot the buttons..." "spawnflags" "3584" "classname" "trigger_multiple" "targetname" "t53" "model" "*51" } { "classname" "trigger_relay" "origin" "-72 -320 48" "targetname" "t13" "killtarget" "t53" } quakespasm-0.93.0/Misc/qs_pak/maps/e1m4.ent0000644000000000000000000012532712425501423017052 0ustar rootroot{ "message" "the Grisly Grotto" "worldtype" "0" "classname" "worldspawn" "wad" "gfx/wizard.wad" "sounds" "5" } { "classname" "light" "origin" "464 480 1656" "light" "300" } { "light" "400" "origin" "712 296 1512" "classname" "light" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*1" } { "angle" "0" "classname" "func_door" "model" "*2" } { "classname" "light_flame_small_yellow" "origin" "560 -112 1374" } { "origin" "848 -112 1374" "classname" "light_flame_small_yellow" } { "origin" "760 656 1536" "classname" "light" } { "light" "400" "origin" "834 498 1040" "classname" "light" } { "light" "200" "classname" "light" "origin" "944 728 1456" } { "classname" "light_flame_small_yellow" "origin" "1016 80 998" } { "origin" "392 80 998" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "680 1224 516" "light" "200" } { "light" "200" "origin" "704 992 516" "classname" "light" } { "light" "200" "origin" "696 1608 628" "classname" "light" } { "origin" "704 1368 588" "classname" "light" } { "origin" "816 1616 444" "classname" "light" } { "classname" "light" "origin" "712 1728 444" } { "origin" "592 1608 444" "classname" "light" } { "classname" "light" "origin" "624 1096 444" } { "light" "300" "origin" "432 1328 816" "classname" "light" } { "classname" "light" "origin" "696 1112 816" "light" "300" } { "classname" "light" "origin" "704 -80 960" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "818 18 948" "light" "200" } { "origin" "586 18 948" "classname" "light_torch_small_walltorch" "light" "200" } { "light" "200" "origin" "688 496 880" "classname" "light" } { "origin" "489 483 1356" "classname" "light_flame_large_yellow" } { "light" "200" "origin" "704 -120 1360" "classname" "light" } { "classname" "light" "origin" "1216 936 1560" "light" "200" } { "origin" "1294 826 1576" "classname" "light_flame_large_yellow" } { "sounds" "1" "targetname" "t1" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*3" } { "angle" "0" "wait" "-1" "classname" "func_door" "model" "*4" } { "target" "t1" "classname" "trigger_once" "model" "*5" } { "map" "e1m5" "classname" "trigger_changelevel" "model" "*6" } { "wait" "-1" "angle" "0" "classname" "func_door" "speed" "50" "model" "*7" } { "classname" "info_player_start" "origin" "-256 2272 1240" "angle" "270" } { "targetname" "t23" "classname" "func_door" "angle" "180" "wait" "-1" "speed" "50" "sounds" "3" "model" "*8" } { "classname" "light" "origin" "696 704 820" } { "classname" "light" "origin" "704 776 672" "light" "200" } { "classname" "light" "origin" "360 904 520" "light" "200" } { "origin" "704 856 400" "classname" "light" "light" "200" } { "classname" "light" "origin" "944 880 416" "light" "150" } { "origin" "1056 1176 424" "classname" "light" "light" "200" } { "classname" "light" "origin" "1096 1408 360" } { "classname" "light" "origin" "416 1696 360" } { "origin" "328 1368 360" "classname" "light" "light" "200" } { "light" "200 " "classname" "light" "origin" "696 752 896" } { "light" "250" "origin" "798 1850 1024" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "642 1850 1024" "light" "250" } { "light" "200 " "origin" "700 1364 952" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "1094 1494 1064" } { "origin" "324 1104 1064" "classname" "light_flame_large_yellow" } { "light" "200 " "origin" "704 1660 952" "classname" "light" } { "sounds" "3" "classname" "func_door" "angle" "180" "wait" "-1" "targetname" "t2" "model" "*9" } { "sounds" "3" "classname" "func_door" "wait" "-1" "angle" "0" "targetname" "t4" "model" "*10" } { "classname" "trigger_once" "targetname" "t4" "target" "t7" "model" "*11" } { "classname" "trigger_once" "targetname" "t2" "target" "t7" "model" "*12" } { "dmg" "90" "speed" "200" "classname" "func_train" "target" "t5" "targetname" "t8" "model" "*13" } { "classname" "path_corner" "origin" "-359 1528 1316" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-359 1528 880" "targetname" "t6" "target" "t5" "wait" "-1" } { "classname" "trigger_counter" "targetname" "t7" "target" "t8" "count" "2" "model" "*14" } { "targetname" "t11" "origin" "-96 1640 1256" "classname" "info_null" } { "targetname" "t9" "origin" "-416 1640 1256" "classname" "info_null" } { "light" "400" "target" "t9" "origin" "-396 1640 1256" "classname" "light" } { "light" "400" "target" "t11" "origin" "-116 1640 1256" "classname" "light" } { "classname" "light" "origin" "-328 1564 1532" } { "classname" "light_flame_small_yellow" "origin" "-88 1640 1514" } { "origin" "-424 1640 1514" "classname" "light_flame_small_yellow" } { "origin" "-248 1464 1154" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "-256 1808 1046" } { "light" "200" "origin" "-164 1732 1268" "classname" "light" } { "classname" "light" "origin" "-348 1732 1268" "light" "200" } { "light" "150" "origin" "-248 1500 1056" "classname" "light" } { "light" "150" "origin" "-256 1772 956" "classname" "light" } { "classname" "light" "origin" "-128 1636 920" "light" "150" } { "light" "150" "origin" "-124 1640 920" "classname" "light" } { "classname" "light" "origin" "-172 1524 920" "light" "150" } { "light" "150" "origin" "-284 1516 920" "classname" "light" } { "classname" "light" "origin" "-360 1580 920" "light" "150" } { "light" "75" "origin" "-360 1700 920" "classname" "light" } { "classname" "light" "origin" "72 1632 928" "light" "125" } { "light" "150" "origin" "80 1488 928" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "158 1308 1064" } { "classname" "light" "origin" "-192 1688 1380" "light" "100" } { "light" "100" "origin" "-192 1592 1380" "classname" "light" } { "classname" "light" "origin" "-320 1592 1380" "light" "100" } { "light" "100" "origin" "-320 1688 1380" "classname" "light" } { "classname" "light" "origin" "-384 1640 1452" "light" "125" } { "classname" "light" "origin" "-112 1640 1452" "light" "125" } { "light" "200" "origin" "-248 1504 1336" "classname" "light" } { "classname" "light" "origin" "-128 1640 1336" "light" "200" } { "light" "200" "origin" "-384 1640 1336" "classname" "light" } { "classname" "light" "origin" "696 1608 816" "light" "300" } { "light" "200" "classname" "light" "origin" "888 1328 424" } { "classname" "light" "origin" "160 1352 960" "light" "150" } { "light" "150" "origin" "368 1104 1000" "classname" "light" } { "classname" "light" "origin" "1048 1504 1000" "light" "150" } { "classname" "light" "origin" "992 992 1048" "light" "150" } { "classname" "light" "origin" "48 1384 1008" "light" "100" } { "light" "150" "origin" "1080 1144 1048" "classname" "light" } { "classname" "light" "origin" "968 824 976" "light" "150" } { "classname" "light" "origin" "896 1328 1000" "light" "150" } { "classname" "light" "origin" "368 1616 1000" "light" "175" } { "classname" "light" "origin" "256 1496 824" "light" "150" } { "light" "150" "origin" "256 1352 824" "classname" "light" } { "classname" "light" "origin" "856 1368 536" "light" "200" } { "light" "150" "origin" "552 1360 536" "classname" "light" } { "classname" "light" "origin" "872 1552 1056" "light" "150" } { "classname" "light" "origin" "1032 1056 904" "light" "150" } { "light" "150" "origin" "1080 1184 904" "classname" "light" } { "classname" "light" "origin" "864 848 904" "light" "150" } { "classname" "light" "origin" "312 1496 1016" "light" "175" } { "light" "200" "origin" "704 1368 808" "classname" "light" } { "light" "150" "origin" "464 816 904" "classname" "light" } { "light" "200" "origin" "944 888 800" "classname" "light" } { "light" "150" "origin" "888 1112 728" "classname" "light" } { "light" "175" "origin" "1008 1504 728" "classname" "light" } { "light" "200" "origin" "720 1848 1200" "classname" "light" } { "light" "200" "origin" "704 888 672" "classname" "light" } { "light" "175" "origin" "512 1456 1040" "classname" "light" } { "light" "150" "origin" "440 840 380" "classname" "light" } { "light" "150" "origin" "720 1848 1028" "classname" "light" } { "light" "150" "origin" "720 1936 1024" "classname" "light" } { "origin" "544 2128 984" "classname" "light" "light" "200" } { "light" "200" "origin" "712 1912 576" "classname" "light" } { "light" "300" "origin" "720 2544 856" "classname" "light" } { "light" "200" "origin" "888 2048 592" "classname" "light" } { "origin" "704 2496 1128" "classname" "light" } { "origin" "512 2048 976" "classname" "light" "light" "150" } { "origin" "952 2328 528" "classname" "light" "light" "200" } { "classname" "light" "origin" "700 2760 808" "light" "200" } { "classname" "light" "origin" "700 2800 616" "light" "200" } { "classname" "light" "origin" "584 2780 584" "light" "175" } { "light" "175" "origin" "808 2780 584" "classname" "light" } { "sounds" "3" "wait" "-1" "targetname" "t29" "classname" "func_door" "angle" "270" "model" "*15" } { "message" "This door is opened elsewhere..." "wait" "-1" "classname" "func_door" "angle" "90" "model" "*16" } { "classname" "light" "origin" "424 2304 1000" "light" "200" } { "light" "300" "origin" "696 2880 1062" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "864 2128 984" } { "classname" "light_flame_large_yellow" "origin" "702 2154 1228" "light" "0" } { "classname" "light" "origin" "832 2008 1136" "light" "200" } { "classname" "light" "origin" "584 2008 1136" "light" "200" } { "light" "200" "origin" "272 2016 1136" "classname" "light" } { "classname" "light" "origin" "272 2320 1136" "light" "150" } { "light" "150" "origin" "1104 2408 984" "classname" "light" } { "sounds" "2" "classname" "func_plat" "wait" "4" "model" "*17" } { "classname" "light" "origin" "700 2844 996" "light" "200" } { "classname" "light" "origin" "704 2216 1252" } { "origin" "936 2304 1252" "classname" "light" } { "classname" "light" "origin" "936 2536 1252" } { "light" "300" "classname" "light" "origin" "488 2536 1252" } { "origin" "488 2304 1252" "classname" "light" "light" "300" } { "light" "0" "origin" "998 2306 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "998 2534 1228" "light" "0" } { "light" "0" "origin" "426 2534 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "426 2306 1228" "light" "0" } { "classname" "light" "origin" "704 2152 984" "light" "200" } { "classname" "light_flame_small_yellow" "origin" "1160 2400 1038" "light" "250" } { "origin" "840 2536 630" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light_flame_small_yellow" "origin" "584 2544 630" "light" "250" } { "light" "250" "origin" "408 2304 694" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "960 2632 528" } { "classname" "light" "origin" "960 2480 528" "light" "150" } { "classname" "light" "origin" "456 2304 624" "light" "175" } { "classname" "light" "origin" "704 2416 576" "light" "250" } { "light" "150" "origin" "728 2184 528" "classname" "light" } { "classname" "light" "origin" "512 2176 528" "light" "150" } { "classname" "light" "origin" "832 2336 656" "light" "175" } { "light" "175" "origin" "592 2336 656" "classname" "light" } { "classname" "light" "origin" "808 2584 576" "light" "150" } { "light" "150" "origin" "616 2576 576" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "488 2712 694" "light" "250" } { "classname" "light" "origin" "488 2624 608" "light" "175" } { "classname" "light" "origin" "480 2464 608" "light" "175" } { "light" "250" "origin" "184 2184 1038" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "512 2096 1038" "light" "250" } { "classname" "light" "origin" "224 2184 976" "light" "150" } { "classname" "light" "origin" "848 2024 992" "light" "200" } { "classname" "light" "origin" "256 1992 1000" "light" "150" } { "classname" "light" "origin" "272 2376 1000" "light" "200" } { "classname" "light" "origin" "1112 2208 1032" "light" "150" } { "light" "0" "origin" "698 2860 1228" "classname" "light_flame_large_yellow" } { "origin" "700 2808 1252" "classname" "light" } { "light" "250" "origin" "472 2632 1062" "classname" "light" } { "classname" "light" "origin" "952 2632 1062" "light" "250" } { "light" "150" "origin" "952 2424 894" "classname" "light" } { "classname" "light" "origin" "480 2424 894" "light" "150" } { "light" "150" "origin" "896 2296 966" "classname" "light" } { "classname" "light" "origin" "704 2304 966" "light" "150" } { "light" "150" "origin" "552 2304 966" "classname" "light" } { "light" "250" "origin" "704 2184 406" "classname" "light" } { "light" "150" "origin" "712 2000 406" "classname" "light" } { "light" "200" "origin" "504 2224 400" "classname" "light" } { "light" "200" "origin" "960 2576 400" "classname" "light" } { "light" "150" "origin" "960 2400 400" "classname" "light" } { "light" "150" "origin" "808 1112 592" "classname" "light" } { "classname" "light" "origin" "600 1112 592" "light" "150" } { "light" "175" "origin" "1016 128 936" "classname" "light" } { "classname" "light" "origin" "392 128 936" "light" "175" } { "light" "150" "origin" "848 -64 1304" "classname" "light" } { "classname" "light" "origin" "560 -64 1304" "light" "150" } { "light" "175" "origin" "1248 832 1496" "classname" "light" } { "light" "150" "origin" "1096 872 1496" "classname" "light" } { "light" "150" "origin" "896 2208 976" "classname" "light" } { "classname" "light" "origin" "1096 200 1512" "light" "350" } { "light" "350" "origin" "264 192 1512" "classname" "light" } { "light" "175" "origin" "488 448 1264" "classname" "light" } { "origin" "1048 728 1456" "classname" "light" "light" "200" } { "light" "125" "origin" "1328 928 1448" "classname" "light" } { "light" "225" "origin" "696 672 1320" "classname" "light" } { "origin" "816 -56 1640" "classname" "light" } { "classname" "light" "origin" "584 -56 1640" } { "light" "175" "origin" "880 96 1376" "classname" "light" } { "classname" "light" "origin" "528 96 1376" "light" "175" } { "light" "200" "origin" "960 344 1072" "classname" "light" } { "classname" "light" "origin" "1120 264 1072" "light" "200" } { "light" "175" "origin" "1208 96 1016" "classname" "light" } { "light" "175" "origin" "1024 360 968" "classname" "light" } { "classname" "light" "origin" "328 336 968" "light" "150" } { "classname" "light" "origin" "416 424 1072" "light" "200" } { "light" "200" "origin" "296 256 1072" "classname" "light" } { "light" "150" "origin" "704 48 1040" "classname" "light" } { "light" "175" "origin" "384 464 976" "classname" "light" } { "classname" "light" "origin" "224 264 976" "light" "150" } { "light" "150" "origin" "952 2192 416" "classname" "light" } { "classname" "light" "origin" "1040 -464 1016" "light" "150" } { "sounds" "2" "spawnflags" "1" "classname" "func_plat" "model" "*18" } { "light" "250" "origin" "1072 -272 1262" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1024 -272 1200" "light" "150" } { "light" "200" "origin" "624 -240 1328" "classname" "light" } { "light" "175" "origin" "1080 -624 1168" "classname" "light" } { "origin" "456 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "504 -576 1168" "light" "175" } { "origin" "624 -448 924" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "624 -704 924" } { "light" "125" "origin" "664 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -488 880" "light" "125" } { "light" "125" "origin" "584 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -408 880" "light" "125" } { "light" "125" "origin" "624 -664 880" "classname" "light" } { "classname" "light" "origin" "664 -704 880" "light" "125" } { "light" "125" "origin" "624 -744 880" "classname" "light" } { "classname" "light" "origin" "584 -704 880" "light" "125" } { "light" "250" "origin" "296 -96 968" "classname" "light" } { "light" "250" "origin" "1112 -96 968" "classname" "light" } { "light" "175" "origin" "1056 -224 968" "classname" "light" } { "light" "500" "origin" "-264 2120 1504" "classname" "light" } { "spawnflags" "2064" "angle" "0" "classname" "func_door" "wait" "-1" "model" "*19" } { "sounds" "3" "classname" "func_door" "angle" "180" "spawnflags" "2064" "wait" "-1" "model" "*20" } { "light" "200" "origin" "704 32 952" "classname" "light" } { "target" "t101" "spawnflags" "256" "targetname" "t23" "angle" "90" "origin" "-248 1560 1224" "classname" "monster_wizard" } { "target" "t23" "classname" "trigger_once" "model" "*21" } { "origin" "-280 1560 1348" "classname" "item_armor2" } { "origin" "-488 2112 1220" "classname" "item_health" } { "classname" "item_health" "origin" "-488 2064 1220" } { "spawnflags" "1536" "origin" "-272 1784 1156" "classname" "item_shells" } { "spawnflags" "256" "target" "t25" "targetname" "t24" "origin" "888 1640 1028" "classname" "path_corner" } { "spawnflags" "256" "target" "t24" "targetname" "t25" "classname" "path_corner" "origin" "624 1264 1028" } { "spawnflags" "256" "target" "t24" "origin" "928 1672 1028" "classname" "monster_wizard" } { "spawnflags" "2304" "target" "t26" "classname" "trigger_once" "model" "*22" } { "target" "t27" "targetname" "t28" "origin" "568 2040 928" "classname" "path_corner" } { "target" "t28" "targetname" "t27" "classname" "path_corner" "origin" "368 2044 928" } { "target" "t27" "origin" "472 2040 944" "classname" "monster_ogre" "spawnflags" "1" } { "sounds" "2" "target" "t29" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*23" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*24" } { "angle" "0" "classname" "func_door" "model" "*25" } { "light" "175" "origin" "1112 2520 964" "classname" "light" } { "origin" "104 1308 892" "classname" "item_health" } { "spawnflags" "2048" "origin" "704 1344 936" "classname" "item_key1" "sounds" "1" } { "target" "t34" "angle" "180" "origin" "920 2040 544" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t35" "targetname" "t34" "origin" "864 2044 528" "classname" "path_corner" } { "target" "t37" "targetname" "t35" "classname" "path_corner" "origin" "704 2048 528" } { "target" "t34" "targetname" "t36" "origin" "704 2048 528" "classname" "path_corner" } { "target" "t36" "targetname" "t37" "classname" "path_corner" "origin" "704 1808 528" } { "targetname" "t38" "angle" "270" "origin" "456 2476 544" "classname" "monster_ogre" } { "target" "t38" "classname" "trigger_once" "model" "*26" } { "targetname" "t29" "angle" "0" "origin" "336 2272 952" "classname" "monster_knight" "spawnflags" "768" } { "targetname" "t39" "spawnflags" "1" "wait" "-1" "angle" "-2" "classname" "func_door" "sounds" "1" "model" "*27" } { "targetname" "t39" "angle" "270" "origin" "712 2540 448" "classname" "monster_ogre" } { "target" "t39" "classname" "trigger_once" "model" "*28" } { "classname" "light" "origin" "712 2544 440" "light" "200" } { "classname" "item_health" "origin" "1064 2184 920" "spawnflags" "1024" } { "spawnflags" "1" "origin" "1128 2184 920" "classname" "item_health" } { "classname" "item_spikes" "origin" "816 2840 920" } { "origin" "816 2800 920" "classname" "item_spikes" } { "spawnflags" "256" "classname" "monster_wizard" "origin" "1656 1496 968" "angle" "180" "target" "t41" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t40" "targetname" "t39" "model" "*29" } { "classname" "info_teleport_destination" "origin" "984 1496 1000" "angle" "180" "targetname" "t40" } { "spawnflags" "256" "classname" "path_corner" "origin" "912 1496 1000" "targetname" "t41" "target" "t42" } { "spawnflags" "256" "origin" "528 1368 1000" "classname" "path_corner" "targetname" "t42" "target" "t41" } { "classname" "item_health" "origin" "408 2640 520" } { "classname" "item_shells" "origin" "456 2672 520" "spawnflags" "1" } { "angle" "0" "origin" "80 864 968" "classname" "monster_wizard" "target" "t44" } { "targetname" "t119" "spawnflags" "2" "classname" "trigger_teleport" "target" "t45" "model" "*30" } { "classname" "info_teleport_destination" "origin" "432 856 952" "angle" "45" "targetname" "t45" } { "classname" "path_corner" "origin" "496 872 952" "target" "t43" "targetname" "t44" } { "origin" "872 1056 952" "classname" "path_corner" "targetname" "t43" "target" "t44" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t39" "model" "*31" } { "spawnflags" "1024" "classname" "monster_knight" "origin" "1168 56 904" "angle" "135" "target" "t49" "targetname" "t67" } { "classname" "monster_ogre" "origin" "1800 224 920" "angle" "180" "target" "t116" "spawnflags" "256" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t49" "lip" "-24" "model" "*32" } { "wait" "-1" "angle" "-1" "classname" "func_door" "spawnflags" "1" "targetname" "t39" "lip" "-24" "model" "*33" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t29" "lip" "-24" "model" "*34" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t46" "model" "*35" } { "classname" "info_teleport_destination" "origin" "1104 232 880" "angle" "180" "targetname" "t46" } { "classname" "path_corner" "origin" "1064 256 880" "target" "t47" "targetname" "t48" "spawnflags" "256" } { "origin" "312 232 880" "classname" "path_corner" "targetname" "t47" "target" "t48" "spawnflags" "256" } { "classname" "info_teleport_destination" "origin" "704 -40 1256" "angle" "90" "targetname" "t50" } { "classname" "trigger_once" "target" "t52" "model" "*36" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t50" "targetname" "t52" "model" "*37" } { "angle" "90" "origin" "570 -898 1300" "classname" "monster_wizard" "targetname" "t52" } { "classname" "item_shells" "origin" "216 120 880" "spawnflags" "1" } { "classname" "item_health" "origin" "192 232 880" } { "origin" "192 192 880" "classname" "item_health" "spawnflags" "1024" } { "classname" "item_shells" "origin" "-216 1464 888" } { "classname" "item_health" "origin" "816 1160 512" "spawnflags" "1024" } { "spawnflags" "1" "origin" "816 1120 512" "classname" "item_health" } { "classname" "monster_wizard" "origin" "944 840 956" "angle" "135" "target" "t53" "targetname" "t64" } { "classname" "trigger_once" "target" "t64" "model" "*38" } { "classname" "monster_knight" "origin" "704 392 1280" "angle" "270" "target" "t65" "spawnflags" "1" } { "classname" "path_corner" "origin" "704 208 1264" "targetname" "t65" "target" "t66" } { "origin" "704 496 1264" "classname" "path_corner" "targetname" "t66" "target" "t65" } { "classname" "trigger_once" "target" "t67" "model" "*39" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t72" "model" "*40" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t72" "model" "*41" } { "classname" "trigger_once" "target" "t72" "model" "*42" } { "classname" "info_null" "origin" "852 -580 820" "targetname" "t73" } { "classname" "light" "origin" "856 -584 936" "light" "400" "target" "t73" } { "classname" "light" "origin" "920 -448 744" "light" "150" } { "light" "150" "origin" "760 -448 744" "classname" "light" } { "classname" "light" "origin" "552 -424 744" "light" "150" } { "light" "150" "origin" "560 -728 744" "classname" "light" } { "classname" "light" "origin" "776 -712 744" "light" "150" } { "light" "150" "origin" "936 -712 744" "classname" "light" } { "classname" "path_corner" "origin" "652 -576 952" "targetname" "t75" "target" "t74" } { "origin" "908 -576 952" "classname" "path_corner" "targetname" "t74" "target" "t75" } { "classname" "monster_ogre" "origin" "816 -260 952" "angle" "270" "targetname" "t72" } { "classname" "monster_ogre" "origin" "724 -260 952" "angle" "270" "targetname" "t72" "spawnflags" "256" } { "classname" "item_health" "origin" "632 -548 820" "spawnflags" "3072" } { "origin" "672 -548 820" "classname" "item_health" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t76" "model" "*43" } { "sounds" "1" "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t76" "model" "*44" } { "light" "150" "origin" "1040 -712 1016" "classname" "light" } { "origin" "1112 -576 942" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1064 -576 896" "light" "150" } { "classname" "light" "origin" "888 -80 968" "light" "175" } { "light" "175" "origin" "512 -80 968" "classname" "light" } { "classname" "item_armor2" "origin" "1184 -96 920" } { "classname" "monster_ogre" "origin" "392 8 912" "angle" "315" "targetname" "t77" "spawnflags" "256" } { "classname" "trigger_once" "target" "t77" "model" "*45" } { "classname" "item_health" "origin" "336 -224 888" "spawnflags" "1" } { "classname" "item_spikes" "origin" "968 16 888" "spawnflags" "1" } { "classname" "item_health" "origin" "560 2808 516" "spawnflags" "1" } { "classname" "path_corner" "origin" "1056 -384 824" "targetname" "t78" "target" "t79" "spawnflags" "256" } { "origin" "1056 -736 824" "classname" "path_corner" "targetname" "t79" "target" "t78" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "1064 -656 840" "angle" "90" "target" "t78" "spawnflags" "257" } { "angle" "90" "origin" "848 -880 952" "classname" "monster_ogre" "targetname" "t72" } { "classname" "item_shells" "origin" "1160 16 1248" } { "classname" "item_health" "origin" "280 64 1248" } { "classname" "item_rockets" "origin" "648 -256 928" "spawnflags" "1025" } { "classname" "item_spikes" "origin" "648 -304 1248" } { "origin" "696 -304 1248" "classname" "item_spikes" } { "classname" "light_flame_small_yellow" "origin" "768 -352 1230" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*46" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*47" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*48" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*49" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*50" } { "targetname" "t83" "classname" "trap_spikeshooter" "origin" "1108 -576 1140" "spawnflags" "1" "angle" "180" } { "targetname" "t83" "angle" "90" "spawnflags" "1" "origin" "768 -796 1140" "classname" "trap_spikeshooter" } { "origin" "1112 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "768 -800 1230" } { "classname" "light" "origin" "712 -756 1168" "light" "175" } { "light" "175" "origin" "768 -424 1168" "classname" "light" } { "light" "175" "origin" "888 -744 956" "classname" "light" } { "classname" "light" "origin" "888 -408 956" "light" "175" } { "origin" "384 -224 888" "classname" "item_shells" } { "origin" "584 1784 920" "classname" "item_health" } { "origin" "832 2064 920" "classname" "item_shells" } { "target" "t72" "classname" "trigger_once" "model" "*51" } { "target" "t85" "targetname" "t84" "origin" "1560 216 896" "classname" "path_corner" } { "target" "t48" "targetname" "t85" "classname" "path_corner" "origin" "1456 216 896" } { "origin" "704 1368 516" "classname" "weapon_supernailgun" } { "spawnflags" "1" "origin" "184 1928 920" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "656 1816 528" "spawnflags" "768" } { "classname" "item_shells" "origin" "1072 -800 820" "spawnflags" "1024" } { "classname" "monster_ogre" "origin" "840 -40 1276" "angle" "180" "targetname" "t86" "spawnflags" "768" } { "classname" "trigger_once" "target" "t86" "model" "*52" } { "classname" "light" "origin" "472 -576 876" "light" "150" } { "classname" "item_shells" "origin" "656 680 1256" "spawnflags" "1536" } { "angle" "270" "origin" "880 2224 536" "classname" "monster_knight" "spawnflags" "256" } { "angle" "180" "origin" "1112 2424 944" "classname" "monster_ogre" "spawnflags" "1281" } { "targetname" "t88" "target" "t87" "origin" "360 384 880" "classname" "path_corner" "spawnflags" "1280" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "504 160 880" "spawnflags" "1280" } { "target" "t87" "angle" "315" "origin" "384 320 896" "classname" "monster_knight" "spawnflags" "1280" } { "spawnflags" "257" "targetname" "t86" "angle" "0" "origin" "376 120 1272" "classname" "monster_ogre" } { "origin" "776 1368 916" "classname" "item_shells" "spawnflags" "1024" } { "classname" "item_spikes" "origin" "184 1968 920" "spawnflags" "1" } { "classname" "monster_wizard" "origin" "312 936 944" "angle" "45" "spawnflags" "769" } { "classname" "monster_knight" "origin" "704 -80 908" "angle" "90" } { "angle" "0" "origin" "568 -56 1276" "classname" "monster_ogre" "targetname" "t86" "spawnflags" "768" } { "spawnflags" "1536" "targetname" "t90" "target" "t89" "origin" "720 1696 924" "classname" "path_corner" } { "spawnflags" "1536" "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "720 1416 924" } { "spawnflags" "1537" "target" "t90" "origin" "704 1784 948" "classname" "monster_ogre" } { "classname" "func_door_secret" "angle" "0" "spawnflags" "1" "targetname" "t91" "model" "*53" "t_length" "73" // svdijk -- added to prevent z-fighting } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*54" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*55" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*56" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*57" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*58" } { "classname" "trigger_counter" "count" "5" "target" "t91" "targetname" "t92" "model" "*59" } { "classname" "light" "origin" "680 -920 1144" "light" "150" } { "classname" "item_spikes" "origin" "752 -876 928" "spawnflags" "1" } { "spawnflags" "3" "angle" "0" "classname" "func_door_secret" "targetname" "t91" "model" "*60" "t_length" "73" // svdijk -- added to prevent z-fighting } { "light" "150" "origin" "680 -256 1144" "classname" "light" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t72" "model" "*61" } { "classname" "trigger_secret" "sounds" "1" "targetname" "t8" "model" "*62" } { "angle" "270" "origin" "704 624 1280" "classname" "monster_knight" "spawnflags" "1" } { "angle" "225" "origin" "1016 -440 1128" "classname" "monster_knight" } { "angle" "180" "origin" "1024 -728 1128" "classname" "monster_knight" } { "spawnflags" "256" "angle" "180" "origin" "1008 -568 1128" "classname" "monster_knight" } { "spawnflags" "256" "classname" "monster_knight" "origin" "304 2312 952" "angle" "0" "targetname" "t29" } { "spawnflags" "1025" "classname" "monster_knight" "origin" "272 136 896" "angle" "45" } { "classname" "monster_wizard" "origin" "784 -576 960" "angle" "0" "target" "t74" "spawnflags" "1" } { "classname" "item_health" "origin" "732 -936 928" } { "origin" "772 -936 928" "classname" "item_health" } { "spawnflags" "256" "classname" "monster_knight" "origin" "704 -248 1272" "angle" "0" } { "classname" "path_corner" "origin" "1328 928 1396" "targetname" "t93" "target" "t94" "spawnflags" "512" } { "origin" "1176 928 1396" "classname" "path_corner" "target" "t93" "targetname" "t94" "spawnflags" "512" } { "classname" "monster_knight" "origin" "1192 884 1412" "angle" "0" "target" "t93" "spawnflags" "513" } { "classname" "monster_knight" "origin" "704 2376 944" "angle" "90" } { "classname" "monster_knight" "origin" "888 2312 944" "angle" "180" "targetname" "t95" } { "angle" "0" "origin" "512 2304 944" "classname" "monster_knight" "targetname" "t95" "spawnflags" "768" } { "classname" "trigger_once" "target" "t95" "model" "*63" } { "spawnflags" "256" "angle" "315" "origin" "728 2312 536" "classname" "monster_knight" } { "light" "200" "origin" "1296 1528 936" "classname" "light" } { "light" "250" "origin" "1432 1360 982" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1400 1360 920" "classname" "light" } { "light" "200" "origin" "1248 1344 680" "classname" "light" } { "lip" "-384" "wait" "-1" "angle" "90" "classname" "func_door" "targetname" "t98" "model" "*64" } { "light" "150" "origin" "1152 1328 584" "classname" "light" } { "origin" "1376 1480 864" "classname" "item_health" } { "sounds" "1" "classname" "trigger_secret" "model" "*65" } { "classname" "func_button" "sounds" "1" "angle" "0" "wait" "-1" "target" "t97" "model" "*66" } { "classname" "func_button" "wait" "-1" "angle" "0" "sounds" "1" "target" "t97" "model" "*67" } { "classname" "trigger_counter" "targetname" "t97" "target" "t98" "model" "*68" } { "classname" "light" "origin" "880 -888 968" "light" "150" } { "light" "150" "origin" "880 -264 968" "classname" "light" } { "angle" "180" "origin" "1112 2344 944" "classname" "monster_ogre" "spawnflags" "769" } { "angle" "270" "origin" "1120 880 1412" "classname" "monster_ogre" "spawnflags" "257" } { "map" "e1m8" "classname" "trigger_changelevel" "model" "*69" } { "light" "175" "origin" "824 -756 1168" "classname" "light" } { "classname" "light" "origin" "1080 -528 1168" "light" "175" } { "classname" "monster_wizard" "origin" "672 -392 1024" "angle" "315" "spawnflags" "257" } { "classname" "monster_knight" "origin" "1008 -656 1128" "angle" "180" "spawnflags" "768" } { "origin" "520 1064 440" "classname" "air_bubbles" } { "classname" "item_spikes" "origin" "16 1432 892" } { "classname" "trigger_once" "message" "A secret cave has opened..." "targetname" "t98" "model" "*70" } { "target" "t4" "health" "1" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*71" } { "target" "t2" "health" "1" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*72" } { "target" "t49" "spawnflags" "769" "angle" "135" "origin" "1208 128 904" "classname" "monster_demon1" } { "spawnflags" "769" "angle" "45" "origin" "288 160 896" "classname" "monster_demon1" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "692 -884 952" "angle" "90" } { "classname" "monster_ogre" "origin" "-312 1648 1372" "angle" "90" "targetname" "t23" "spawnflags" "768" } { "angle" "90" "origin" "-192 1648 1372" "classname" "monster_ogre" "targetname" "t23" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "704 1288 540" "angle" "270" "spawnflags" "768" } { "targetname" "t101" "target" "t106" "spawnflags" "770" "classname" "trigger_teleport" "model" "*73" } { "spawnflags" "768" "targetname" "t101" "angle" "270" "origin" "-256 2424 1288" "classname" "monster_wizard" } { "targetname" "t101" "origin" "-248 2440 1280" "classname" "trigger_relay" } { "targetname" "t29" "spawnflags" "256" "angle" "0" "origin" "144 2648 1024" "classname" "monster_wizard" } { "targetname" "t29" "spawnflags" "768" "classname" "monster_wizard" "origin" "144 2592 1024" "angle" "0" } { "targetname" "t29" "spawnflags" "768" "angle" "0" "origin" "144 2536 1024" "classname" "monster_wizard" } { "targetname" "t29" "target" "t102" "spawnflags" "258" "classname" "trigger_teleport" "model" "*74" } { "targetname" "t29" "target" "t103" "spawnflags" "770" "classname" "trigger_teleport" "model" "*75" } { "targetname" "t29" "target" "t104" "spawnflags" "770" "classname" "trigger_teleport" "model" "*76" } { "angle" "270" "targetname" "t102" "spawnflags" "256" "origin" "704 2656 1008" "classname" "info_teleport_destination" } { "angle" "270" "targetname" "t103" "spawnflags" "768" "classname" "info_teleport_destination" "origin" "920 2520 1008" } { "angle" "0" "targetname" "t104" "spawnflags" "768" "origin" "592 2192 1008" "classname" "info_teleport_destination" } { "sounds" "1" "classname" "func_door" "angle" "0" "wait" "-1" "speed" "150" "targetname" "t105" "model" "*77" } { "classname" "monster_demon1" "origin" "1056 -880 1128" "angle" "90" "spawnflags" "768" "targetname" "t105" } { "classname" "trigger_once" "spawnflags" "768" "target" "t105" "model" "*78" } { "classname" "light" "origin" "1056 -920 1184" "light" "125" } { "classname" "trigger_relay" "origin" "1104 -864 1120" "target" "t105" } { "classname" "monster_ogre" "origin" "1120 768 1416" "angle" "180" "spawnflags" "769" } { "classname" "air_bubbles" "origin" "884 1616 440" } { "targetname" "t106" "spawnflags" "768" "angle" "270" "origin" "-264 2232 1296" "classname" "info_teleport_destination" } { "classname" "path_corner" "origin" "568 1984 928" "targetname" "t107" "target" "t108" "spawnflags" "256" } { "origin" "424 1960 928" "classname" "path_corner" "target" "t107" "spawnflags" "256" "targetname" "t111" } { "classname" "path_corner" "origin" "704 2024 928" "targetname" "t108" "target" "t109" "spawnflags" "256" } { "origin" "712 1712 928" "classname" "path_corner" "targetname" "t109" "target" "t110" "spawnflags" "256" } { "classname" "path_corner" "origin" "712 1416 928" "targetname" "t110" "target" "t109" "spawnflags" "256" } { "classname" "func_door" "angle" "-2" "wait" "-1" "lip" "-24" "targetname" "t26" "spawnflags" "2304" "model" "*79" } { "classname" "monster_ogre" "origin" "240 2048 944" "angle" "0" "spawnflags" "257" "target" "t111" } { "target" "t23" "spawnflags" "1792" "classname" "trigger_once" "model" "*80" } { "classname" "monster_knight" "origin" "576 2768 536" "angle" "0" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "824 2776 536" "classname" "monster_knight" } { "classname" "monster_wizard" "origin" "704 -1032 1024" "angle" "90" "spawnflags" "256" "targetname" "t52" } { "angle" "90" "origin" "760 -1032 1024" "classname" "monster_wizard" "spawnflags" "768" "targetname" "t114" } { "classname" "monster_wizard" "origin" "816 -1032 1024" "angle" "90" "spawnflags" "768" "targetname" "t114" } { "targetname" "t52" "classname" "trigger_teleport" "spawnflags" "258" "target" "t112" "model" "*81" } { "classname" "trigger_teleport" "spawnflags" "770" "target" "t113" "targetname" "t114" "model" "*82" } { "targetname" "t114" "classname" "trigger_teleport" "spawnflags" "770" "target" "t115" "model" "*83" } { "classname" "info_teleport_destination" "origin" "896 224 1352" "angle" "135" "spawnflags" "256" "targetname" "t112" } { "classname" "info_teleport_destination" "origin" "488 1648 1016" "angle" "315" "spawnflags" "768" "targetname" "t113" } { "classname" "trigger_once" "spawnflags" "768" "target" "t114" "model" "*84" } { "classname" "info_teleport_destination" "origin" "800 904 928" "angle" "90" "spawnflags" "768" "targetname" "t115" } { "angle" "270" "origin" "-256 2232 1242" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "-256 2096 1218" "classname" "weapon_supershotgun" } { "angle" "270" "origin" "704 424 1266" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 2488 946" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1968 546" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "704 104 898" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1568 938" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "704 1344 912" "classname" "weapon_rocketlauncher" } { "angle" "0" "origin" "712 -576 840" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "944 -576 816" "classname" "weapon_nailgun" } { "angle" "180" "origin" "1064 -576 1128" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "696 584 1256" "classname" "weapon_grenadelauncher" } { "classname" "light" "origin" "316 804 780" "light" "150" } { "classname" "light" "origin" "316 804 644" "light" "75" } { "classname" "item_rockets" "origin" "298 710 706" "spawnflags" "1" } { "classname" "item_shells" "origin" "988 -928 1104" "spawnflags" "1" } { "classname" "item_health" "origin" "-56 2112 1220" "spawnflags" "3584" } { "classname" "item_health" "origin" "-56 2072 1220" "spawnflags" "2305" } { "spawnflags" "1" "origin" "584 2416 512" "classname" "item_spikes" } { "origin" "688 1392 516" "classname" "item_spikes" } { "classname" "item_artifact_invulnerability" "origin" "712 2312 948" "spawnflags" "1792" } { "origin" "32 1392 916" "classname" "item_artifact_envirosuit" } { "mangle" "26 310 0" "origin" "384 488 1552" "classname" "info_intermission" } { "light" "100" "origin" "800 1160 464" "classname" "light" } { "classname" "light" "origin" "608 1160 464" "light" "100" } { "light" "100" "origin" "604 1472 464" "classname" "light" } { "target" "t40" "classname" "trigger_teleport" "model" "*85" } { "mangle" "-20 75 0" "origin" "456 2144 568" "classname" "info_intermission" } { "mangle" "10 80 0" "origin" "464 1032 1000" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "1080 -752 1008" "classname" "info_intermission" } { "classname" "trigger_secret" "model" "*86" } { "classname" "path_corner" "origin" "1632 216 896" "targetname" "t116" "target" "t84" } { "classname" "item_spikes" "origin" "1200 48 872" "spawnflags" "1" } { "classname" "item_spikes" "origin" "928 2664 320" "spawnflags" "1" } { "classname" "item_shells" "origin" "1240 768 1384" "spawnflags" "1" } { "classname" "item_shells" "origin" "-88 2160 1224" "spawnflags" "2049" } { "classname" "info_player_coop" "origin" "-208 2272 1240" "angle" "270" } { "angle" "270" "origin" "-304 2272 1240" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-352 2272 1240" "angle" "270" } { "light" "200" "origin" "1288 1648 956" "classname" "light" } { "classname" "item_spikes" "origin" "-264 1464 888" } { "spawnflags" "1792" "origin" "1296 1488 888" "classname" "item_artifact_invisibility" } { "spawnflags" "1792" "classname" "func_wall" "model" "*87" } { "classname" "func_wall" "spawnflags" "1792" "model" "*88" } { "target" "t118" "targetname" "t117" "origin" "-176 1640 888" "classname" "path_corner" } { "targetname" "t118" "target" "t117" "classname" "path_corner" "origin" "-320 1640 888" } { "target" "t117" "origin" "-256 1632 904" "classname" "monster_knight" "spawnflags" "1" } { "origin" "1376 1424 864" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*89" } { "target" "t120" "targetname" "t53" "classname" "trigger_once" "model" "*90" } { "target" "t120" "targetname" "t39" "classname" "trigger_once" "model" "*91" } { "classname" "light" "origin" "480 2568 568" "light" "125" } { "origin" "162 1482 976" "classname" "ambient_drip" } { "origin" "786 1010 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "778 1210 584" } { "origin" "594 1202 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "602 1010 584" } { "origin" "786 1514 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "794 1698 584" } { "origin" "618 1690 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "618 1522 584" } { "origin" "698 1362 584" "classname" "ambient_drip" } { "origin" "714 1970 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "898 2170 592" } { "origin" "938 2346 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "682 2298 592" } { "origin" "458 2306 592" "classname" "ambient_drip" } { "origin" "458 1690 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "322 1506 880" } { "origin" "338 1226 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "466 1090 880" } { "origin" "394 882 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "674 810 880" } { "origin" "914 818 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "922 1034 880" } { "origin" "1082 1266 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "994 1442 880" } { "origin" "898 1714 880" "classname" "ambient_drip" } { "origin" "706 1362 1080" "classname" "ambient_drip" } { "origin" "1194 1522 1032" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1314 1354 1032" } { "origin" "442 354 920" "classname" "ambient_swamp1" } { "origin" "978 314 920" "classname" "ambient_swamp2" } quakespasm-0.93.0/Misc/qs_pak/default.cfg0000644000000000000000000000417312665410352016742 0ustar rootroot// // load keybindings // // commands with a leading + will also be called for key up events with // the + changed to a - unbindall // // character controls // bind ALT +strafe bind , +moveleft bind a +moveleft bind . +moveright bind d +moveright bind DEL +lookdown bind PGDN +lookup bind END centerview bind e +moveup bind c +movedown bind SHIFT +speed bind CTRL +attack bind UPARROW +forward bind w +forward bind DOWNARROW +back bind s +back bind LEFTARROW +left bind RIGHTARROW +right bind SPACE +jump //bind ENTER +jump bind TAB +showscores bind 1 "impulse 1" bind 2 "impulse 2" bind 3 "impulse 3" bind 4 "impulse 4" bind 5 "impulse 5" bind 6 "impulse 6" bind 7 "impulse 7" bind 8 "impulse 8" bind 0 "impulse 0" bind / "impulse 10" // change weapon bind MWHEELDOWN "impulse 10" bind MWHEELUP "impulse 12" // zoom alias zoom_in "sensitivity 2;fov 90;wait;fov 70;wait;fov 50;wait;fov 30;wait;fov 10;wait;fov 5;bind F11 zoom_out" alias zoom_out "sensitivity 4;fov 5;wait;fov 10;wait;fov 30;wait;fov 50;wait;fov 70;wait;fov 90;bind F11 zoom_in; sensitivity 3" bind F11 zoom_in // Function keys bind F1 "help" bind F2 "menu_save" bind F3 "menu_load" bind F4 "menu_options" bind F5 "menu_multiplayer" bind F6 "echo Quicksaving...; wait; save quick" bind F9 "echo Quickloading...; wait; load quick" bind F10 "quit" bind F12 "screenshot" // mouse options bind \ +mlook // // client environment commands // bind PAUSE "pause" bind ESCAPE "togglemenu" bind ~ "toggleconsole" bind ` "toggleconsole" bind t "messagemode" bind + "sizeup" bind = "sizeup" bind - "sizedown" bind INS +klook // // mouse buttons // bind MOUSE1 +attack //bind MOUSE2 +forward bind MOUSE2 +jump //bind MOUSE3 +mlook // // game controller // bind LSHOULDER "impulse 12" bind RSHOULDER "impulse 10" bind LTRIGGER +jump bind RTRIGGER +attack // // default cvars // gamma 1.0 volume 0.7 sensitivity 3 //viewsize 100 viewsize 110 scr_conscale 1.6 scr_menuscale 1.6 scr_sbarscale 1.6 // default to mouse-look enabled +mlook quakespasm-0.93.0/Misc/qs_pak/mkpak.sh0000755000000000000000000000524012375064103016270 0ustar rootroot#!/bin/sh # # Copyright (c) 2014, Sander van Dijk # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. error() { echo "$(basename "$0"): $*" >&2 exit 1 } assert_valid_stdout() { if test -t 1 then error "Usage: $(basename "$0") [file ...] > output.pak" fi } assert_valid_file() { if test ! -e "$1" then error "$1: No such file" fi if test ! -f "$1" then error "$f: Not a regular file" fi if test ! -r "$1" then error "$1: Permission denied" fi if test $(echo -n "$1" | wc -c) -gt 55 then error "$1: Name too long" fi } assert_valid_int32() { if test $1 -lt -2147483648 -o $1 -gt 2147483647 then error "Too much data" fi } octal() { if test $1 -gt 7 then octal $(expr $1 / 8) fi echo -n $(expr $1 % 8) } byte() { echo -en \\0$(octal $1) } little_endian_uint32() { byte $(expr $1 % 256) byte $(expr $1 / 256 % 256) byte $(expr $1 / 65536 % 256) byte $(expr $1 / 16777216 % 256) } little_endian_int32() { if test $1 -lt 0 then little_endian_uint32 $(expr $1 + 4294967296) else little_endian_uint32 $1 fi } zero_padding() { if test $1 -lt 1 then return fi byte 0 zero_padding $(expr $1 - 1) } header() { echo -n PACK little_endian_int32 $1 little_endian_int32 $2 } directory_entry() { echo -n "$1" zero_padding $(expr 56 - $(echo -n "$1" | wc -c)) little_endian_int32 $2 little_endian_int32 $3 } assert_valid_stdout directory_offset=12 directory_size=0 for file in "$@" do assert_valid_file "$file" file_offset=$directory_offset assert_valid_int32 $file_offset file_size=$(wc -c < "$file") assert_valid_int32 $file_size directory_offset=$(expr $directory_offset + $file_size) assert_valid_int32 $directory_offset directory_size=$(expr $directory_size + 64) assert_valid_int32 $directory_size done header $directory_offset $directory_size for file in "$@" do cat "$file" done file_offset=12 for file in "$@" do file_size=$(wc -c < "$file") directory_entry "$file" $file_offset $file_size file_offset=$(expr $file_offset + $file_size) done quakespasm-0.93.0/Misc/qs_pak/default.cfg.orig0000644000000000000000000000343012327137640017675 0ustar rootroot// // load keybindings // // commands with a leading + will also be called for key up events with // the + changed to a - unbindall // // character controls // bind ALT +strafe bind , +moveleft bind . +moveright bind DEL +lookdown bind PGDN +lookup bind END centerview bind z +lookdown bind a +lookup bind d +moveup bind c +movedown bind SHIFT +speed bind CTRL +attack bind UPARROW +forward bind DOWNARROW +back bind LEFTARROW +left bind RIGHTARROW +right bind SPACE +jump bind ENTER +jump bind TAB +showscores bind 1 "impulse 1" bind 2 "impulse 2" bind 3 "impulse 3" bind 4 "impulse 4" bind 5 "impulse 5" bind 6 "impulse 6" bind 7 "impulse 7" bind 8 "impulse 8" bind 0 "impulse 0" bind / "impulse 10" // change weapon // zoom alias zoom_in "sensitivity 2;fov 90;wait;fov 70;wait;fov 50;wait;fov 30;wait;fov 10;wait;fov 5;bind F11 zoom_out" alias zoom_out "sensitivity 4;fov 5;wait;fov 10;wait;fov 30;wait;fov 50;wait;fov 70;wait;fov 90;bind F11 zoom_in; sensitivity 3" bind F11 zoom_in // Function keys bind F1 "help" bind F2 "menu_save" bind F3 "menu_load" bind F4 "menu_options" bind F5 "menu_multiplayer" bind F6 "echo Quicksaving...; wait; save quick" bind F9 "echo Quickloading...; wait; load quick" bind F10 "quit" bind F12 "screenshot" // mouse options bind \ +mlook // // client environment commands // bind PAUSE "pause" bind ESCAPE "togglemenu" bind ~ "toggleconsole" bind ` "toggleconsole" bind t "messagemode" bind + "sizeup" bind = "sizeup" bind - "sizedown" bind INS +klook // // mouse buttons // bind MOUSE1 +attack bind MOUSE2 +forward bind MOUSE3 +mlook // // default cvars // viewsize 100 gamma 1.0 volume 0.7 sensitivity 3 quakespasm-0.93.0/Misc/qs_pak/gfx/0000755000000000000000000000000013204512422015403 5ustar rootrootquakespasm-0.93.0/Misc/qs_pak/gfx/conback.lmp0000644000000000000000000120001012327137640017521 0ustar rootroot€®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­¬¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬«««¬«¬¬¬­¬¬¬¬¬­¬­¬¬­­¬¬­¬¬¬¬¬­¬¬­¬­¬­®®®®®®®®®¿111¿11¿¿1¿¿1¿¿¿1¿¿¿®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬­¬¬­¬¬­­­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬«­«««««¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬®®®®®®®®®®¿11¿1111111111¿¿¿1®®®®®®®®®­¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬«¬««««¬¬¬¬¬¬­­¬¬¬¬­­¬¬¬¬¬¬­¬¬¬¬­­®®®®®®®®®¿¿¿¿¿¿1¿1111¿¿¿¿1¿¿¿¿®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬­­­¬­­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬­­­¬¬¬¬¬¬­®®®®®®®®¿¿¿¿¿¿¿11111¿¿1¿1¿1®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­¬¬­¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬«¬¬¬¬««­­¬­­­¬¬­­­¬¬¬­¬¬­¬­®®®®®¿¿¿11¿¿11111111¿1111111111®®®®®®®®®®®¬­®¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­­¬­¬¬¬­­­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬­«¬¬¬¬¬¬­¬­¬­­¬¬¬­­¬¬¬¬¬­®®®®®®®®¿1111¿¿¿111111¿11111111¿®®®®®®®®®®®®®¬­­®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬­¬¬­­¬¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬«­««¬¬¬«¬¬¬¬¬¬­¬­­¬­­¬¬­¬¬¬¬¬¬¬®®®®®¿1111¿¿¿¿1¿1111¿1¿1¿1¿¿¿¿®®®®®®®®®®¬¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬««««¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬¬®®®®®¿11¿1¿¿¿1¿¿11¿1¿¿¿¿®®®®®®®®®®¬¬®®¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­­¬¬¬¬¬¬­­¬­­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬«««¬¬¬««««««¬«¬¬¬¬¬­¬¬¬¬¬¬­­­¬¬¬­¬¬¬­®®®®®¿¿111¿¿¿1¿¿¿11¿¿¿¿®®®®®®®­®®®®¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬­¬¬««««¬¬¬¬¬¬­­¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬®®¿11¿¿¿1¿¿1¿¿¿1®®®®®­­¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬­¬­¬­¬­¬¬­­¬­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬¬¬«¬­¬­¬¬­¬­¬¬­¬­¬­¬­¬¬¬­­­­®®®®®1¿¿¿¿¿11¿¿¿¿¿¿¿1®®®®®®®®®­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬¬«¬¬¬¬¬¬­¬­¬­¬¬¬­­­­­¬¬¬¬¬­¬¬­¬­®­®®®¿¿¿¿1111111¿®®®®®®®®®®®®­­®­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­¬¬¬­­¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬¬­­¬­­®®¿¿111111¿¿¿¿¿®®®®®®®®®®®¬­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬«¬¬«¬«««¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬®®®®®¿1¿1¿¿¿¿¿®®®®®®®®®®®®®­®­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬«¬¬¬«««««¬¬¬­¬¬­¬¬¬¬¬¬¬¬­­­­­­¬¬­®®®®®11¿¿¿1¿®®®®®®®®®®®®®­®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬¬­¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«««¬««««¬¬­­¬¬¬­­¬¬­¬¬¬¬¬­¬­­¬¬®®®®®®®1¿®¿¿¿¿®®®®®®®®®®®®®®®®®®­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬­«««««¬««««««««««««««««­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­®®®®¿1¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­¬¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­®­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««¬«««««¬¬­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬­­­¬­¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««­¬­¬­­¬¬­¬¬­¬¬­­¬­­¬¬­¬­¬­¬®®¿®¿¿¿®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­­¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬­¬¬¬­¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬«««¬«««««««««««««««««««¬«­¬­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬­¬¬­¬¬®®®1¿®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬¬¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®¬­­­¬¬¬­¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬­«¬¬«««««««««¬««««««¬«¬¬­¬¬­­®¬­­¬¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬­¬­­­­®®¿¿¿®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬¬¬¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬¬«««««««««««¬«««««««««««««­¬­¬¬¬¬¬­¬¬­­­¬¬­­¬­¬¬¬¬¬­¬­­¬¬¬­­®®®¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®¬­­­­­­­¬¬¬¬¬¬¬­¬¬¬­¬¬«««¬¬¬¬¬¬¬¬««««««««««««««««««««««««¬¬¬­¬­­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­­­®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬«««««««««««««««««««««¬¬¬¬¬¬­¬­¬­­¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬­¬­®®¿1¿1¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­­­¬¬¬¬­¬¬¬¬­¬¬«¬¬­¬¬¬­¬­¬¬¬¬¬¬¬«««««««««««««««««««¬¬¬­¬­­¬¬¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬­­¬¬­­®®®®®¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­­­¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬¬¬¬¬««««««««««««««««««««««««¬¬¬¬­­­¬­¬­¬¬­¬­¬­¬¬­­­¬¬¬¬¬­¬¬¬­®®®®¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««­««««««««««««««««¬«««««««¬¬­¬¬­¬­¬¬®¬¬­¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬®®®¿¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­­­¬­¬¬¬¬¬­¬¬¬¬­­¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««¬¬¬­¬¬¬¬­¬­­¬¬¬¬­¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬­®®¿11®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬¬¬­¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬««««¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬­¬¬¬­¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬­®¿®¿¿®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬«««««««««««««««««««««««¬¬¬«¬¬¬­¬­­­­¬¬¬­¬¬¬¬¬­¬¬¬¬­­®¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­¬¬¬­­­¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­««¬¬¬¬«¬«««««««««««««««««««««««¬¬­­¬­®¬¬¬¬­­¬¬¬¬¬­­¬¬­­¬¬®®®¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­¬¬­¬¬­­­¬¬¬­­¬¬¬¬­¬­¬¬««¬¬¬¬¬««¬«««««««««««««««¬««««¬¬¬­­¬®®®¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­¬¬¬¬¬¬­®®®¿¿¿1¿®1®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬­¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬¬­¬­­¬¬­­­¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬«¬¬«¬¬¬¬«««¬¬«¬«¬¬¬¬­¬¬­­®®®­­¬­¬¬­¬­­¬­¬¬¬­­­¬­­­¬­­®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬­¬¬­¬¬¬¬­¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­¬¬¬­¬¬¬­¬¬¬¬¬­­¬­¬¬¬¬¬««««¬«¬«¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­®­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬­¬­­­¬­®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­¬­­¬­­­­¬­­¬¬¬¬­¬¬­¬¬¬­¬¬«¬«¬«¬¬«¬«¬¬¬¬¬¬«¬«¬¬¬¬¬¬­¬¬­­­­¬­¬¬¬¬­¬¬¬¬¬­¬¬­­­­®®®®®®®¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­¬¬­¬­­­­­­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬««¬¬¬¬«««¬««««¬«¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬­¬­­®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­¬¬¬­¬­­¬¬¬¬¬­¬¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­¬­­¬­­­¬¬­¬¬­¬¬¬­­¬­¬¬«««««««««««««¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­­¬­­¬¬¬¬¬¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬­®¬¬¬­­¬¬­¬¬¬¬­¬¬¬««««««««««¬¬¬¬¬¬¬¬­¬¬««­¬¬­¬­­¬¬­¬¬­­­¬¬¬­­¬¬­¬¬­­¬¬­¬¬®®®®®®®®®®®®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­­­­­­­­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬«¬«««««¬¬¬¬««¬«¬¬­­¬¬¬«¬­¬­¬¬¬¬­­¬¬¬¬­­¬¬¬¬­¬¬­¬¬­¬¬­®­®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­¬¬­¬­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬¬«««¬¬¬¬««¬¬¬«¬¬¬¬­¬­¬­­­­¬¬¬¬¬¬­­¬­¬­¬­¬¬­®¬­¬®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬­¬¬¬¬­­­¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­®­­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬««««««¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬­¬¬¬¬­­¬¬­­¬¬¬¬¬­­­®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬­­¬­¬­­¬¬¬¬­¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬­­®­­¬¬¬­¬¬¬¬¬­¬¬¬¬­¬«««¬­¬¬¬«¬¬«¬¬¬¬«¬¬¬«¬¬¬¬­¬­­¬¬¬¬¬­­¬­¬­¬¬®­­¬¬­®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬­¬¬­¬¬¬­­¬¬¬­­­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬®®¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬««¬«¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬¬¬¬¬­­¬¬­¬­¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬­¬¬¬¬¬­¬­¬¬¬­¬­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­®­¬¬¬¬¬¬­­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬«¬­¬¬­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­­­­­¬­®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬­­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬­­­¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬«¬««¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­­­­¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­­­­­­­­­­¬¬¬¬­¬¬­­¬­¬¬­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬«¬¬¬­­¬¬­­¬­¬¬­¬­­¬­­¬­¬¬¬¬¬¬¬¬­®­®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬­­­¬¬¬­¬­¬­­¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­­­­¬­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬««««¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬¬­¬¬¬­¬­¬¬­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬­­­­­®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­­­­­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬«¬««¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬­­­­¬¬­¬¬¬¬­¬­¬¬­®¬¬­­­­®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬­¬®®­®­¬­¬¬­­­¬¬­­¬­¬­­¬­¬­­­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬­­­¬¬¬¬¬­¬¬­­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­­­­­¬¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬««¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬­­­¬¬­¬­¬¬¬¬¬¬¬­­¬­¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬­¬­¬¬¬­¬¬¬­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«««¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬«¬¬¬¬¬¬¬­¬®­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­¬¬®­¬­­®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬­¬¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­¬­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬«¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬®®¬¬­¬¬®­¬¬­¬¬¬¬¬¬¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­¬®­­­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬®­­®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®¬¬¬¬­¬¬­¬­¬­¬­­­¬¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬­¬¬­¬¬¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­®­­®­¬®­­¬¬­¬¬¬¬­­¬­¬­®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®¬¬¬­¬¬­­¬¬¬¬¬¬¬­¬¬­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­­®®®­¬¬¬­¬¬¬¬­­¬¬­®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­¬­¬­¬¬­­¬¬¬¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­­¬¬­­­­­­­­­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­­¬­®¬¬­¬¬¬­¬¬­­­­­­¬¬®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬­¬¬­¬­¬¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­®¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬­­­­®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­­­¬­¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­¬«­¬¬¬¬­­­¬­­¬­­¬¬¬­­­¬­¬¬­¬®®­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬­­¬­¬­¬­­­­¬¬­¬­®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬«¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬«¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬¬­­­­¬¬­¬¬®­¬¬¬¬­­¬®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬­¬¬­¬­­­¬¬­­¬®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬«¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬¬¬­­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­­¬¬¬­­¬­­¬­­¬¬¬®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®­­­®¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬«¬«¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬­¬¬¬­¬­­­­­­¬­¬­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬­¬¬¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­®¬¬­­­­­¬¬¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬®­¬¬­¬­¬¬¬¬¬­¬­­­¬¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬­¬¬¬­­¬¬¬­¬­­¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­­­­­¬­­¬­­¬¬­¬­¬¬¬¬­­­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­­­¬­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬«««««¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬­­¬¬¬¬¬®­­¬­¬­¬­¬­¬¬­¬­®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬­¬¬¬¬¬¬­­¬¬­­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬­¬¬¬­¬¬­¬¬­¬¬­­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬­­¬¬­­¬­®®®®®®­­®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬­¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­­¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­¬­¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬­­­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­­­­¬¬­¬®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬­¬­¬­¬¬­¬­­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­­®¬­­­­­­®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­­¬­¬¬­­­­¬¬­­¬¬¬¬­¬¬­¬¬¬­¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬­¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­­¬¬­¬­­¬¬­­­¬¬­¬­¬¬¬¬¬­­­¬¬¬¬­¬­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­®®­®®®­®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬­­¬¬­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­­­¬¬­¬¬¬¬¬¬¬­¬¬­­­­­¬­­­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬­¬®®¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®­®®®¬­¬­¬­¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­¬­¬¬­¬¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­­¬¬¬¬­­¬¬­¬¬­¬¬¬¬¬¬­­¬¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬¬¬­­¬¬­¬¬­­¬¬¬¬¬­­¬¬­¬­¬®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬®®®®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬¬­­¬­¬¬¬¬¬­­­­®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­­¬­¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬­¬­­­­¬¬¬¬­¬­¬®­­­­®®­®®®¬­®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬¬­­¬¬­¬¬¬­¬¬¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­®­­­­®®®®¬®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­¬¬­¬¬­¬¬­¬­¬­¬­¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬­¬¬­¬­­¬¬¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬®­­­®®®®®®®®®®®®®®®®®®®11¿®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬­¬¬¬­¬­¬¬­¬­­¬®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­­¬­­­­¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬­­¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬­­¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­­¬­­¬­­­­­¬¬¬®®®®®®®®®®®®®®®®®®®¿¿1®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬¬¬¬­¬­¬¬¬¬®¬¬¬¬®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­­¬¬­¬­¬¬­¬¬¬¬­¬¬¬­¬¬¬­­¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬­¬¬­­¬¬¬¬¬¬¬­­¬¬­¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬­¬­­­¬®¬¬­­¬¬­®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­¬¬¬¬¬¬¬­¬¬­­¬¬¬¬¬¬­¬­¬­­¬¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®¬­¬­¬¬¬¬®­®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬®¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®¬¬¬¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬­¬­­¬­¬­¬¬¬¬¬¬­­¬­¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬®­®®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­¬¬­¬¬¬­¬¬¬­¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬­­¬­¬¬­­¬¬­­¬¬­¬¬¬­¬­¬¬¬­­¬­®­®®®¬®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®¬®­¬¬¬¬¬­­¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬¬­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬¬­¬­­¬­¬¬­¬¬¬¬­¬¬­­¬­¬¬¬­­¬¬­­¬¬¬­­­¬­¬¬¬¬¬¬­¬¬¬¬¬®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­­­¬­¬¬­¬­­®®­­®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬­¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬­¬¬­¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬­­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬­­¬­¬¬­­­­®®­­¬¬®®­®®®®®®®®®®®®®®¿¿®®®®®®®®®­¬­¬¬¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®­­¬­¬¬¬¬­­¬¬­­¬¬¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬­­¬¬­­¬­­¬¬¬¬­­¬¬¬¬­¬¬¬¬­¬­¬®®­­®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®¬­¬¬¬¬­­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬¬­­¬¬­¬¬¬¬¬¬­­¬¬­¬­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬­¬¬¬¬¬­¬¬¬¬¬¬®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬¬­­¬­¬¬¬¬­­¬¬­¬¬¬¬­­¬¬­¬­¬¬­­¬­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­­­¬®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬®¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­­­­­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬­¬¬¬­­¬­­¬¬­­­­­­®­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®®­®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­­¬­¬¬­­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬­­­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­­¬­¬­­¬­­¬¬­¬¬¬¬¬¬®­¬®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬®­®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬®®®¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬­­¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬­¬¬­¬­­­¬­­­¬®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­¬­¬¬¬­¬¬¬¬­­­¬¬¬¬¬­¬¬¬­¬­¬¬­¬­¬­¬¬¬¬­¬­¬¬¬¬­­¬¬­¬¬­¬¬¬­¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­¬­¬­¬¬¬­­­¬®­¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­¬¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­­­­¬­­¬¬­­¬¬­¬­¬¬­­¬¬¬¬­¬­­¬¬­­­®­¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬¬­­­¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬¬¬­­¬¬­­­¬®­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬¬¬¬­¬­¬­¬­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®®®®®®­¬¬¬¬¬¬­¬¬­­¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬¬­¬­¬¬­­¬¬¬¬­­¬¬­¬¬¬¬­¬­¬­¬¬­¬­­­­¬®¬­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­­¬­¬¬­¬¬­¬­¬¬¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­­­­­¬­¬­­­¬¬¬®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®­­¬­­¬¬­¬¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬­¬¬­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬®¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­­­­¬­­¬­¬¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬­­­­¬¬¬­­¬­­­­­­®®®®®®®®®®®®®®®®¿®®®®®®®®®­­­¬¬­¬¬¬¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬®®®®®®11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­¬¬­­¬­¬¬­­­¬¬¬¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­¬¬¬¬¬­®¬¬®­­­­­¬­­­­­­¬¬­­­­­­®®®®®®®®®1®¿®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­­¬¬­¬¬­¬­®®®®®®®®®®1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­¬­­­¬­­­­­®­­­­¬¬­¬¬­­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬¬­­­¬¬¬¬­¬­¬­­­­­­­­­¬­­­¬­¬­­­­®®®®®®®®®1®1®®®®®®®®®®®®®®¬¬¬¬­­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬®®®®®®1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬­­­­­­­­­­­¬¬¬¬­¬­­­¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­­¬¬¬­­­­¬­­­­¬­­­¬­­­®®®®®®®1¿¿®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬­¬¬¬¬­¬­­®®®®®®®11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬¬­¬¬­­­­­­­­­­­¬­¬­­¬­¬­­­¬¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­­¬­¬­®®­­­­­­®­­¬­­­­­¬­¬¬¬¬¬®®®®®®®®¿1¿¿®®®®®®®®®®®®®®®®®¬­­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬­¬­¬¬­®®®®®1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬­­­­­­¬­­­­­­­¬­­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬¬­¬¬­­­¬­¬¬¬®­­¬­­®­­¬¬­¬¬­­®®®®®¿®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­­¬¬¬­¬­¬®®®®®®®®11¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬­¬¬­­­­¬¬¬¬­­­¬¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬­­­­­­¬®­­¬­­­­¬¬­­­®®®®®¿1¿®®®®®®®®®®®®¬¬¬­¬­¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬­®®®®1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­­­¬­¬¬¬­¬¬¬¬¬­¬¬¬­­¬¬­­¬­¬­¬¬­¬­­­¬¬­¬¬­­®¬¬­­­¬¬¬¬­®®®®®®®®¿¿®®®®®®®®®®®®­¬¬­¬¬¬¬­­­¬­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬®®®®1¿¿1¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬­­¬¬®­¬­­¬¬­¬­®­¬¬­­¬¬¬¬¬¬­¬¬¬­¬­¬­­­­¬­¬­­­­­­®®®­¬­­¬­­­­®®®®®®®®®®¿®®®®®®®®®®®®®®®®®¬¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬®®®®®®111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬¬¬­­¬¬­®­¬¬­¬­­¬¬¬¬¬¬­­¬¬¬­¬¬¬¬¬¬­­¬¬­¬¬­®­­­®­¬­¬­­¬­­¬­­­­®­®®®®¿¿1¿®®®®®®®®®®®®®®®®®¬¬¬­¬­¬¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬®®®®®®®®11111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬­­¬­­¬¬¬­®­¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬­­­®¬­­®­­¬¬­¬­­­¬¬¬­­®®®®®1¿¿1¿¿®®®®®®®®®®®®®®¬­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­®®®®®1111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­®®®¬¬­¬­¬­¬¬¬­­¬¬­¬­¬¬¬­¬¬­¬­­¬¬­­­®­®®®­­®­­­­­¬¬¬¬¬­¬¬¬¬¬­®®®®¿11111®®®®®®®®®®®®®®®­¬­­¬­¬¬¬¬­¬¬¬­­­¬¬¬¬¬¬­­¬¬¬¬¬­¬¬®®®®®®®¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬­­¬­¬­¬¬­¬­¬¬­¬¬¬­­¬¬­¬¬¬¬¬­¬¬¬¬­­­­¬­­¬­­­­¬¬¬¬­­­¬¬­­¬¬¬­­¬¬­­®®®®®®®®11¿¿®®®®®®®®®®­¬¬­¬­¬¬¬¬¬­¬®¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬®®®®11¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­­­­¬­®®®­¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­­­¬­¬¬­®¬­¬­¬¬­­¬¬¬¬¬¬¬­­¬®®®¿1¿¿®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬®®1111¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®®­­­­®¬¬®¬¬¬­¬¬­¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­­­®­¬­­­­¬¬­­¬¬¬¬¬­­­­®®®®®®¿¿¿¿®®®®®®®®®®®®¬¬¬­­¬­¬¬¬­¬¬­­¬­¬¬¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬®®®1¿11¿1¿¿1¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­®­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬­¬­®®­­®®­­¬­¬¬­¬¬¬®¬¬¬¬¬¬®®®®®¿¿11¿¿¿®®®®®®®®®®®®®®­­­¬¬­­­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬®®11111¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬®­­¬­­¬­¬­¬­­­¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬¬¬¬­­¬­®®¬­®¬­­­¬¬­¬­¬¬­®¬­¬­­­®®®®®®®®®®¿¿¿¿¿11®®®®®®®®®®®®®®®®®®®­¬¬­¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬­­¬®®111¿1¿¿¿1¿¿¿¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­¬­­­¬­­­¬­­¬¬­­­¬­¬­¬¬­­®®®®®®®®®®®®®®­­¬¬¬¬­­¬­­®®®®®®®®®®®®®®®®¿¿¿11¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬¬­­¬­¬¬¬¬­¬­¬¬­¬­¬­¬¬­­¬¬¬¬­¬­¬¬®1¿1111¿1¿1111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®¬­¬­­¬®­¬­­­­­­­­­¬¬­¬­¬¬­¬¬¬­­®­®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­­­¬­­®®®®®®®®®®®®®®®®®®®1¿1¿¿1¿1¿1¿®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬111¿111¿1¿¿¿1¿1111¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬¬­­­­¬­¬¬¬¬¬¬¬­®®®®®®®®®®®®®®¬­­­­¬¬¬¬­¬­­¬­®®®®®®®®®®®®®®®®®®¿1¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬®­11¿1111¿11¿¿1¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­­¬¬­¬­¬­­­¬­®®®®®®®®®®®®®®®®®®¬®­¬¬¬­¬¬¬¬­¬­­­­®®®®®®®®®®®®®®1¿¿¿1¿¿¿11¿11111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­­¬®111111¿¿111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬­¬¬­¬¬­­¬­®®®®®®®®®®®®®®®®®®®®­¬¬­¬­¬­­®­­­­®®®®®®®®®®®®¿¿¿¿111¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬¬­¬¬¬¬¬­¬­¬­¬¬­¬¬¬¬­­¬­¬­¬¬­¬­¬­¬¬¬­­­®¿1111¿1¿¿¿¿1¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­®®®®®®®®®®®®®®¿11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬­¬¬¬¬¬­­¬­¬­­¬¬­¬¬¬¬­¬¬­­­­¬­®¿111¿¿1¿11¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®¬¬¬­­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®­­­­®®®®®®®®®®®®®111¿111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬®¿111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬­¬­­®­­®®®®®®®®®®®®®®®®®®®®®®®®®­­¬®®­®®®­®®®®®®®®¿1111¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­®®1¿1¿1¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­­¬­¬­¬¬¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­­¬­­­®®®®®¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬­¬­­¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬­­­¬­­®®®¿1¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®¬­­­­­­¬­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬­®¿1¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬¬¬­­¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬­¬¬­­­®®®1¿¿1¿11¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­¬­¬­®®®­¿1¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬­¬­¬­­¬¬­¬­­­­¬¬®®¿1¿¿1¿¿¿¿11¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬­®®®¿¿¿1¿¿1111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬­¬­­¬¬¬¬­¬¬¬¬¬­¬¬¬­¬¬®®®¿¿®¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬­¬­¬­¬¬¬­­¬­¿¿¿1¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­®®­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬­­¿¿¿¿®¿¿1¿¿11¿¿¿¿¿¿1¿¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬­¿¿1¿¿®¿1¿¿¿¿¿¿¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿1¿11¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬¬1¿¿¿¿1111¿¿¿¿¿¿¿¿¿¿1¿¿¿¿11111111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿1¿¿1¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬«¬¬­¬¬¬¬­­¬¬¬­¬¬¬­¬1¿¿¿¿1¿1¿¿111111¿¿1¿11111¿¿¿¿¿¿¿¿¿¿1111111111¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿1¿¿¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿¿¿1¿11¿111®®®®®®®®®®®®­®®®®®®®®®®®®®¬¬®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬¬­¬¬¬««¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¿¿1¿¿¿¿¿11¿1111111111111111111¿11¿1¿¿1¿¿1¿¿11111111111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿¿1¿¿1¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®¿1¿¿¿¿1¿1¿11®®®®®®®®®­­®®®®®®®®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬­««««¬¬¬¬¬¬¬¬¬¬¬¬¿¿1¿¿¿¿¿1¿¿11¿¿11111111¿¿¿1111111111¿11¿¿¿¿¿¿11111111111¿¿¿1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿¿1¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®¿11¿¿¿1®®®®®®®®­­­­­­¬­­®®®®®­®®®­¬¬­®®®®®®®®®®®®®®®®®®­¬¬¬««««¬«««««¬¬¬¬¬­¬­¬¿¿¿¿¿1¿1¿¿11¿¿111¿¿¿11111¿1¿¿¿¿¿111111111111¿¿¿11¿1111111111¿1¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®111¿1¿1®®®®­­¬¬¬­­¬¬­®®®®®®®­®®®®®­®¬®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®¬¬­¬««««««¬«««««««««««««««««¬¬¬­¬¬¬¬¬¿¿¿1¿¿11¿¿¿111¿¿1¿¿1¿¿¿11¿1¿11¿¿¿¿¿1¿111111¿1¿1¿¿11111111111111¿¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿111¿®­­­­­­­­¬®®®®­®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬««««««««¬««««««««««««««««««««¬¬¬­¬¬­­¿¿¿¿¿¿¿¿¿111¿¿¿11¿¿¿11¿¿111¿¿¿¿¿¿¿¿¿¿1¿¿1111¿¿111111111111¿¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­­¬¬­¬®®®®­®®®¬®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®­­¬¬­¬««««««««««««««««««««««««««¬¬¬­¬11¿1¿¿1111¿¿¿1¿¿¿11111111111111¿1111111111111¿¿1¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬­­­­­¬­­­¬­­­®­­­­¬­®®®®®®®­¬¬­­¬¬¬¬¬««««««««««««««««««««««¬¬¬¬¿111¿111111¿¿¿1¿¿¿1¿1111111111111111111111111¿11¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­¬¬¬­¬­­¬¬¬¬­¬¬­¬­­­­­­¬­®­­­­­¬­¬­¬­®­®®®®®®®®®®¬­­¬¬­¬¬­¬¬¬¬«««««««««¬«««««««««««¬««¬¬¬¬11¿¿1¿¿111¿111¿¿¿1¿11111111111111111111111111¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®¿®®®®®®®®®®®®®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬¬­¬­­­­­­­­¬¬¬¬¬­¬¬­­¬¬¬¬¬­¬®®®®®®®®®®®®®®®­­¬¬­¬¬­¬¬««««««««««««««««««««««««««««¬¬¬¬¿11111¿¿¿¿111¿1111111111111¿¿¿¿1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®­®®®®®®®¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬¬¬­¬¬¬­¬¬¬¬­¬­­¬¬­¬­¬¬­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬®®­®®®®®®®®®®®¬­­­­¬¬¬¬¬««««««««««««««««««««««¬¬¬­¬¬¬111¿¿¿¿¿¿¿¿¿11111111¿¿¿¿¿¿111®®®11¿®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­­¬¬¬¬¬­¬­­¬­¬¬¬¬¬¬­¬­¬­­®®®®®®®®®®®®®®®¬­­­­¬­¬¬­¬¬««««««««««««««««««««««««¬¬­­¬¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿1®®®®­­®®®®®®®­­®®®®®®®®®­­­­­¬¬¬¬¬¬­­¬¬­­¬­­¬­¬­¬¬­­­¬¬­­¬­­­¬¬¬­­¬­­¬­¬­¬¬­­®®®®®­­®®®®®®®®®®®¬­¬­­­¬¬¬¬­­¬­­¬¬­®®¬¬­¬¬­««¬«««¬««««««««««««¬¬¬­¬¬¬¬­¬­¬­­­­­¬¬¬¬­¬­¬¬¬­¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®®­®®­­­¬¬­­¬¬­¬¬¬­­­­­­¬­¬¬¬¬¬­¬¬­­¬­­­¬­¬­¬¬­¬¬­­¬­¬¬­­¬­­­­­­®®­®®®®®®®®®®­­­¬­¬­­¬­¬¬­¬­­­­­­­¬­®®®¬¬¬««««««««««««««««««««¬­­¬­­¬­­­®­¬¬­¬¬¬­­¬«««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬­¬¬¬­­­­­­¬­¬¬­¬­¬¬­¬­­¬¬­¬¬¬­­¬­¬¬­­¬¬­­­­®®­®®®®®®®®®­­¬­¬¬­¬¬­¬­­¬­¬¬¬¬­­­­­¬­¬¬­®­­®®®¬­®¬¬¬¬¬«««««««««««««««««««««««««­¬­­­­¬¬¬¬­¬¬¬¬­­­¬­¬¬¬­«¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬­®­­®­­­­¬­¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬­­¬¬¬­­¬­­­­¬¬¬¬¬­­¬­­­­­¬¬®®®®®®®®®®®®®­¬­­­­­¬­¬¬­¬­­¬­¬­­¬¬­­­­­­­¬¬­¬¬­¬«««««««««««««««««««««««¬­¬¬¬¬­­¬­¬¬¬­®­­¬­¬­­¬««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­­¬¬­¬¬­­¬¬¬¬­­¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬­­¬­¬­­¬¬­¬¬¬­­­­¬¬¬®®®®®®®®®®®®®®®­­­­­­­¬¬­¬¬­¬­¬¬­¬¬­­¬¬­­¬¬¬­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬­¬¬¬­¬¬­¬­­¬®­¬­¬¬­­««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­­®¬­¬­¬¬­¬¬­¬¬¬­­­¬¬­¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬­¬­­¬¬¬­®®­®®®®®­¬­­­­­­¬¬­¬¬­­­¬­¬­¬­­­­¬¬­¬­¬¬¬¬­¬¬¬««««««««««««««««««««««¬¬¬¬¬­­¬­¬­¬­­¬¬®¬­­¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­®­­­­­­­­­­­­­­­®­¬¬¬¬­­­­¬¬­¬¬­¬¬­¬­­¬¬­­¬¬­¬­¬¬¬­¬¬­¬¬¬­­­­­­¬­¬¬­¬¬¬¬­¬¬­­¬­¬­¬¬®­­­­®®­®¬¬­­­­¬­­¬¬¬¬­­­¬­­­¬­¬­­¬¬­­­­­¬­¬¬­¬««««««««««««««¬¬¬¬¬¬­¬­­¬®¬¬¬¬¬­­¬¬­«¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­­®­­­­­­­­­­¬¬­¬¬­¬¬­­¬­¬­­¬­¬¬¬¬­­¬¬¬¬¬¬­­¬¬¬­¬¬¬­­¬­­¬­¬¬­¬­¬¬­­­­¬¬­­­­­­®­¬­­¬¬¬­­­­¬­¬¬­¬­¬¬­­­¬­¬­¬¬­¬­­­¬¬­­®®­­¬«««««««««««««««««««¬¬¬¬¬¬¬¬­­¬­­­®¬­­¬­­¬¬¬¬­¬¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­­­­­­­­­­­­­­­­­­­¬­¬¬­¬­­­¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­­¬¬¬­­­­¬­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­­­­®®­¬­­¬¬¬­­¬­¬­­¬¬¬¬¬¬­­­¬­¬¬­­­¬­­¬­¬®­¬­«««««««««««««««««««««­¬¬¬¬¬­­¬¬¬­¬®®¬¬¬¬¬¬­­¬­¬«¬¬««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­¬­­­­­­­­­­­­¬­­¬­¬­­­­­®­­¬¬­¬­¬¬­¬¬¬­¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬­¬¬¬­¬­­­¬¬¬­­­­¬¬¬¬­¬­­­­®­­­¬¬¬¬¬¬­¬­­­®¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­­¬­¬¬¬­­­¬­­­««««««««««««««««««««««««¬¬¬¬¬­­¬¬­­¬¬®­¬­¬¬­­¬­«¬«¬«««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬­¬­­­­¬­­­­­­­­¬­­­­®®®­­­¬­¬­¬­­­¬¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬­­¬¬­¬¬­¬­¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬­­­­­­­­®®­­­­¬¬­¬­­­¬­¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬­¬¬­­¬­¬¬­­¬¬¬¬««««««««««««««««««¬­¬¬¬¬¬¬­¬¬­¬¬­¬­®¬­¬­¬¬¬¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­­­¬­­­­¬¬­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬­­­­­¬¬¬¬¬­­­­­¬­¬­¬­¬¬¬­¬¬­¬¬­­¬¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­­­­­­­®®­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­­­¬­¬¬¬¬­¬¬­­¬­­­­­««««««««««««««««­¬¬¬­­¬¬­­¬­¬¬­®¬¬¬¬­­¬¬¬¬¬«««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­­­­­­­­­­­­¬¬­­­­­¬­­¬­¬¬­¬¬¬¬¬­¬¬­­­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬¬­­¬­¬­­¬¬­¬¬¬­¬¬­¬­­­­­­­®®­­­­¬­¬­¬¬¬¬­¬­¬¬­¬­­¬­­­­¬¬­­¬­¬¬¬­¬­¬­­­­«««««««««««««««««««««¬­¬­¬¬¬­¬¬­¬¬­¬¬¬­­¬¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­­¬­­¬¬¬¬­­­­­¬­­­­­­¬¬­¬¬­¬¬­¬¬­­­­­¬­¬¬­­¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬­¬¬¬¬¬­¬­¬­­­­­­­®­­­¬­­­¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­­¬­¬¬­¬¬­¬¬­­­¬­­­­­¬­«««««««««««««««««««««¬­­¬¬¬­­¬­­­­¬¬¬­¬¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­­¬¬¬¬­­­¬¬¬¬¬­­¬¬­­­®­¬­­¬¬­¬¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬­¬¬¬­¬­­¬¬¬¬¬­¬­¬­¬¬¬­¬¬­¬¬­¬¬¬¬­¬­­­­­­­®®®¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬­¬­¬¬­­¬­««««««««««««««««««««¬¬¬¬­¬¬­¬¬­­®¬­­¬¬­­¬¬­¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­­­­­¬­¬­­¬¬¬­­­­­¬­®¬®¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬¬¬­­­¬­¬¬­¬¬­¬­¬­­­­¬­¬¬¬¬­­­­­­­­­­­­­¬¬­­¬¬¬¬­­¬¬¬¬­­¬¬­­¬­¬¬­¬­¬¬­¬¬­­­­­­­­«««««««««««««««««««««««¬­¬­¬­¬­­¬­­®¬­®®¬¬­®­­¬¬««««««««««««««««®®®®®­­®®­­­­­­­­­­­­­­¬¬­­­¬­¬­­­¬­¬¬¬­¬¬¬¬¬¬­­¬­­­­­¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬­­®®¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬««««­««««««««««««««««««««¬««««««««««««««««¬­¬®®®­¬¬­¬¬­­¬¬«««««««««««««««««®®®®®®®®®­®®®¬­­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬­¬¬­¬­¬¬¬­¬­¬­¬¬¬¬­¬­¬­­­¬®®¬¬­­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬«««««««¬««¬«««««««««««««««««««««««««««««««¬¬®¬­¬­«««««««««««««««««««««®®®®®®­­­­­­®®®­­­­­­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­­­­­­¬¬­­­­¬­¬¬­¬­¬¬­¬­­¬¬­¬¬¬¬­­­­¬­­­¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬««¬««¬¬««««««««««««««««««««««««««««¬¬­¬¬¬¬¬¬¬¬­¬­¬¬¬­¬«««««««««««««««®®®®®®®®®®­­­­¬¬­¬­¬¬­¬¬¬¬­­¬¬¬­¬­¬¬¬­­¬¬¬­¬­¬¬­¬¬­­¬­¬¬­­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­­¬¬­¬­¬¬¬­®­¬¬­­­­¬¬¬¬¬¬¬­««¬¬«««««««««««««««««««««««««««««««««««¬¬¬¬­­­­­¬¬¬­­¬«««««««««««««««««««««®®®®®®®®®­­­­®­®®­¬­¬¬¬­­¬­­¬¬­¬­¬­¬­¬¬­¬­¬¬­¬¬­¬¬­­¬¬­¬¬­­­¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬­­¬­¬¬­­¬¬­­¬­­­­¬¬¬­¬¬¬¬¬¬¬¬¬««¬¬¬«««««««««««««««««««««««««««««««««­­¬®­¬­¬¬¬­«¬«««««««««««««««®®®®®®­­®®®­®®®®­­­¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­­­­¬­¬¬¬­¬­­¬¬­¬­­­¬­¬­¬¬¬­­¬¬¬­­¬¬­­­­­­¬¬¬­­­¬¬¬¬¬««¬¬¬««««««««««««««««««««««««««««««««««­­®¬¬­¬­­¬««««««««««««®®®®®®®®­­­­­­®®®­­®­­¬¬¬¬­¬¬­­¬¬¬¬¬­¬­¬­¬­­¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬­­­¬¬¬¬¬¬¬¬­¬­­­­¬­­¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬«««««««««««««««««««««««««««««««««­«­­¬¬®¬¬­¬¬­­«««««««««««««««««««««®®®­­­­­­­®­­­­­­¬­¬­¬¬¬¬¬¬­¬¬­¬¬¬­­­­¬¬­¬­¬¬¬¬¬­­¬­¬¬­¬¬¬¬¬­¬­¬¬­¬­¬­¬­­­­­¬­¬¬¬­¬¬­­¬¬¬¬¬¬«««¬««¬¬­«««««««««««««««««««««¬­¬«««¬¬­¬¬¬¬¬®­¬­­¬««««««««««««««««««®®®®®®®®®®®­­­¬­®­­­­­¬¬­­­¬­­¬¬­¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬­¬­­¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬¬­¬­­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬««­¬«««««««««««««««««««««««««¬¬¬­®¬¬¬¬¬­­¬¬­«¬««««««««««««««®®®®®®®®®­­­­­­­­¬­­­¬­­­¬¬­¬¬­¬¬­­¬¬¬­¬­­¬¬¬­­¬¬¬¬¬­­¬­­­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬¬«­¬«««««««««««««««««««««««««««««®­­­¬­­¬¬¬¬­¬¬««««««««««««««««««®®®­­­­­­­­­®®®®­­­­­­­­¬­­­­­­­¬¬­¬¬­¬¬­¬­¬¬­­­¬¬­¬¬­¬­­¬­¬¬­¬¬¬­¬¬­­¬­¬¬­¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬««¬¬¬¬¬«««««««««««««««¬«««««««««««««««¬­¬­¬¬¬¬¬­­¬­¬«¬¬«««««««««««««««««®®®­­­­­­­­­­¬¬­¬¬¬¬¬­­¬­¬­¬¬¬¬­¬¬¬­¬­¬¬­¬¬­­¬­­­¬­¬­¬­¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬­¬¬­­­¬¬¬¬«¬«¬¬¬««««««««««««««««««««««««««««««««¬­¬­¬¬­­¬­«¬«¬««««««««««««««««««®®®®®®®­­­­­­­­­­­®®­­­­­­­¬­­¬¬¬¬¬­­¬­­¬­¬­¬¬¬¬¬¬­¬­­­¬­­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬­­­¬¬¬¬¬¬­¬­¬­­¬¬­¬­¬¬­«¬¬¬¬¬¬«¬««««««««««««««««««««««««««­«¬¬¬­®¬­¬­¬¬¬¬««««««««««««««««««««««®®®®®®®®®­­­­­­­­­­­­­­­­­­­¬­­­­­­­¬­­¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­¬¬¬­¬¬­¬¬­¬¬­­¬¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬««¬¬«««¬««¬¬««¬«««««««««««««««««««««««¬«¬¬¬¬®¬¬¬¬­­¬¬¬¬¬«««««««««««««««®®®®®®®­­­­®®­­­¬¬­­­¬­¬¬­¬¬­­­¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬­­¬­¬¬­¬­­­­­¬¬­¬¬­¬¬­­­¬­­¬¬­¬­¬¬¬­¬¬¬­­¬­¬¬¬¬­¬¬¬¬¬­¬¬««««¬««­««¬««««««««««««««««««««««««¬¬­¬­¬¬¬­­¬¬««««««««««««««««««®®®®®­­­­­­®­­®®®­­­­­­­­¬¬¬­¬­­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­­­¬­­­¬¬¬­­­®®®­¬¬¬¬¬­¬¬­­¬­­¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬««««««««««««««««««««««««««««««««««­­­¬¬¬­¬¬««««««««««««««««­®­­­­­­­­­®®®®®­­¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­­¬¬¬­¬¬¬­­¬¬¬¬¬¬¬¬¬­­­­­­¬­­¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬­¬¬««««««««««««««¬¬«««««««««««««««¬«««««««««««««¬­­­®­¬­­¬¬­­¬¬­¬«««««««««««««««««««®®®®®­­­­­­­­¬­­®¬­¬¬¬­¬­¬¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­¬­¬­¬¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬®®®¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«««««««««««««¬««««««««««««««««««««««««««««¬«¬¬¬­¬­®¬­¬­¬­®®¬¬­®­­¬¬««««««««««««««««®­­­­­­­®­®®®­­¬¬­¬¬­­¬­¬­¬¬­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬¬­­­­¬¬­­¬­¬¬¬¬¬¬¬­¬®¬¬¬¬¬¬¬¬¬¬¬«­¬¬«¬«««««««««««««««««¬«««««««««««««««««««¬¬­­­®­¬¬®¬¬­¬¬­¬««««««««««««««««««®®­­®­­­­­­®®®­®­¬­¬¬­¬¬­¬­¬­¬­¬­¬­¬¬­¬¬­¬­­¬­¬¬­¬¬¬¬¬¬­¬­¬­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬«¬«««««««««««««««««««««¬«««««««««««««««««¬««««¬¬­¬­¬®®¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬«¬««««««««««««««««­­­­®­­­®­®®¬­¬­¬¬­¬­¬¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬«¬¬­¬¬¬¬¬¬¬¬¬¬«««««¬««««««««««««««««««««««««««««¬««­¬­­¬¬®¬¬¬­­­¬¬¬­­¬«¬«««««««««««««®­®®­­­­­­­­®®®®­­¬­¬­­¬­¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­­­­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬«««««««««««««¬­«««««¬¬®¬­¬¬®¬¬¬¬­­®¬­®®­¬¬­¬¬¬¬¬¬«««««««««««««««««®­­­­­­­®®®®¬¬­­¬­­¬¬­­¬¬¬­¬¬­¬­¬¬¬¬­¬­¬­¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬­¬««««««««««««««««««««««««««««««««««­««««¬¬­¬­¬­®­¬¬¬¬¬®¬¬«««««««««««««««««««««­®­­­­­­®®®®®®®®®¬­¬¬­¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­­­¬¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬«­¬«««««««««««««««««««««««««««««««««««««¬««««¬¬¬¬­®®®¬¬­­­¬­¬­­¬¬¬¬«««««««««««««««««®®®®®®­­­­­­­­­®­¬¬­¬­­¬¬­¬­¬¬­­­¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­¬¬¬¬¬­­¬¬¬¬­¬¬¬««««««««««««««««««««««««««««««««««««««««¬¬««¬¬­¬­®®¬­¬­­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬««««««««««««««®­®®®­­­­­­®®®®®­¬¬¬¬­­¬­­­­­¬­­¬¬¬¬­¬¬­¬­­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬¬­¬««¬««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬­¬­­¬­¬¬¬¬¬¬¬««««««««««««««««««««®­­®­­®­­­­®®®¬­¬¬­­­¬¬­¬¬¬¬­¬­­­¬¬¬­­­­¬¬¬¬­¬¬¬¬¬­¬¬­­­¬­¬¬¬¬­¬«¬¬¬¬­«««««««««««««««««««««««««««««««««««­«¬¬­¬­¬¬­­¬­¬¬­­­­­¬­¬¬¬¬«««««««««««««««®­­­­®­­­­­­­­­®­­¬­¬¬­¬¬­¬¬­­¬­­­¬­­¬­­­¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬­¬¬¬¬«««««««««««««««¬««««««««««««««««««¬«­­®®­¬¬­­­­¬¬­­¬­¬¬¬««««««««««««««««®­­­­­­­­­­­­®®®®®¬­­¬­¬­¬¬­­¬¬­¬­¬­¬­­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬­­­¬¬­­¬¬¬¬¬­­­¬¬¬««««««««««««««««««««««««««««««««««««¬¬¬¬¬¬­¬¬¬¬­­¬­¬¬¬­¬¬¬­¬­¬¬¬¬«««««««««««««««««­­­­­­­­­­­­­­­­®®®­­¬¬¬¬¬¬­­¬­­­­¬­­¬¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­¬¬¬¬¬¬¬¬¬­¬«¬¬««««««««««««««««««««««««««««««««««¬¬­­­¬¬¬¬­¬¬­¬¬¬­­¬¬­¬­¬¬­­¬«¬¬¬««¬«««««««««««««­­­­­­­­­­­®®­¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««­­¬¬¬­¬¬­¬­­­­¬­¬¬¬¬¬¬¬¬««««««««««««««««®®®®®­­®­­­­­­­­­®­­¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬­­¬­¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­­¬¬«¬¬­­­¬¬¬­­¬­¬¬¬¬­®­¬¬¬­¬¬««««««««««««««««««««®®®®®­­­­­¬¬¬­­¬¬¬­¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬«¬¬¬««««¬«««««¬«««««««««««««««««««««««««««««««««««¬«««««««««««««««««¬¬¬¬¬¬¬­¬­­¬­¬®­­­¬¬¬¬­­­¬¬¬¬¬«««««««««««««««««««¬®®®®­­®®®®­­®¬­¬¬­­¬­¬¬­¬¬¬¬­­¬¬¬¬««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«­­¬­­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­««««««««««««««««®®®®®®®®®®¬­¬­¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬­¬­¬¬¬­¬¬®®­­¬¬¬¬¬¬«­¬¬««¬«««««««««««««®®®®­®®®®­­­¬¬¬­­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬¬«««««­«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬­¬¬®¬¬¬­¬¬¬¬­¬«¬­¬¬­«­¬¬««««««««««««®®®®­®­®®­­®®­­­­¬¬­¬¬­¬¬¬¬­­­¬¬¬¬­¬­­­¬¬¬¬¬«­««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬­¬¬¬¬®¬¬¬¬­¬­¬¬«¬¬««««««««««««­®­®­­®®®®®­¬­¬­¬¬¬­¬­¬­¬¬­­¬­¬«««¬¬«««««««««««««««««««««««««««««¬««««««««««««««««««««««¬«««««««««««¬¬¬¬­¬­­¬¬®­¬¬¬­¬¬¬¬¬¬¬¬­«««¬«««««««««­««­®­®®®®®®­®­¬¬¬­¬¬­¬¬­­¬¬­¬­­¬¬¬¬««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬¬­¬®¬­¬¬¬­¬¬¬¬«««¬¬¬­«««««««««««««««««®®®®­®®®®­­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬«««¬­«««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««¬«¬¬¬¬¬¬¬¬®­­¬­¬¬¬¬¬«««¬¬««««««««««««««««®®®®­®­­®®®­­­­­¬­­¬­¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««¬¬­¬¬¬¬¬¬­¬­¬«««­¬««««««««««««««««¬«®®®®®®®®®®®®®­­­­­­­­­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬«¬«««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬«¬¬¬«¬¬­¬­¬¬¬­¬¬«­¬«««««««««««««®®®­®®®®­­­­­­­¬¬­¬¬­¬¬­¬¬¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬«¬¬­¬­¬¬¬¬­­®­­¬¬¬¬¬¬¬­¬««««««««««««««««®®®®­­®®­­®­­­­®¬¬­¬­­¬¬¬¬­¬­¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««­­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬«¬««¬««««««««««««««««««®®®®®®®®­­®®®­®®®­­­­­­¬­¬¬­¬­­¬­¬­¬¬¬­¬¬­¬¬«««««««««««««««¬«««««««««««««««««««««««««««««««««««««««¬¬­¬­­­­¬­¬¬¬¬¬¬«««««««««««­«««««««­­®®®­®­­®®­®­®­­­­­­­­¬¬¬¬­¬¬¬­¬­­­¬­¬¬¬¬¬¬«««¬««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«¬¬¬­­®­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««­®®­®®®®­¬­­­­­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬­­¬¬¬««««««¬¬««««««««««««««««««««««««««¬«««««««««««««««««««««««­¬¬¬¬¬¬®¬¬¬¬¬¬¬««««¬««««««««««««««««««®®­®®®®®®®®®­­­­­­­­¬­¬¬­¬­­¬­¬¬­¬¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬¬¬­®®®¬­®¬¬¬«««««««¬««¬«««««««««««««««®®®­®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­­­«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«­­®­¬¬¬¬¬¬¬«««¬«««¬«««««««««««¬««««««®­®®­®®®®®­®®®®®®®®®®®®®®®®®­­®®®­­­­­¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬«««­¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬«¬­­®®¬­«««««¬««««««««¬«««¬¬««««««®®®­®®®®®®®®®®®®®®®®®®®®®¬­­¬¬­¬®­¬¬­¬¬¬¬«««¬¬«««¬«««««««««¬¬«««««««««««««««««««««««««««««««««««««««¬¬«¬¬¬¬®­¬«««««««««¬««««««««««««¬««««­®®­®®®®®®®®®®®®®®®®®®®®®­®®®­¬­¬­¬¬­¬¬¬¬­­¬¬¬¬«««¬¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««­¬­­­¬®®¬¬¬«««««««««¬¬«««««««««««¬«««¬®®®®­®®®®®®®®®®®®®®®®®®®®®®­®®®­­¬¬¬­­¬¬¬¬®­¬¬¬¬¬¬¬¬¬««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬­¬¬­¬®¬¬¬««««««¬«««««««««««««««««««¬®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®­­­­®®®®®­­­®­­­¬­­­­¬¬¬¬­¬¬¬¬¬««¬¬­¬¬«««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬­¬¬¬¬®­¬­««««««««¬«««««««««¬««««®­®®®®®®®®®®®®®®®®®­®­®®®®®­­¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬«««¬¬¬««««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««¬««¬¬¬¬­«­­­¬¬«¬¬««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­¬®¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬««¬¬¬««¬«««««««««««««­­¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬­¬¬­¬®¬¬¬««««««««««««««««­­¬«­®®®®®®®®®®®®­®®®®®®®®®®®®­®®­­­­¬­®®¬­¬¬¬¬­¬¬¬¬¬¬«­¬¬¬«««««««««««««««¬««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬­¬¬­­­¬«¬««««««««««««««««¬¬­««««®®®®®®­®®®®®®®®®®®®®®®®®®®®®­®®­­¬®­®®¬¬­¬¬¬¬­¬­¬­¬¬¬¬¬¬«¬¬¬¬««««««««««««««««««««««««««««««««««««­««««««««««««««¬¬¬¬¬¬¬­­¬­¬¬¬­««««««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬­¬¬¬¬«¬¬«««««««««¬«««««««««««««««««««««««««««««««««««««««««««­­­¬¬¬¬­­­¬¬¬¬¬¬««««««««««««««««««¬®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬¬­¬¬­¬¬¬«««­¬¬««¬¬«¬««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬­¬¬¬®¬¬«««««««¬¬¬««««««««««­««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®­­¬¬­­¬­¬¬¬¬¬¬«­¬¬­¬««¬««««««¬«¬«««««««««««««««««««««««««««««««««««««««««««¬­¬­¬¬¬¬¬¬¬¬¬¬®¬­«««««««««««¬«««««««««««««­«««®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬­¬¬­­¬­¬¬¬¬¬­¬¬¬«­¬««¬¬««««««««««««¬««««««««««««««««««««««««««««««««««««­­¬¬¬¬¬¬¬¬¬««««««««««««««««««««««®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­¬¬­¬­¬¬¬¬­­®¬­¬¬¬¬«««««¬­¬¬¬««««««««««««¬¬«««««««««««««««««««««««««««««««««««««««««¬­¬­¬¬¬¬¬««««¬««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®¬­­¬¬­­­­¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««¬«««««««««««««««««««¬««««««««««««««««««««««««««««««««««««­¬¬­­¬¬­¬­®¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­¬­­­­­¬¬¬­­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­««­«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬­¬«®««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­¬­¬­¬­­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬««¬««««««¬««««««««««««««««««««««««««««««««««««««««­«¬­­¬««««««««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬¬­¬¬­¬¬­¬­­­¬¬­­¬­¬¬­¬¬¬¬«¬¬¬¬¬¬¬««¬«««¬«««««««««¬««««««««««««««««««««««««««««««««««««««««¬«¬¬¬««««««««««««««««««««««¬««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬«¬¬¬­¬«¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬««­«««««««««««««««««««««¬¬¬«««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬­­¬­­¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬«­«««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬¬­­¬­¬¬¬­¬¬­¬¬¬®¬¬¬¬¬¬¬«««««««¬«««««««««««««««««««««««««««««««««««««««««««««««¬¬««®¬­««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­¬¬¬¬¬¬­¬¬­­¬­¬­¬¬®®®¬¬¬¬¬«¬¬­¬¬¬¬­¬««¬«««««««««««««««««««««««««««««««««««««««««««««««¬«¬«¬­¬¬««­¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­¬­¬¬­¬¬­­¬¬¬­­®®®­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­««®¬«««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­­¬¬¬­¬­­­¬­¬­­­¬®®®­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­­¬¬¬«®««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬­¬¬­¬¬¬­¬¬­­¬¬®¬­¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬«««­¬¬¬¬««¬­«««««««¬««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­­¬­­¬¬­­¬­¬­¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­­­¬¬¬¬¬«¬««««««­««««««««««««««¬««««««««««««««««««««««««««««««««««««««««­­««®¬«««««««««¬«««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬«¬¬¬¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­­¬¬­¬­¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬«¬«¬­«««««¬««««««««««««««««««««««««««««««««««­««««««««««¬¬­¬¬¬¬­««««­­¬¬«««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­¬­¬­­¬­¬¬­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬«««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬¬¬¬«®¬­««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬¬¬¬¬­­¬¬¬­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬«¬««««¬««¬«¬­«««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­¬­««­®­¬¬««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬®®¬­¬¬­«¬¬¬¬¬¬­­­¬¬¬¬¬­¬««««««¬«««¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­¬¬««®¬¬¬¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬¬¬¬­­­­¬­¬®­¬­¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬­®¬¬¬­¬¬«««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­«««¬®¬¬««««¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­­¬­­­­¬­¬¬¬¬®®¬¬­­¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬­¬­««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««¬««­­««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬­¬¬­¬¬­¬­­®¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬­­¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬®­¬­¬¬«««««««««¬«««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬¬­­¬­­¬¬¬­¬®®®¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬­¬¬¬­¬­¬«¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬®¬¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­¬­¬­¬­­­¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬¬­¬­¬¬­¬«««««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬®¬¬¬¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬­¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬­­­­­­¬¬¬­­­¬­¬­¬¬¬¬«««««««««¬«««««««««««««««««««««««««««««««««­«««¬«««««««««««««««¬¬­®¬­¬«¬«««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬­­­­­¬¬­¬¬­¬­¬¬­¬¬¬­¬¬­­­­¬­­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬¬¬­¬­­¬­­­­¬¬¬­¬¬¬­­¬¬­­¬¬¬­¬¬­­¬¬¬¬¬­¬¬««««¬««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««­­¬¬¬¬««««««««««««««««««¬«®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­¬­¬¬­­¬¬­¬¬­¬¬­­¬¬¬­¬¬­¬¬¬¬¬­¬­­¬¬¬¬­¬¬­­¬¬­¬­¬««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬¬¬®¬«««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­¬­­¬­¬¬­¬¬­¬¬­­­¬¬­¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­­¬­­¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬««««­­­¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­®­­­¬¬­¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­­¬¬­­¬­¬¬¬¬­­­¬­¬¬­­­­­¬¬¬««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬¬«¬««­¬«««««««««««««««««««««««®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬­¬­¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬««««««««««««««««¬««««««««««««««««««««««««««««««««««««««¬«­¬­«««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬¬­­¬¬­¬­¬­¬¬­­­¬¬­­¬­¬¬¬­­­¬­¬¬¬¬­­­¬­­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««­«««««««««««««««««««¬««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­­­¬¬­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬­¬¬­¬­¬¬­¬­­¬­­­¬¬««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­¬««««««««««««««««¬««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­¬­¬¬­­­¬­¬¬­¬¬­­®­¬¬¬­­¬¬¬­¬¬­¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬«¬««««««««««««««««««««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­¬¬­¬­­¬­¬¬­­¬¬¬­¬­¬­¬¬­­¬­¬¬­¬¬¬¬­­¬­¬­¬¬¬¬­¬¬­­¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««¬«««­­««¬««««««««««««««««««¬«««¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬­­­­¬¬¬¬­¬¬­¬¬®¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬«««««««««««««««­«««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­«¬««««««««««««««««««««««¬¬­¬¬¬¿¿¿1¿1¿¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­¬¬¬¬¬¬­¬­¬¬¬¬­¬¬­­¬­¬­¬¬­¬¬¬¬¬­¬¬­­¬­¬¬¬¬­¬¬­¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««®¬««¬«««««¬««««««««««««««««­¬¿1¿11¿1¿11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­®­­¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬®®®¬­¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬­­«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬««««««««««¬«««««««««««¬¿¿1¿¿¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬­­¬­­­­­¬¬­¬­¬­­¬­¬­­¬­¬¬­¬¬¬¬­­¬¬­¬¬¬«¬«««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««««­«¬«««««««««««««««««««««««««««¬¬«¬1¿¿1¿¿1¿¿11¿¿¿1¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­­­¬¬­¬¬¬­­¬¬­­¬¬¬­®­­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­««««««««««««««­¬¬«««««««««­¬¬¬¬¿¿11¿¿¿1111¿¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬­­­¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬««««««««««««««««««««¬«««««««««««««««¬««««««««««««««««««««««««««««««««««««««««­¬«««««««««««««¬¬­««««««¬1111111¿¿1¿11¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­¬­¬­¬¬¬­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬­¬¬¬««««««««««««««««««««««««««««««««¬««««««««««««««¬«««¬«««««««««««««««««­¬­««««««««««««­«««¬¬¿¿111111¿¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬­­­­­­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­¬¬¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬«¬««««««««««««««««««¬¬¬¬¬¬¿¿11¿1¿¿¿1¿1¿11¿¿1¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬­­¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««­«««««««««««««««««««««««««««««««««««¬¬«««««««¬«««««««««¬«««««««««¬¬¬­¬¿¿¿1111¿1¿¿¿¿11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬­¬¬­¬¬­¬­¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««­¬¬««««««««««««««««««««¬¬«¬¬­¬¬«111¿¿11111¿1¿¿11111111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®­¬¬­­®¬­¬¬¬­¬¬®­¬¬­­¬¬­¬¬­¬­¬¬­­¬¬­¬¬¬­¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬«««««««««««««««««««««««­¬«1¿¿11¿11111111¿¿1111¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬­¬¬­¬¬¬¬­¬¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬««¬««««««««««««««««««««««««««««««««««««««««««¬««««««««­««««««««««««««««««¬¬¬«««««««««««¬«¬«««««««¬¿¿1¿1111¿¿11¿¿1¿1111111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­¬¬­¬¬¬­¬­¬¬­¬¬­¬¬¬­¬­¬­¬­¬¬­¬¬¬­­¬¬­¬­¬­­¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«««««««¬««««««««««­¬¬1¿¿1¿¿1¿¿1¿111¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬¬¬¬¬®­­­¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬­­¬¬¬­­¬­¬¬¬¬¬¬¬¬­¬¬¬­«¬¬¬«¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««¬­«¬¬«««««««««««¬«««­«««««««¬¬¬­¬«¿¿¿11¿1¿¿111¿1111¿¿1¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬­¬¬­¬¬­­¬¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬«¬¬«««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««¬¬««««««««««¬«««««««­¬¬¬«1¿1111¿¿1¿1111¿¿¿¿¿¿1¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­®®­­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­­­®¬¬¬¬¬­­­­¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬­­¬«««««««««««««««««««««««¬¬¬¬¬¬¿¿¿¿¿¿¿¿¿¿111¿¿¿1¿1¿¿1¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬®®¬¬¬¬­¬¬­¬¬­­¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬««««««««««««««¬««««¬¬¬¬1¿¿¿¿¿¿1¿11¿11¿¿11¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬­­¬¬­­­¬­¬¬­¬¬¬¬¬­¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««¬¬¬¬¬¬¬«««««««««««««««¬¬¬¬«­¬¬¬¿¿¿¿¿¿11¿11¿¿¿1¿1¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®­­®®­¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬­­¬­¬¬¬­­¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«¬«««««««««¬¬¬¬¬¬­¬¬¿¿111¿¿¿¿¿¿¿1¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­®­¬¬¬­­­¬¬­­¬¬­¬¬­¬¬­¬­¬¬­­¬¬­­¬¬¬¬¬¬­­¬¬­«¬«««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««¬¬««««««««««««««««««««««¬­¬««««««¬«««««­«¬¬¿1¿¿1¿¿¿11¿¿11¿¿1¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬­¬­¬¬­¬­­­¬­­¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««¬­¬¬¬¬¬¬««««««««««¬¬¬¬¬¿¿¿¿¿1¿¿¿¿¿1¿¿1¿¿1¿¿1¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬­­­¬¬­¬¬¬­¬­­­®­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬««««««««¬¬­¬«­«¬­¬¬¬­¬¿¿¿¿¿¿¿11¿¿¿1¿¿1¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­¬®¬¬¬­­¬¬­­­¬¬­¬­­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬«««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««¬¬««««««««¬­««««¬¬¬¬¬¬¬¿¿¿¿11¿¿¿¿11¿¿¿¿1111¿111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®­­¬­¬­­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«««¬¬¬«««««¬¬««¬¬¬¬¬¬¬¬«¿¿111¿¿1111¿¿¿¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®¬­­¬¬­¬¬¬¬­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬««¬¬­¬¬¬«««««¬««¬¬¬¬¬¬¿111¿¿¿¿¿¿¿¿¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­­­­¬­¬¬­¬­¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«­¬¬¬¬«««««««««¬¬««««¬««¬¬¬¬¬¬¬1111¿¿¿¿¿¿111¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬¬¬«««¬««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­­¬­««««««««««««««­¬¬¬¬¬¬¬¿1111¿¿¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­­¬­­¬­¬¬­­¬¬­­¬¬­¬¬­­¬¬¬¬¬¬¬¬­¬¬¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬­¬¬­«««««««««««¬¬¬¬¬¬¬¬­¬111¿¿¿11¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®­­­­­­¬­­¬¬¬­¬­­¬¬¬¬¬­­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««¬««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««¬¬¬¬¬­¬¬««««¬«««¬«««««¬¬¬­¬¬¬­¬¿¿11¿¿¿111¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««¬«««««««««««««««««««««««««««««««¬««¬¬¬¬¬¬¬««««««««««««¬¬¬¬¬¬¬¬­¬¬¬¿¿11¿¿¿¿11¿¿11¿¿1¿¿¿®®®®®®®®®®®®®®®®®®­®®­®®­¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«««¬«««¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««­««««««««««¬«««««««««««¬¬¬¬¬­­¬««««««««««««««««¬¬¬¬¬¬¬­¬¿11¿¿¿¿¿1¿1111¿¿¿¿¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬­¬­¬­¬­­¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««­«««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬¬­­¬¬««««««««««««««««««¬¬­¬¬­¬¬¬¬¬­¬1¿¿¿11¿¿¿¿1¿111¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­¬¬­­¬¬­¬¬­¬­¬¬¬¬­­¬­¬¬¬­¬­¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬«¬«««««««««««««¬¬¬¬«««««««««¬¬­¬¬¬¬««««««««««««««««««««««««««¬««¬««««««««««««««­¬¬¬­¬¬««««««««««¬­¬¬­­¬¬«1¿¿¿111¿11¿11¿111¿®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬¬­­¬¬¬¬­¬­¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬«««««««««««««««««««¬««««««««­«««««««¬«¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««­­¬­¬¬««««««««¬¬¬¬¬¬­­¬¬1¿¿¿11¿¿¿111¿11¿11¿¿1¿¿¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­­­¬¬¬­¬¬¬­¬­¬¬­¬¬¬¬¬¬¬®­­¬­¬­¬¬¬¬¬¬¬«¬«««««««««««««««««««««««¬¬«¬¬¬¬«««««««««««««¬­¬¬¬¬««««««««««««««««««««««««««««­«««««««««««««¬¬¬­¬­««««¬­¬¬­¬¬¬¬¬¬1¿¿¿¿¿¿111¿11111111¿¿¿¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­¬­¬¬¬¬¬­¬­­­¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬­¬¬¬«­««««««««««««««««««««¬«««««««««««¬­­¬¬¬­¬««««««¬«««««««««««««««««««««««««««««««­¬««««¬«¬¬¬­¬¬¬¬¬¬1¿¿1¿¿¿¿¿¿11¿1111¿¿1¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­­­­¬­­¬¬¬¬¬­¬¬¬­¬­¬¬­­¬­¬­­­­¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬«¬«««««««««««««««««¬¬««¬¬¬¬¬««««««««­­¬¬¬­­«¬««««««««««««««««««««««««««««¬«¬«¬«««««¬«««««««¬«¬­«¬«««««¬¬¬¬¬¬¬­¬¬¬1¿¿¿1¿1¿1¿111¿¿¿1¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®­­­­­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬­¬¬­¬­­¬¬¬¬¬¬«««««««««««««¬¬¬¬¬¬««««««««¬¬¬­¬­­««¬¬««««««««««««««««««««««««««««««¬¬¬««««««««««­«¬­««««««¬¬«­¬¬¬¬¬11¿1¿11¿¿1¿¿¿1¿1¿¿11111¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬¬­¬­­­¬­­¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­®¬¬­­¬­¬¬¬¬«««¬¬««««««««¬««¬«¬­¬««««««««««¬­­¬­¬­¬«««¬««««««««««««««¬«¬«««¬«««««««««««««««¬¬¬«««¬¬¬¬¬¬11111111¿¿1¿1¿¿11¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­¬­­­¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬­¬­®­¬­­¬¬­««««­««««««««««««««««¬¬¬¬««««««¬«¬¬¬¬««««««««««««««««««««««¬¬¬¬«««««««««««««««««¬¬«­«¬¬¬«¬¬¬¬¬¬¬­1111111111¿¿111111¿11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬­¬­¬¬­¬¬¬­¬¬­­­¬¬¬«««««««««««««««««««««««««««­«­­¬¬««««««««««¬¬«««««««««««««««««««««¬«««««««««««««««¬¬®¬¬¬««««­¬¬¬¬«111111111¿11¿¿111¿111¿1¿®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­­¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­¬­¬¬­¬­¬¬­¬­¬¬««««««««««««««««¬¬¬¬¬«¬¬¬¬¬««««««««««¬¬¬¬««««««««««««««««««««¬««««««««««««««««««¬¬¬««««««­¬¬¬¬­¬¬111111111111¿¿¿¿¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬¬¬¬¬­­¬¬¬­¬¬­­¬­¬­¬¬¬­¬­¬¬¬­¬¬­­­¬¬¬­¬­­¬¬­­¬¬¬¬¬­««««««««««««««­­­«¬¬««««««««««««««¬­«¬««¬¬««««««««««««««««¬««««««««««««««««­««««««««¬¬¬¬¿111¿1111¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®¬­­®­¬¬­¬­­­¬¬­¬¬¬­¬¬¬¬­­¬­¬¬¬¬¬¬¬­­¬­­¬¬­­¬­­­¬¬¬­««««««««¬«««««¬¬¬¬¬¬­¬¬««««««««««««¬¬«««¬««««««««««««««¬««««««««««««««««««¬¬«««««««««¬¬¬¬¬­«¿111111¿¿¿¿¿¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­¬­¬¬¬¬¬­¬¬­­­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬«««««««««««¬¬¬¬¬­¬¬­­¬««««««¬««¬¬­­¬­­¬««««««««««««««««««««««««««««««««««««««««««­¬¬««««««««¬«¬¬¬¬11111111¿¿¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­¬¬¬¬­¬¬¬­¬¬­¬¬¬¬­¬­¬¬¬­­¬­¬­¬­¬­­¬­¬­¬¬¬¬¬¬­«¬¬««««««««««««««¬­¬¬¬¬«««¬¬««¬¬¬¬««««¬«¬««««««««««««­««««««««««««««««««««««««¬¬¬«««««««««¬¬¿1111111¿¿11111®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®­­­­¬¬­­­­­­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬¬¬­­¬­¬¬¬¬­­­¬¬¬¬­¬«««««««««««««««««¬«¬­¬¬¬¬««««««««««­¬¬¬«¬­­¬«¬¬««««««­««««««¬¬«««¬«««««««««««««««¬¬­¬«««««««««¬««¬¬¬¬¬¬¬11111111¿¿¿¿¿¿¿¿1111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬¬­¬¬­¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬««««««««¬«««««««««¬¬­­¬¬¬¬«««««««¬­¬­­«¬«¬«««««««««««««««¬«««««¬««««««««««««««­­««««««««««««¬¬1111111¿11¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®®®®®®®®®­¬­­­­¬­­­­­¬¬¬¬¬¬¬®­­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««¬¬¬­¬¬«¬«««««««¬¬­«¬­¬«««««««««««««¬«««««««««««««««¬«­¬¬¬«««««««««««««¬¬«¬¬1111111¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®­®®®®®®­­¬¬­¬¬­¬­¬­¬­¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬«¬¬¬¬¬¬¬¬«««««¬««««¬­¬««¬¬¬¬«««««««¬­««¬«««««««««««««««««««««¬­¬¬¬¬«««««««¬¬¬¬­11111111¿11¿11¿®®®®®®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬®­­­­¬¬­­¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬««¬««¬¬¬¬¬¬¬­¬««««««¬¬¬¬¬««¬¬«««««««­¬««««««««««««««««««««««««««­¬¬¬¬­««««­¬¬¬¬¬¬­11111111¿11¿11¿1®¿®®®­®®®®®®®®®®®®­®®®®®®®®®®®®®®®­®­®®­­¬­­­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬««¬«¬¬¬­­¬¬¬¬¬«««¬««««««««¬¬«««­««¬«««««««««««««¬«««««««««««««««««««­¬¬¬¬«««««««¬¬¬¬¬¬¬¬¬111111111111¿¿11¿®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­¬¬­¬­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬­¬¬««««««««««¬«¬­¬¬««««««««««««««««««««««««««««««¬¬¬¬¬¬«««««««««««¬¬¬¬¬¬¬¬¬11¿11111111111¿1¿¿®®®¿®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­­¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬­¬¬¬¬­­­¬¬¬¬­¬¬¬¬­­¬¬­­­«¬¬­¬¬¬¬­¬¬¬«««««««««««««­¬«««¬¬¬¬««««««««¬«««««¬««««««««««««¬¬¬¬¬¬««««««««««««««¬¬¬¬¬¬¬1111111111¿¿1¿¿¿®®®®®­®®®®®®®®®®®®­­®®®®®®®®®®®­¬¬­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬««««««««««««­««­¬««¬«««««««««««««««««««««««««««««¬­¬­¬¬­­¬­«««««««««««¬¬¬¬¿1111111111¿¿¿¿¿®®®®®®®®®®®®®­®®®®®®®®­®®®®¬¬¬­¬­­¬¬¬­¬¬­¬¬¬¬­­®­­¬¬­­¬¬­­¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬««­¬¬¬¬¬¬­¬­¬¬«««««««¬««««««¬¬««««««««««¬««««««««««««««««««¬¬¬¬¬­¬­¬¬¬«««««««««¬¬¬¬¬¬¬¬¬¬11111111111¿¿¿¿1¿¿¿¿®®®®®®®®­­®®­®­­®®®­®®®®®®®®®®®­­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬­­¬­­­­¬­¬¬­¬­¬¬¬¬­­¬¬¬­¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬¬­­¬¬¬¬«««««««««««¬¬¬«««««­¬«««««­«««««««««««««««««««««¬¬¬­¬¬«««««««««««¬¬¬¬1111111111¿¿1¿¿¿11¿¿¿®®®®®®®®®­®®®®­®®­®®­­®­®®­®®®®®®®®®­¬¬­­­¬¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬««««¬¬¬¬¬¬­««««««««¬¬««¬¬¬¬¬««¬«¬¬«««««««««««­«««««««««««««««««««¬¬¬¬¬¬¬«««««««««««««««««¬¬­«¬¿11111111¿1¿¿¿¿¿¿¿®®®®®®®®®®®®­®­®®®­­®®®®®®­­­®¬­­¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬««««¬¬¬¬¬¬¬¬««««««««««««««­¬¬««««¬¬¬¬¬«««««««««««««««««««««««««««««««¬­¬¬¬¬­««««««««««««««¬¬¿111111111¿¿1¿¿¿®®®®®®®­®®­®®®®®®®­®®®®®®­¬­­­¬¬¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬¬­¬­­­¬­¬­¬¬¬­¬¬¬¬¬¬«««¬¬«¬¬¬¬¬¬««««««««««««¬¬¬«««««­«««««¬«««««««««««««««««««««¬¬¬¬¬­¬¬«««««««««««««««¬¬¬1111¿11111¿¿¿¿¿¿¿®®®®®®®®­®­®®®®­®®®®®®®®®®®­­­®¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬«¬¬¬¬¬««««««««««¬««¬¬«¬«««««««««««««««««««««««««««¬«¬¬­®¬¬¬­­¬¬¬«««««««««««¬¬¬¬¬«1¿1¿¿111¿¿¿1¿¿1¿¿®®®®®®®®®®®®®®­®®­®®­®®­­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬­­­¬¬­¬¬­¬¬¬¬¬­­­¬¬¬¬¬¬«««¬¬¬¬¬­¬¬¬¬¬¬¬«¬«««««««««««¬¬««­¬¬«¬««¬««««««««««««««¬«««««««¬¬­¬¬¬«««««««««««««««¬¬¬¬¬¿1¿¿¿¿¿¿¿¿¿¿¿®®®®­®®®®®®®®®®­®­­®®®®®®®®­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬­¬­­¬¬¬­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««¬¬­¬­­¬¬­«««¬¬««¬«««¬¬¬¬­««««««««««««««««««««««««««««¬¬¬¬­¬¬¬¬­¬«««««««««¬¬¬¬¬¬¬¬111¿¿1¿¿¿1111¿11®®®®®­®®®®®®®®®®­®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­­­¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬«««««««¬««­¬¬«¬­¬««««««««««««««««««««««««««««««¬­­¬¬­¬««««««««««««««¬­¬¬11¿¿11¿11¿¿11¿1¿¿¿¿¿¿¿®®­®­­­®­®­­­­­­­­­­­®®®­®­®®®®­¬¬­¬¬­­­¬­¬­¬¬¬¬­­¬¬­¬¬­¬¬­­¬¬­¬¬­¬¬¬­¬­¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«¬«¬¬¬¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««¬­¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬111111¿11¿¿¿¿1¿111¿¿¿¿®®­­®­­­­­­­­­­­­­­­­®®®­®®®®®­­­­¬¬¬¬¬¬¬­¬¬¬¬­¬­­¬¬­¬­­¬­¬¬¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬¬¬¬¬¬¬««««¬«««¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««¬¬­­¬­¬¬¬­¬­­­¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬1111111111¿¿11¿11¿¿¿®®­®®­­­­­®­­­­­­­­­­®®®®®®¬®­­¬¬­¬­­¬­¬¬¬­¬¬­¬­¬¬­¬­¬­¬­¬­¬¬­¬¬­­¬¬­¬­¬­¬¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬««««««««¬¬¬¬­¬¬­­¬¬­­¬¬¬¬«««««««««««««««««««««««««««««¬¬¬­¬¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¿1111111¿¿111¿1¿¿¿®­®­­®­­­­­­­­­­­­­­­­­­®®®®¬­­¬­­¬­¬¬¬¬¬­­¬¬­­¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬­¬­­­¬­¬­­¬­­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬«¬¬¬¬¬¬¬¬¬¬¬«««««««¬¬­««¬¬¬¬¬¬¬¬¬¬­­¬«««««««««««««««««««««««««««««««¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬111111¿¿1111¿¿¿¿¿¿®®®®®­­­­­­®­­­­­­­­­­­®®­®®®®¬¬­­­­­¬­­¬­¬¬­­¬¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­­­­¬­¬¬¬­¬¬¬¬­­¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬««««««¬«««««¬««¬¬­¬­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬1111¿¿¿1¿11¿¿1¿111¿¿®®®­­­­®­­­­­­­­­­­®­®®­®®®®®®®¬­¬¬­­¬­¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬­¬­¬¬­­­¬­¬­¬¬¬­¬­¬¬­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬«¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬«««¬¬¬¬¬¬¬¬­¬¬¬¬¬¬««««««««««««««««««««««««««««««««««¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬1111¿1¿¿1111¿¿1¿111¿¿¿1®¿®®­­­­­®­®®­®­­­­­­­­­®­­­­®­­®­­¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­«¬¬¬¬¬¬¬¬¬¬¬««¬«««¬««««¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««¬¬¬­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬111111¿¿11¿¿1¿1¿¿¿¿¿¿¿1®¿®®®®®®®­­­­®­­®­­­­­­­­­­­­®­®®­¬­­­¬¬¬­¬­¬¬­¬¬­¬­¬­¬¬¬­­¬¬­­¬­¬¬­­­¬­¬¬­¬­­¬¬­¬¬­¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬«««¬¬¬¬¬¬«««««««¬««««¬¬¬¬¬¬¬­¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬11¿1¿111111¿¿¿1¿1¿¿®®®®®®®­­­­­®®­­­­­­®­®®­®­­®­¬­­¬¬¬¬­¬¬­¬¬­¬­­­¬­­¬¬¬­­¬­¬­¬­¬¬­¬­¬­¬¬­¬­¬­­­­­¬¬¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬«««««««««««««­¬¬¬¬¬­¬¬¬¬¬¬¬«««««««««««««««««««««««««««­¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¿1¿1¿11¿1111¿¿¿¿¿¿®¿¿1¿®®®®®®­­­­­­­®­­­­­­­­­­®­®­®­®®®®­­¬¬­­­¬¬­¬­¬¬­¬¬­¬¬¬­¬¬­­­¬¬­¬­¬¬­­¬¬­­¬¬­¬¬­¬¬¬­­­¬¬¬­­­­«¬«¬¬¬¬¬¬¬¬­¬¬¬¬«¬«««¬«««¬«««¬««­¬­­­¬¬¬¬¬­¬«¬««««««««««««««««««««««««««««­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¿1¿11111111¿¿¿¿¿®®¿®®®®®®­­­­­­­­®­­­­­­­­®­®®­®®®®®®­­­¬¬­­­¬­¬­­®¬­­¬¬¬­¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬­¬¬­­¬¬­¬­¬¬­¬­¬¬­­®­­¬­¬®®¬­¬¬¬¬«¬¬¬¬¬¬¬­¬¬¬««««¬««¬««¬­­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬­­­¬¬­¬¬¬¿¿1111111111111¿111®®®®®®­­­­®­­­­­­®®®®®®®®®®­­­­­¬­¬¬¬­¬¬­­­­­­­¬­¬¬¬­¬­­¬­­­­¬¬­­­­¬¬¬­¬¬­¬­­­¬¬­¬­¬¬®¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««­¬¬«¬¬¬¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««««««««««««®¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬11111111111111¿¿¿1¿®®®®®®®®­­­­­­­­­­­­­­­­­®­®®­­®®®®®­­­¬­­¬¬¬¬­¬­¬¬¬¬­¬­¬¬­­­­¬¬¬¬¬­¬­­¬¬­¬­¬¬¬­­¬¬­­¬­­¬¬¬­¬¬­­¬­¬¬¬¬­­¬¬¬¬¬­¬¬¬¬¬«««««««««««¬¬«¬¬¬­¬­­­­¬«««««««««««««««««««««««««««««««««­­¬¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¿111111111111¿¿¿¿11¿®®®®®®®®®®®­­­­­­­­­­­­­­­­­®®®­­­­®®®­­­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­¬¬­¬­¬­¬­¬¬­­¬­¬­¬¬¬­¬¬¬¬­­¬¬¬¬¬¬«¬¬¬¬¬¬¬¬«««««««««««¬¬¬¬¬¬¬¬¬¬¬¬­­«««««««««««««««««««««««¬«¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬1111111111¿111¿1¿1¿¿1®®®®®®®®®®®­­­­­­­­­­­­­­®­­®®®®®®­®®®®®­¬¬¬¬­¬¬­¬­¬­¬¬¬­¬¬­¬­­­¬¬¬¬¬¬¬­¬¬¬­¬­¬­­­¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®¬¬«««««««««««««¬«««««««««««««««¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¿11111111¿11111¿¿1¿¿1¿®®®®®®®®­­­­­­­­­­­­­­­­­­®®®®®®®­®­®®®®¬­¬¬­­­¬¬¬¬­¬­¬¬­­­­¬­¬­¬­¬­¬­­¬­­¬¬­­¬¬­¬­­¬¬¬­¬­¬¬­¬­­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬¬¬¬­¬¬¬¬¬¬­¬««««««««¬«««««««««««««««««««­¬¬¬¬­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¿1111¿¿1111¿1¿1¿®®®®®®®®®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­­­¬¬­­­­­­¬¬¬¬­­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬­¬­­­¬­¬¬¬­­¬¬¬«««««««««««««««««««¬¬¬¬­¬­­¬¬¬¬¬««««««««¬«««««««««««¬¬¬¬¬­­­¬¬¬¬­­­¬¬¬­¬­¬¬¬¬¬­­­¬¬­¬¬¬­­¬¬­¬¬1111111¿1¿¿11¿¿1®®®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬­­­­¬­¬­­¬¬¬¬­¬­¬¬¬¬­¬¬¬­­¬­­­­¬¬¬­­­­­¬¬­­¬­­¬¬¬¬¬¬¬­¬­¬¬¬­¬­¬¬¬¬¬­­­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬««««««««««««¬«¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««¬­¬­­¬¬­¬­¬­¬­¬¬¬¬¬­­¬¬¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬­¬¬1111111¿1¿1¿¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬­¬­¬­¬¬­¬¬¬¬¬¬­¬­­¬¬­­­¬­¬­¬­¬­­¬¬¬­­­­­­­¬¬¬­­¬¬¬¬¬­¬­¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬««««««««««««««««¬¬­¬¬­¬¬¬««««««««««««««««««««««««««««««««««««¬­¬¬­­¬¬¬¬­¬­¬¬­¬¬­¬¬¬­­­­­­¬­­¬­¬¬¬¬¬¬­¬­¬¬¬­¬­­¬¬¬­¿11111¿¿¿1¿¿11¿®®®®®®®®®®®­­­®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­¬¬­¬¬¬¬¬­¬¬­¬¬­­­­­­¬¬­¬­¬¬­¬­¬¬­¬­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­­¬¬¬­¬¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬¬¬¬¬¬­¬¬«««««««««««««««««««««««««««¬¬¬¬­¬¬¬¬¬­¬¬¬¬­¬­­¬¬­­¬­¬¬¬¬¬¬­­¬¬¬¬­¬­¬¬¬¬­­¬¬¬­¬¬¬¬1111¿¿1¿¿¿¿¿¿11¿¿1¿®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬­¬­­­­®­­¬¬­­­­¬¬­­¬­­¬¬¬­¬­­¬¬¬­­¬­¬­¬­¬­¬¬­¬¬¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬¬­­¬¬­«««««««««««¬­¬­¬¬­¬¬¬¬¬­¬«««««««««««««««««««««««««««««««¬¬¬­¬¬­¬­¬¬­¬­­­¬¬­­¬¬¬¬­­­­­­­¬¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬­¬¬­¿¿1¿¿1¿1¿¿¿1¿¿¿¿®®®®®®­®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­¬­­­­­­­¬¬¬¬­¬¬­¬¬¬­¬­­­­­­­­¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬««««««««««««««¬««­¬¬­¬¬¬¬¬¬­¬«««««««««««¬««««««««««««««««««««¬¬­¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬¬­­­­­­­¬­¬¬¬­­­­¬­¬¬¬¬¬¬¬¬11¿1111¿¿¿¿¿¿11¿®®®®­®­®®®®®®­­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­­­­­­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­®­­¬­¬¬¬¬­¬­¬¬­¬®­­­­­­¬¬¬­­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬«««««««««««««««««¬¬«¬­¬¬­¬¬¬¬¬«««««««««««««««««««««««««««­¬­¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬­­¬­¬¬¬¬­¬¬­¬­­­­¬­­­¬¬­¬­­¬­­¿111¿1¿¿11111¿®®®®®®®­®®®®®­®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­­¬­­¬¬¬­¬¬¬¬­¬¬­¬­¬­­­­¬¬¬¬¬­¬¬¬­¬­¬­­­­­­¬¬¬¬­­­¬¬­¬¬­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­­­¬¬­­­¬¬­­¬¬¬¬­¬¬¬­««««««««­­¬¬¬¬¬­­¬¬¬¬¬¬«««««««««««««««««««««««««¬­¬¬­¬¬¬­¬¬¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬­­¬¬­¬¬­­¬¬¬¬¬¬­¬­­¬¬­1111111111¿¿1¿¿®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬­­­®®¬­­¬¬¬­¬¬¬¬¬­­¬­¬­­¬­­¬­¬¬­­¬¬­¬¬®®­­­­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬¬­¬¬­¬­¬­¬¬¬­­¬¬­¬¬­¬¬¬¬«««««««««««­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬­¬­¬¬­¬¬­¬­¬¬¬­¬­¬¬¬­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­¬­¬¬­¬¬­¬­¬­11111¿1111¿¿¿¿¿¿®®®®®®®®®®­®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®­¬­­¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­¬­¬­¬¬­¬¬­¬¬­¬¬­®­­­­­­­­­¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬­¬¬¬¬­¬¬­®­­®¬¬­­¬¬­­­­¬¬¬¬¬¬««««««««««¬«¬¬«¬¬¬¬­¬¬¬¬¬««««««««««««««««««««««««««««««¬¬¬¬¬­­¬­¬¬¬¬¬­¬­¬­¬¬­­¬¬­­¬¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­¬­­¬¬111111¿111¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®­®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬®¬­¬¬­¬¬­¬¬­¬¬¬¬¬­­­­¬¬­­­­­­­¬¬­¬¬­¬¬¬¬­­­­¬¬¬­¬®­­¬¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­®®®­¬­¬¬¬¬¬¬¬­­­¬¬««««««««««««««¬¬¬­­¬¬¬¬­¬¬¬¬««««««««««««««««««««««««««««««««««««««««¬«¬¬­¬¬¬¬¬­­­­­­­­¬¬­¬­¬­¬¬¬¬­¬¬¬­­¬¬¬¬­¬¬¬¬¬­¬¬­¿11111¿¿1¿¿111®®®®®®®®­®­®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬®­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬¬¬­¬¬­¬¬¬¬­­®®®­­­­­­¬¬­­­­­­¬¬¬¬¬­¬¬­¬­¬­¬¬¬­­­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬®®®®®®®­­­¬¬­¬¬­¬¬­¬¬««««««««««­«¬«¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««¬­¬­¬¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬­­¬­¬­¬­¬¬¬¬¬¬­¬¬¬­¬­¬¬¬11111¿¿¿¿¿¿111¿®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­®­­¬­­¬¬­¬­¬¬­¬¬¬­­¬­®­­¬¬­¬­¬¬¬¬­¬­­®®®­­¬­¬­¬­¬­¬­­¬­¬­¬­¬¬¬¬¬¬­¬¬­¬¬­¬­­­¬­¬­¬¬­¬¬­¬­¬­¬¬¬¬¬­¬­®®®®®®®®¬¬­¬¬­¬­¬­¬¬¬««««««««¬¬«««¬¬­¬¬¬¬««««««««««««««««««««««««««««««««¬¬¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬­¬­­¬­¬­¬­¬¬­¬¬11111¿1¿¿1111¿¿®®®®®®®®®®®®®®®®®®­®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬­¬­­¬­¬­¬­­­­®­¬®­¬¬­¬¬­¬­¬¬¬­®­®®­­¬­¬¬¬­¬­­¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­­®®®®¬¬¬­¬¬¬¬¬¬««««««««««­«¬¬¬¬¬¬¬¬­¬««««««««««««««««««««««««««¬¬¬­­­¬¬­¬­¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­­¬­­¬¬¬­111¿1¿¿1¿11¿¿¿¿¿®®®®®®®®®®®­®®®®­­®­®­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬¬­¬¬­­­­¬­¬®­¬¬¬¬¬¬¬­®®­­­¬­­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬­¬­¬­¬¬¬¬­­¬­¬¬¬¬¬­­¬¬¬¬¬­¬¬­¬­­­­­¬¬¬¬¬¬«««««««««««««««««««¬¬¬¬¬¬¬¬­«««««««««««««««««««««««««««««««««««««¬­®®¬­­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬111¿1¿¿¿¿¿¿®®®®®®®®®®­®®®®­®®®­®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®¬®­­­­­­­¬¬¬­¬¬¬­­¬¬­­­­¬­­­¬­¬¬­¬¬­®®­­¬­­¬­¬¬¬¬­­¬¬¬¬­¬¬­¬®®¬­¬¬­­¬­¬¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬««««««««««¬««¬¬¬¬¬¬­¬¬¬¬­««««««««««««««««««««««««««««««««««««¬®¬¬¬¬­¬¬­¬­¬¬¬¬¬­­­¬­¬¬­¬­¬¬­¬¬¬­­­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­®®­­­­¬­­­­®­­­­­­­¬¬¬­¬¬¬¬¬¬¬­¬­­­®®®®­­­­¬¬­¬¬¬¬¬­¬¬¬¬­¬­¬­­¬­­¬­¬­­­­¬­¬­¬¬¬­¬««­­¬¬­­¬¬¬¬¬¬­­¬¬««««««««««««««««««««««««««««««¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬­­­¬­¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­­¿¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®®­¬­­­¬¬­­®®®­®¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­­­­®­®®­¬¬­­¬¬­¬¬­¬¬¬­¬­¬­¬¬­¬¬­¬¬­¬­¬­­­®®¬­¬­¬¬­¬¬­¬¬¬¬¬«­¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬¬«««««««««««««««««««««¬­®¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬­­­¬¬¬¬¬¬­¬­­¬¬­­¬¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­¬¬¬­­­¬­­­®®­¬¬®¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬­®­®®®®®­¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬­­­­¬­­­­­­­¬¬­­­­¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­¬¬­¬¬¬¬««««««««««««««««««¬«­­­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬­­¬­¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬­­­­¬¬¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­¬­®®­¬­­­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬¬­­­­®®­®¬¬¬¬­¬¬¬¬­­¬¬­¬­¬¬­¬¬­¬¬­­­­­­­­¬¬¬¬¬¬­¬­­­¬««¬¬¬­¬¬­¬¬¬­¬­­¬¬¬¬««¬«««««¬««««««««««¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬­¬¬­¬­­¬­¬¬­­­­¬¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­®®®®­¬­­­¬­¬­¬­¬¬¬¬¬¬­­­­­­®®­­®®¬¬­­¬­¬¬­¬­­­¬¬¬­¬¬¬¬¬¬¬­­­­¬­¬¬­­­­¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­¬«««««««««««««««««««¬­­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬­­­­¬­¬¬­¬¬¬­¿¿¿®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬®¬¬¬­¬¬­¬­¬­¬¬¬¬­­¬­­­®­®­­¬­¬¬­¬¬­¬¬­¬¬¬¬®­­¬­¬­¬¬­­­­­¬¬­­­­¬¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬¬¬­¬­¬­­¬«««««««««««««««««­««­¬­­¬­­­¬­¬¬­­¬¬¬¬¬¬¬­¬­­¬­¬¬­¬­¬­¬¬­¬¬­­¬¬¬¬¬­¿¿®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®®¬­¬¬¬­¬¬¬¬¬­­¬¬¬­¬¬­­®­­­­®®®®®®®®¬¬¬¬­¬¬­¬¬®­­¬¬¬­­­­­¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬«¬¬¬¬¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««««««¬­¬¬¬¬¬­¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­­­­­­¬¬¬¿®®®®®®®®¿®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­­­­­­®®­­­­­¬¬¬¬¬­¬­¬¬­®­­­­­®®®®®®®®®®®¬¬¬¬­¬­¬¬­¬­¬¬­­¬­¬¬­­­­¬­¬¬­­­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­­«««««««««««««««««««¬­¬¬­­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬¬¬¬¬­¬¬¬­­¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­¬­­­®®­®­­­­­­­¬¬¬­­­­­­­®®®®®®®­®®®­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬¬¬¬­­­¬­¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­®®®¬­¬­­¬¬¬«««««««««««««««««««««¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬­­­¬­¬­¬¬®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®®®­­­­­­¬­­­­®®­¬­¬¬¬¬¬¬­¬¬­¬­­®®®®®®®®®®®®®®­­­¬­¬¬­¬¬¬¬¬¬­­¬­¬¬¬¬¬­¬­­­­¬¬¬¬­¬­¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­­¬¬­¬­­­¬««««««««««««««««««««««¬¬¬­¬­­­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬¬­­­­¬¬­®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬­­­®­­­¬­¬¬¬­­­­­­­®®®®®®­®­®¬­¬­¬¬¬­­­¬¬¬¬¬¬­­­­­­­­­­¬¬¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­¬­¬¬®­¬­¬­­­««««««¬«««««««««««««¬®­«¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­­­­­­­­­­¬®¬¬­¬¬¬­­­­­®®®®®®®®®®®®¬­¬¬­¬¬­®­¬­¬­­¬­¬¬¬­¬­­­®®®®­­¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬­­«¬­­¬­¬­¬­­¬¬¬­­¬«««««««««««««¬¬«­¬­­­¬¬­¬¬¬­­¬¬­¬­¬¬­¬¬¬­­­­¬­­¬­­¬¬¬­­­¬¬­¬¬®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­­­®®®®­­¬¬¬®®­­¬¬¬¬¬¬­­­­®®®®®®®®®®®®®¬¬¬­¬­®¬­¬¬®®¬­­¬­­­¬­­­®¬­­¬­­­­¬¬­¬¬¬¬¬¬¬¬¬«¬¬¬­­­¬¬­¬¬¬¬¬­­¬«««««««««««««««««¬¬¬¬«¬¬­­­­¬¬¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬­¬­¬¬­¬¬¬­­¬¬¬­­­­­¬¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­®­¬¬¬­­­¬¬¬¬¬¬¬­­­­­®®®®®®®®®®®­®¬­¬¬­­¬¬­¬¬­­¬¬¬¬­¬®­­­®®¬­¬¬­­¬¬­­­®¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬­¬¬­­¬­­¬¬­­¬¬««««««««««««¬¬««¬­¬­­­­­­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­¬¬¬­­®­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­®®­­­­­¬¬¬¬­¬¬¬¬¬¬­­®®®®®®­®®®­¬¬­¬¬®­¬¬­®­¬­¬­­¬­¬­®­­­­®®­¬­¬¬¬­¬®­­¬¬­¬¬¬¬¬¬¬«¬¬¬­¬­­­¬¬¬­¬¬¬­¬­¬¬¬««««¬««««««««««¬¬¬¬¬­­­¬¬­¬¬­¬­¬¬¬¬­¬­¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬­­­­¬¬®­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬­­­­¬­­­­®­­­­­­­­¬¬­­­¬¬¬¬­¬¬­­®®®®­¬¬¬­­¬¬¬­­¬¬¬­¬¬¬¬¬­¬¬­­­¬®®­¬¬­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬«­­­­¬¬¬­­­­¬¬­¬¬¬¬¬««««««««¬««««««««««««¬¬«¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬­¬¬­¬­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­­­­­­­­­¬¬­¬­¬¬­­­­­¬­¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬­­­®¬­¬­¬¬¬¬­­¬­­¬­¬¬¬­¬¬¬¬¬¬­­¬­­¬­¬­­¬®®®®­­­­®®®¬­¬¬­­­¬­¬­¬­¬¬¬¬­¬¬¬¬¬¬¬«¬­¬¬¬¬¬­¬­¬­¬¬¬¬­¬­¬¬«««««««¬­¬¬¬«««¬¬¬¬­¬­¬¬¬¬­¬¬¬­¬¬¬¬¬­¬¬¬¬­­­¬¬¬¬¬¬­¬­­¬­®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­¬­¬­­¬­­¬¬¬¬­¬¬­­¬¬­¬¬¬­¬¬­¬­­­­¬¬­¬­¬¬­¬¬¬­¬­¬¬¬­¬­­¬¬¬¬¬­­­¬­¬¬­®¬¬­­¬¬¬¬­­­­¬¬­¬¬®®®®®®®®®­¬­­­¬­­­¬¬­¬¬¬¬­¬¬¬¬­¬­­¬¬¬­­¬­¬¬«««««««««««««««««¬«¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­¬­­­­­¬­¬¬­¬­¬­­¬¬­­­¬­­­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­­­­­­¬¬¬­¬­¬¬­¬¬­­­¬¬¬¬­¬­¬®®®®®®®®­®®®¬¬¬¬­¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬®­­­¬­¬¬­¬«««««««««««««««¬¬¬¬¬­­­¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬­¬®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­¬­¬­¬¬­¬­¬­¬­­­­­­­­­¬¬­­¬¬­¬­¬¬¬¬¬¬­­¬­­¬­¬¬­¬¬­­­¬­®­¬¬¬¬­­­¬­¬­¬­¬¬¬¬­¬¬­¬¬®®®®®­®®®­­­¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬­¬®®®¬¬­­¬¬«««««««¬«««««««««­¬¬­­­¬¬¬¬­­¬¬¬­¬¬¬­¬¬¬¬­¬¬­®¬¬®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­®­­­­­­­¬­­­­¬­­­¬­¬­­­­¬­­­¬­¬¬¬­­­¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬­¬¬¬­­¬¬¬¬¬­¬­­­®®®®®®®®®¬­­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬­­­­¬¬­¬¬««««««««¬«««««««««««««««¬¬­­¬¬­­¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬­­­­®¬­­­®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬­­­­­­­­¬¬­¬­¬¬­¬­¬­­­­¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­­¬¬­¬­¬­¬­¬¬­¬¬¬¬­¬¬­­­­®®®®®®­­¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬®­¬®­­­¬¬¬¬««««««««««¬«««««««««¬««««¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­­¬¬¬­­®®¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­¬¬¬¬¬¬­¬¬¬­­­­­­¬­¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬­­­®­­­®®®®¬­¬®¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬­­­¬¬®­¬¬¬««««««««¬««««««««««««««¬­¬­­¬­¬¬­­¬¬¬¬¬¬¬­¬¬¬­­­¬­­®­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬­­­¬¬­­¬¬­­­­¬­¬¬­­­¬¬­¬¬­¬­¬­¬¬­¬­­¬­¬¬­­¬­¬¬­¬­¬¬­¬¬­­¬¬¬¬­­¬¬­¬­¬­¬¬­­®­®®®®®®®­­­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­­¬­­¬¬¬««««««««««««««««««««««««««««¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­­¬­­­­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­­­­­¬¬­¬¬¬¬¬­­¬¬­­­­¬¬¬­­¬¬­¬¬­¬¬¬­­¬­¬¬¬¬­­­¬¬­¬¬­¬¬¬¬­­¬¬¬­¬­¬¬­­¬¬­¬¬¬¬­¬¬¬¬­­®®®®®®®¬­¬¬­­­¬¬¬­¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬««««««««««««««¬¬¬¬­®­¬¬­¬­¬¬¬¬­¬¬­­¬­¬¬­­­­­­­¬­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­­­­­­¬­¬¬¬­¬­¬­­¬¬­­­­¬­¬­®­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬­¬­¬¬­¬¬­¬¬­¬¬­¬­­­®®®®®®®¬¬¬­¬­­¬­¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬­­¬¬­¬¬­¬¬¬¬«««««««««««««««««««¬¬¬¬¬®­¬­¬­¬¬¬¬­­¬¬¬¬¬­­¬­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­®®®­­­®­­­­¬¬¬¬¬­¬­­­­¬­­­¬¬­­¬­¬­­­­¬­¬¬¬¬¬­­¬¬­¬­¬¬­¬¬¬¬­­¬¬¬­¬¬¬­¬­­¬­¬­¬¬­¬¬­¬¬¬¬­®®®®®®¬¬­¬­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬­­­¬¬­¬¬­­¬­«««««¬««««««««««««««««««««««««««¬¬¬­­¬­¬¬¬¬¬¬¬¬¬¬­­¬¬¬­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­®­¬¬­¬­¬­­­¬­­­¬­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­­¬¬­¬¬¬¬­¬­¬¬­­­¬­®®®®­­¬­®®®®¬­¬¬­­¬¬¬­¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­¬­­­­­¬¬¬¬«««««««««««««««««««««««««¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­®­­­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬­­­­¬¬­­­­¬¬¬¬¬¬¬¬­­­­­¬­­­­­­¬¬­¬­¬¬¬¬¬­­¬¬®­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬­¬­­­®®®®­­¬­­®®®¬­¬¬¬­­­¬­¬¬­¬¬­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­­¬«««««¬¬¬««««««««««««««««««¬¬­¬­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬­­¬­¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­®­­­¬­­­¬¬¬¬­­¬¬­¬¬­­­­¬¬­­­¬­¬¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬¬­¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬­¬¬­¬¬­¬­¬­®®¬¬­­®®­­­­¬¬¬­­¬­¬¬¬­¬­¬¬­­¬­¬­¬­¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬««««««¬«««¬««««««««««««««««¬¬¬­­¬¬¬¬¬¬¬­¬¬¬¬¬­­­­­­¬­­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®¿1¿¿¿¿¿¿¿1®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®¬¬­­®­­­­¬­­­­¬¬­¬¬­­¬¬­­­¬­­­­¬­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­­­­¬¬­¬¬¬­¬­¬­¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­¬­¬­®®®®®®­­­¬¬­­®®­­­¬­¬­¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­¬­­­¬¬¬­¬­¬¬¬««¬«««««««««««««««««««««¬¬­­­­­¬¬¬¬¬­­¬¬¬­­¬¬­¬¬­­­­­­¬¬­­­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­­­­¬­­¬¬¬¬¬¬­­­­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬¬­®¬­­¬¬­¬¬­¬¬­¬¬­¬®®®®®­¬¬¬¬­®¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬«««««««««««««««««««««¬¬¬­­¬¬¬­¬¬¬¬¬­¬¬¬¬¬­­¬¬­­­­­­­¬­¬­¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­¬¬¬¬¬¬­­­¬­¬®®®­¬­¬¬¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬­­¬­¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬®®®®­¬®¬¬­¬­¬¬®¬­­­­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬««««««««««««««««««¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬­­¬¬­­­­­­­­­­­­­¬¬­­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®­­­­­­­­­­¬¬­¬¬¬­¬¬­¬­­­¬­­¬­­­¬¬¬¬¬¬­¬­¬­­¬¬¬­­¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­­­¬¬¬­­®¬­­­¬¬¬­­­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««¬¬¬¬¬­­¬¬¬­¬¬­¬­¬­¬¬¬¬­¬­¬­¬­­­¬­­­­¬­¬¬­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬­¬­­¬­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­­­¬­­­­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­­­­­­¬­®­¬­¬¬­¬­¬­­­­¬®­­¬¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬®¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬«««««««««««««««¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬­­­­­­­­¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬­¬¬­­¬­­¬­¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬­­¬­­¬­­­¬­¬¬­¬¬­¬¬¬¬¬­­¬­­­¬­®­­¬­¬¬¬­­­­­¬®®­­¬­¬¬­¬¬­­­¬­¬­¬¬­¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬­­¬­­¬¬­¬¬­­¬¬­­¬¬¬¬¬¬¬««««««««««««««««««¬¬¬­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬­­­­­­­­¬­¬­­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­¬­­­¬­¬­­¬­¬¬­­¬¬­¬¬¬¬¬¬¬­¬¬­­¬¬¬¬¬¬­­¬­¬­­¬­­¬¬­¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬®­®®­­­¬¬­¬¬­¬­®­¬­¬¬¬¬­¬¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬«««««««««««««««««««¬¬¬¬¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­­­­­­­­­­®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­­­­¬­­­¬¬­¬¬¬­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬¬¬­¬­¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬­®­¬­­­¬­¬¬­¬¬¬¬­­¬¬¬­­­¬¬¬­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬¬¬­­¬¬¬¬¬­¬¬­¬¬­¬««««««««««««««««««««¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­­­¬¬­­­­­®­­®®­­­­¬­­¬¬¬¬­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®­­­­­­¬­­¬­¬¬­­¬¬­­¬­¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬¬­¬­¬¬­­¬­¬¬­¬­¬­¬¬­¬­¬¬­­­­¬­­¬­¬­­­¬­¬­¬¬­­­®­­¬¬­­¬­¬­­­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­­¬¬««««««««««««««««««¬­¬¬¬­¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­¬­­­¬­­­­®­­­¬¬¬­¬­­®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­¬­¬­¬­¬­¬­¬¬­­­¬­¬¬¬¬¬­¬­­¬¬¬­¬­¬­¬­­­¬¬­¬­¬¬­­¬­¬­¬¬­¬­¬­­­­­¬¬¬¬¬­­­¬­¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­¬¬¬«««««««««««««««««««««««««¬¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬­­­­­¬­¬¬­­­­­¬®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­­­­­­­¬¬­¬­¬­¬¬¬¬¬­¬¬­­­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬­­­¬¬¬­¬­­­­¬¬­¬¬­¬¬­¬­¬¬­­­­­­¬­¬¬¬­®­­¬¬­¬­¬¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬¬­¬¬¬¬¬¬¬««««««««««««««««¬¬­­¬­¬¬¬­¬­­­¬¬¬¬¬­¬­­­­­­­­­­­­­­­­­­®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬­¬¬­­­¬¬¬¬­¬­¬¬¬¬¬¬­®­¬­¬¬¬¬¬¬¬¬¬­­¬­­­¬¬­­¬­¬­¬­­­­­­¬­¬¬¬­­¬¬¬­­¬­­­­¬­¬¬­­­­­­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬­­­­­¬¬­¬­¬¬¬¬¬«««««««««««««««««««««¬¬­¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­­¬¬­¬­­­­­­­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­­¬¬­¬¬­¬¬­­¬­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬­­¬¬­¬­¬­­¬­¬¬¬¬¬­­¬¬­­­¬®­­­­¬¬­¬­­­¬¬¬¬¬¬­¬¬­¬¬¬­¬¬­­­¬¬¬¬­¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬«¬¬«««««««««««««««««¬¬­­­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­­­¬¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬¬­­¬¬­¬­¬¬¬­¬®­¬¬­­­­­­­­¬¬­®­¬­­­­¬­¬¬­­¬­¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬«««««««««««««««««¬¬¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬­®­­®®­­­­­­®®®®®®®®®®®®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­¬­¬­­¬¬¬¬­­­¬¬­¬­­­­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬­¬­¬¬­®­¬­­­­¬­­­¬¬­­­­­­­¬­­¬¬­­¬­¬­¬¬­­­¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬««««««««««««««««««««¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬­­­®¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬­¬¬­¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­­¬¬­¬¬¬­¬­­­¬­­­­­¬¬­¬¬¬­¬¬¬¬­¬­­¬¬¬­­¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬¬¬­­­¬¬¬¬¬¬¬««««««««««««««««««««¬¬­­¬­¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬­­¬­­¬­­­­®®®­­­¬®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­¬­¬­¬­¬¬­¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬­¬­­­¬¬¬­¬¬¬­­¬­¬¬¬¬­¬¬­­¬­¬¬­®­­­­­­­­­¬¬­¬¬­¬¬­¬­­­­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬­­­­­­®®­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­¬¬¬­¬¬­¬­¬­¬­¬­¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬­¬­­­­­­¬¬­¬¬¬­­­­­¬¬­­­­¬­¬¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬­¬­¬­­¬¬¬¬¬¬¬«««««««««««««««««««««¬¬¬­¬¬¬¬¬¬¬­­­¬¬­¬­­­¬¬¬­­­­­­­­­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­¬­¬¬­¬¬­¬¬¬¬­¬­­­¬­­¬¬¬¬­­¬¬­¬¬¬¬¬­­¬­¬¬­­¬­¬¬­¬¬¬­­¬­­®­­­­­­­­­­®­­­­®­¬­­¬¬­­­­¬­­­­¬¬¬¬¬­­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬¬¬¬­¬¬­¬­­­­¬¬¬¬­¬­­¬®­®®­®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬­­­­¬­¬­­¬­¬¬­¬¬¬¬¬­¬­­­¬¬¬­¬¬­¬­­­­­­­­­­­­­­­¬­­¬­­­¬­­¬­­­¬¬­­­­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬­¬­­¬­¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬­¬««««««««««««««««¬¬­­­¬¬­­­­­­­­­¬­­­­­¬­¬­­®®®®®®®®®®®®®­­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®­­¬­­­­¬¬¬¬­¬¬­¬¬­­¬­­­¬­¬¬¬¬­­¬­¬¬¬¬­¬¬¬¬¬­­­­­¬­­¬­­­®­®­­­®­­­­­­­­­­­­­®®¬­¬­¬¬­­®­­­¬­¬¬¬­­­¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬¬¬­­¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««¬¬¬­¬­¬¬­¬­­­­¬¬­¬­¬­¬¬­­­®®­®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­¬­¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­­­®®­­®®­­­­­­­­­¬­­®­¬­¬­¬¬­­¬¬­¬­­¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­­¬¬¬­¬¬­¬­­­¬¬¬­¬¬­¬¬­­¬¬¬¬­­­¬¬¬¬««««««««««««««¬¬­­¬­¬¬­­­­­­­­¬¬­­­­¬­®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬­¬­¬¬­¬¬¬­¬­¬¬¬­¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬­¬¬¬­­¬­¬­¬­­­®­­®®®®­­­­­­­­­­­­¬­­®®¬­¬¬­¬®®®®®­­­­¬¬­¬¬¬¬¬¬­­¬¬­¬­­¬¬­­¬­¬¬¬­¬¬¬¬¬¬­­¬¬¬­¬¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬«««««««««««««««¬­­­¬¬­¬­­­­¬­­­¬­­­­­­­­­­­­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®­­¬­¬¬­¬¬­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­­¬¬­­­­®®®­­­­­­¬­­®¬­¬¬­®­¬­¬­¬¬­­¬­¬¬¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬­¬­­¬­­­¬¬¬¬­­­­¬¬¬­­¬¬¬­¬««««««««««««««««¬­¬­¬¬­¬¬¬¬­­­­­­­­­­­®¬­­­­®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬­­­¬­­­¬­­­­¬¬¬¬¬¬¬¬¬¬­¬­¬­­­¬¬¬­®­­­­­­­­­­­­­­­®®®®­¬¬¬¬­¬¬­¬¬¬¬­¬¬­­¬¬­­¬¬¬¬¬­¬­­¬¬¬­­¬­­¬¬¬¬­¬¬­¬¬­¬­¬­­¬¬¬¬¬¬¬­¬¬¬«««««««««««¬­­¬¬¬¬­¬­­­¬¬¬¬¬­­­­­­­®®®­­­®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®¬­­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬­­¬¬¬¬¬¬­¬­­­¬­¬¬¬­­­®­­­­­­®­­­­­­¬¬­¬¬­¬­­­®­¬­­¬­¬¬¬­¬¬­­­­¬¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬­­­¬¬¬¬­¬¬¬¬¬««««««««««««¬­¬¬¬®¬¬¬¬­­­­­¬¬¬¬­­¬­­®®®­­®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®¬­¬¬­¬¬­¬¬¬¬¬­¬¬­¬¬­¬­¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬­­­¬­­­­­­­­®­­­¬­­­­­¬¬¬­®®­­­¬¬­¬¬¬¬¬­¬­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬«««««««««««««¬¬­¬­­­¬­¬¬­­­­­¬­­­­­®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­¬¬­¬¬­¬¬­¬¬­¬¬­­¬¬­¬¬¬­­¬¬­­¬­¬¬¬­¬­­¬­­¬¬¬¬¬¬­­­­­­®®­¬­­®­­¬­­¬­­­­­®®®­¬¬­¬¬¬¬­¬­¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬­¬¬­¬¬¬¬¬¬¬¬­««««««««««««««¬¬¬¬­¬¬®¬¬¬¬­­¬­­®­­­­­­®®®®®®®®®®®®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­­¬¬­­¬¬­¬¬¬­­­¬­¬­­¬¬­¬¬¬­­¬¬­­­­¬­­­­­­­­®­¬­®¬¬¬­¬­¬­­­®®®­­­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬­¬­¬­¬­¬¬¬¬­¬­­¬¬­­¬¬¬¬¬¬«««««««««««««¬¬­¬¬­¬­­¬¬­¬¬­¬¬­¬¬­®­®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬­­¬¬­¬­¬¬­¬¬¬­¬¬­­¬­¬­­­¬¬¬¬­­¬¬­­¬¬­­¬­¬­®­¬­®­¬¬¬¬­¬¬¬¬­­­­®®­­­­­¬¬­­¬­­¬­¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬¬¬¬¬¬««««««««««¬¬­¬­­¬®¬¬­¬­­­­­¬¬¬­­­­­­®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­¬¬­­¬­¬­­­­­¬­­¬¬­¬­¬¬¬­¬­¬¬­¬¬¬¬¬­¬­¬¬¬¬­­®®®®®®®®®­®®¬­¬­­­¬­­¬­¬¬­­­­®®®®­­¬¬­¬¬­­¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬­­­¬¬¬­­¬¬¬¬¬­¬¬¬«¬«««««««««««­­¬­¬­¬¬­­­¬¬­¬¬¬­­¬¬­®®®­®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­­¬­¬¬¬¬¬¬­¬­¬­¬­¬­­¬¬­­¬­­¬¬¬¬­¬¬¬¬¬­­­­­­¬­­­­®®®®®®®®®®®®­­­¬®¬®®®®®­¬­­­¬­­­­­®­­­­¬¬­­¬¬­¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬¬­¬¬¬¬­¬­¬­¬¬­¬­¬¬­¬­­¬¬¬­­­¬¬¬¬¬¬¬¬¬«¬«««««««««««­¬¬¬­­­¬­¬­­¬¬¬­¬­­­¬­­­­¬­®®®®®®®®®®®®®®®®­®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬­¬¬¬­¬­­¬¬­­¬­¬¬­¬­¬­¬­¬¬¬¬¬­­¬­¬¬­¬¬­¬­¬¬­­­®®®®®®®­®­¬®®­¬­­­­­­®®­­¬­¬¬­­­­­¬¬­­¬­­¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬««««««««««¬­¬¬­¬¬­¬­­­­¬­­¬¬­­­­®­®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­¬­¬¬­¬­¬­¬¬­¬­¬¬¬¬¬­­­¬¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬­­­¬¬­­­®®®®®®®®®®®®®­­®­­­¬­­¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­­­¬­¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬«««««­­­­¬¬¬­­®®®®­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬¬­¬¬¬­¬¬¬­¬­­¬¬­­­¬¬¬­¬¬­­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬­­¬­¬­¬­®®®®®®®®®®®®®®®®­®®­­­­­­­­¬­¬­¬¬­­­­­¬¬­­­­¬­­­¬¬¬¬­¬¬¬®¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­­­­¬­­­¬¬­¬¬¬­¬¬¬¬­­¬­¬­­­­¬¬¬««¬««¬­¬¬­¬¬­¬®¬­­®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬­¬¬­­¬­­¬­­­¬­­­­¬­­­¬­­¬­¬­¬¬¬­¬¬¬¬¬­¬¬­¬­­­­¬®®®®®®®®®®®®®®­­­®­­®®¬¬­­­­­­­¬­­¬¬¬¬¬¬¬¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬¬­­¬­­­­­­­­¬­¬­¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬¬«¬««««««¬¬¬­¬­¬¬®¬¬®®®®®®®®®­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬¬­­¬¬¬­¬­­¬¬¬¬­­¬¬¬­­­­­¬­¬¬­¬¬­­¬®®¬­¬¬­¬­¬¬­¬¬¬¬­¬¬®®®®®®®®®®®®®®­®®®®®®®­¬­­­­®­­¬­­­­­­¬¬¬­­¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬­­­­­­­¬­­­¬­¬­¬­¬­¬¬¬¬¬­¬­¬¬¬¬««««««¬¬­¬¬¬¬®­®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬­¬¬¬¬­¬¬­¬­­¬¬­­¬­­­­­¬¬­¬­¬­­¬¬¬¬®®­¬¬­¬­¬¬¬­¬­¬¬­®®®®®®®®®®®®®®®®®®­­­­­­­­­®­­¬­­¬­­­­­¬¬­­­¬¬­¬­¬­¬­¬­­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­­­­­­­­­­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬¬««¬­¬¬¬¬­­®­¬¬­®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬¬¬¬­­­¬­­­¬­­­­¬­¬­­®®®¬¬¬¬­¬¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬­­¬¬¬¬¬­­­¬¬¬­­¬¬¬­­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬­¬­¬¬­­­­­­­­¬¬­¬­¬¬­¬¬­¬¬¬¬­¬­¬­¬¬¬«««¬¬¬¬¬¬­­­­­­­®®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­­¬¬¬¬­¬­¬­¬¬­¬­¬¬¬­¬­¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬¬­­¬¬­®®®®®®®®®®®®®®®®­®­¬¬­¬¬­­­¬¬¬¬­­­­­¬¬­­­¬­­¬¬­­¬¬¬¬­¬­¬¬¬¬¬¬­­­­­¬¬­¬¬­­­¬­­­­­¬­¬¬­¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬««««¬¬¬­¬­­­­­®¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­­­­¬¬­¬¬¬¬­¬­­¬¬¬­­­¬¬­¬­­¬¬¬­¬­¬­¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬¬­¬­®®®®®®®®®®®®®®®¬­­­­­­­­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­­¬¬­¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬­­­­­­­­¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬«««««¬¬­¬­¬­®¬­­¬­­­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­­¬¬¬¬­¬­¬­­­­¬¬­¬¬¬­­¬­¬­¬¬­­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­­¬¬­¬¬¬­¬®®®®®®®®®­®®¬¬­¬­­¬¬¬¬¬¬­¬­¬¬­­­­¬¬­¬¬­¬¬­¬­¬­­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬­¬¬­­¬­­­­­­¬­¬¬¬­¬¬­¬­­­¬¬¬¬¬­¬­¬¬«««¬¬¬¬¬¬¬­¬®¬­­­­­­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­­¬­¬¬­¬¬­¬¬­¬¬¬¬­­­­¬­¬¬­­­¬¬­­¬¬­¬¬¬¬­¬¬­¬­¬¬¬­­¬¬®®®®®®®®®®®­®­­¬¬­­­­¬¬­­­¬¬¬¬¬­­¬¬¬­¬­­¬¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­­¬­­¬­­­­¬­¬¬¬­¬­¬­¬­¬¬¬¬¬­¬¬¬¬«««««««¬¬¬¬­¬­­­­­­­­¬­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­­¬¬­¬¬¬¬­¬¬­­¬­¬¬­­¬­­­­­­¬¬­¬­¬­¬¬­­¬¬¬¬¬¬­¬­¬­­¬¬¬¬¬®®®®®®®®®­­­®®­­­­­­­­­¬­¬¬­¬­­¬¬­­¬¬¬¬­¬¬­¬­¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­¬¬­­­¬¬­­¬­¬­­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬«««««««««¬¬¬­¬¬¬¬­¬¬­¬¬¬­­®®®®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­¬­¬­¬­¬­¬¬¬¬­­­­¬¬¬¬­­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬®®®®®®¬¬®®®®­­®­­­­­­­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­­¬¬¬­­­­¬­­­­¬¬¬­¬¬¬¬¬¬¬¬¬««««««««««¬­¬¬­­¬¬­¬­¬¬®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­¬¬­¬¬­­­¬¬¬­­­­¬¬­­­­­­­¬­¬¬¬­¬­­¬¬­¬­¬¬­¬¬¬­¬¬®®®®®®®¬¬®®­­­¬­­­­­­­­¬¬­­­­­¬­­­­­¬¬¬¬­¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬¬­­­­­­­­¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬««««««««¬¬¬¬¬­¬­¬¬­¬¬­­¬­­­®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­¬¬¬¬­¬¬¬­­¬¬¬­­­¬­­¬­¬¬­­¬­¬¬¬¬­¬¬­¬¬­¬¬¬­­­¬­¬¬®®®®®®¬¬®­­¬­­­­¬¬­­¬¬­¬­¬¬­­­¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬­¬¬­¬­­­­­­­­­¬­­­¬¬­¬­¬­¬­¬««««¬¬¬­¬¬¬¬­¬¬¬¬¬­­¬¬­­­­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­®¬¬­¬¬­¬¬¬¬­¬­­¬¬­­¬­¬­­¬¬­­¬­¬¬®®®®®®®®®®­­®®®­­­­­­¬­­­¬­¬¬¬­­­¬­¬¬­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­­­­­­­­­­­¬¬­­¬­¬¬­¬¬¬¬¬¬«««««««¬¬¬­¬­®¬¬­¬¬¬­­¬­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­®­­­¬¬­¬¬­¬­¬¬­¬¬¬­¬­¬¬­¬­¬¬¬¬¬­­¬­­­­­­¬¬¬­¬¬­¬¬­­¬®®®®®®®®®­­­®®®®­­­­­­­¬®­­­­­­­­­­­­­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­­­­­­­­­­¬¬­¬¬­¬­­¬¬¬¬¬¬«««¬¬¬­­¬¬¬¬¬­¬­­¬¬¬¬®­¬¬­­­­­­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬¬­­¬­¬­¬¬¬¬­¬¬¬­¬¬­­­¬¬­¬­­¬­¬¬­¬¬¬­¬­­¬­¬¬­¬¬­­­­¬¬¬­®®®®®®®®®­­®­®­®®­¬­­¬­®®­¬­­­­¬¬­­¬­¬­­¬­­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬­¬¬¬­¬¬¬¬­­­­­­­­¬¬­¬­¬­¬­¬¬¬¬¬¬«««««¬¬¬¬­¬¬­¬­®­¬­­¬­­¬­­­¬­­­­¬­­­®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬­¬¬¬¬­¬­­¬­¬­¬¬­­­¬¬¬­¬¬¬­­­­¬¬­¬¬¬¬¬­¬®®®®­­­­­­­®®­­­­­­­­­­­­­¬¬­¬¬­¬¬­­¬­­¬¬­­¬¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­­¬­¬­¬­­­¬¬­¬¬¬¬¬­¬¬¬¬¬­««¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬­­­¬¬¬­­­­­­­­®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­¬¬¬¬­­¬­¬­¬­­¬¬¬­¬­¬­¬­­­¬­¬¬¬¬­¬­¬¬­¬¬­¬¬¬­¬®­­­­­¬­­­­­®­­­­­­®­­­­­­­¬¬­¬¬¬¬­¬¬­­¬¬­¬¬¬­­­¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­­­­¬¬­­¬¬¬­­­­­¬¬¬¬¬­¬¬«««¬¬¬¬¬¬­¬¬­¬®¬¬¬¬¬¬¬¬¬¬­®­­®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬®­­¬¬¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬­­­­¬¬­¬­¬­¬¬­¬­¬¬¬¬¬­¬­­­¬¬­¬®®®®®­­¬­­­®­­®­­­¬­­­­®­­­­­­­¬­¬­¬­­¬¬­¬­­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­­¬¬­¬¬­­¬¬­¬¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­­¬­­­­­¬¬­­­®­®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬®¬¬­¬¬¬­¬¬¬­¬¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­®®®®®®­¬­­­®®­¬­¬­¬­­­­­¬­¬­­­­¬¬­¬¬­¬¬¬¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬¬­­­¬¬¬­¬¬¬­¬­¬¬­­­­­¬¬¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬­¬¬¬­¬­­¬®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­­­­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­¬¬¬¬­¬¬¬¬­¬®®®®®®­­­­­­­¬¬¬­­®®®®­­­­­­­¬­®­­­­­¬­¬¬¬¬­­­¬­­¬¬¬­¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬««¬¬¬¬­¬¬­¬­¬­­­­­¬®®®®®®®¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­­¬­­­­¬¬­­­­¬­¬­¬¬­¬­¬­¬¬¬¬­­­¬¬¬¬­¬¬­­¬¬¬¬¬¬­¬¬¬­®®®­­­­­¬­­­¬­®®®®®®­­­­­­­­­­­­­­­­¬¬­¬¬¬¬­­¬­¬¬¬­¬¬¬­­­¬¬­­¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬­¬¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬¬­¬­¬­¬­®­®­­®®®®®®®®®¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­­¬¬¬­¬¬¬¬¬­­¬¬­¬­¬¬­¬­¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬®®­®¬¬¬¬­®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬­¬­­­­¬­¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬¬­¬¬­­­¬¬­¬¬¬¬­­¬­­¬¬­¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬­­¬¬¬­­®®®®®®¿1111®­¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬¬­¬­¬­­¬¬­¬¬­¬¬­¬¬¬¬¬¬­­¬¬­­¬­¬­­­¬­¬¬­¬¬¬¬­¬¬¬®®®®®­­­­­­¬¬¬¬®®­®®®®®­­­­­­­­­­­­­­­¬¬­¬¬­¬¬­­¬¬­¬­¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬¬­¬­¬¬­­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬­­¬­®®®®®®®®®111111111111®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­­­¬¬¬­­­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­­¬¬­¬¬­­­¬­¬­¬¬­¬­¬­­®®®®®­­¬­¬¬­­­­®®®­¬­­®­­­­­­­®­­­¬¬¬¬¬­¬¬¬­­­­¬­¬¬­¬¬­¬­¬­­­¬­¬­¬­­­­¬¬¬­¬­¬¬­¬­¬¬¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬­®®®®®®®®®®®®®®®®®111111111¿1111111111111®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬­¬¬­¬­­¬¬­¬­¬¬­¬­­¬¬­¬­­¬­¬­­¬¬¬­­¬¬­­­¬­¬¬¬®®®®­­­­¬¬¬­­®®­­­­­­­­¬­­­­¬­¬¬­¬¬­¬­¬­­¬¬¬­­¬¬­¬¬¬¬­­¬­­¬¬­¬¬­¬¬­­¬­­¬­­¬­¬¬­¬­­¬¬­­¬­­¬¬¬¬¬¬¬­­¬«««¬¬¬¬¬¬¬­¬¬­­­¬¬¬­®1111111111111¿11¿111111111111111111111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬¬¬­­¬­¬¬­¬¬­­­­¬­¬¬¬­­¬­¬­¬®­¬­¬­¬¬­¬¬¬­®®®­­­­­­­­¬¬¬­­¬­®®®®­­­­­­­­¬¬­­­¬¬¬¬¬¬­¬­¬­­­­­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬¬¬¬­¬­¬­­­­­­¬¬¬¬¬­¬¬­¬¬«¬¬¬¬¬¬¬¬­¬¬­­¬¬¬­11111111111111111111111111111111111111111111111111111®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­¬­¬­­¬¬¬­¬­¬¬­¬¬­­­­®­­¬­¬­¬­¬­¬¬®¬¬­­¬¬¬¬­­¬¬­¬¬­­­­­­­­­­¬­­­®®®®®®­­­­¬­­­­­­­­­¬­¬¬­¬­¬­¬¬­­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬¬¬­­­­¬¬¬¬­­­¬¬­¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬¬­­­­­­­111111111111111111111111¿111111111111111111111®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®¬­­­¬­¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬­®­­­¬­¬¬­¬¬¬¬­¬­¬¬¬¬­­­¬­¬¬­¬®®®®­­­­®­­¬¬¬­¬®®­­¬­­­­­­¬¬­­­¬­¬­­­¬¬­¬­¬­­¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬­­¬­­­­­¬­¬¬­­¬¬¬­­¬¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬«««¬¬¬­­¬¬­¬¬¬¬­¬¬­®11111111111111®®®¿¿11111111111111111111®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬­¬®¬¬­¬¬¬­¬¬­¬¬¬¬¬­­­­¬¬¬¬¬¬¬®®®®­­­­­®­­­­¬¬¬­­¬­¬­­­­­­­­­­¬­¬¬­¬­¬­­¬­­¬¬¬­­¬¬­¬¬¬­­­­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­­­­¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬­¬­¬¬­­­­11111111111111®®®®®®®111¿1111111111111111111®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬­¬¬­­¬¬¬­¬¬­­­¬­¬­¬¬­­­­¬¬­¬­­¬¬­­®¬¬¬¬¬¬­®®®­­­­­¬­¬­®®®­­­­¬¬­­­­­­­­¬¬¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬­¬­¬­¬­­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬«¬¬¬­¬­­­­¬­1111111111111®®®®®®11111111111111111111111111®®¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬¬¬¬¬­­­­­¬¬­¬¬­¬­¬­­­¬­­­­¬­¬­­®¬¬¬¬­­¬­®®®­­­­­¬­¬­¬­­­¬­­­­­¬­­­­­­¬­­¬­­¬¬­¬¬¬­­¬¬­­¬­¬­¬­¬¬­¬¬­¬­¬¬­­¬¬­­­­­¬­¬­­¬­¬¬­¬¬¬¬¬­¬­¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬­¬­­¬­¬11111¿11111111111®111111111111111111111111111111®®®¿1¿¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®­®®®­­®®®®®®®®®®®®®­­­­­¬­¬­¬¬­¬¬­¬­¬­¬­¬¬­¬­¬¬­¬¬¬­¬­­­­­¬­­®®­®®®­®®­­­­­­¬­¬¬­¬¬­¬­­­­­­­­­®­­­­­­­­­­¬¬¬¬¬­¬­¬¬¬­­­­¬¬¬­­¬¬­¬¬¬¬­¬­­¬­­¬­¬¬­¬¬¬­¬­­¬¬­¬­¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬­­­­1111111111111111111¿111111111111111111111111111111111®®®¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®­®®®®®®®®®­®®­®­®®®®®­­®®®®®®®®®­­¬¬¬­­¬¬­¬­­­­¬­¬¬­¬¬¬­¬¬¬¬­¬¬­­¬­¬­¬­­¬®®®®®®®®­­­­­­­­­­¬¬­¬¬®­®­­­­­®­­­­­­­­­­­­­­¬­­¬¬­­­¬¬­­¬­¬­­¬­¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬­­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­­­¬­¬®111111111¿111111¿11¿11111111111111111111111®11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­®®®®®®®­®®®®®®®®®®®®®­­®®®®®®®®®®®­­­­­­­­®­¬­­­¬¬¬­¬­­¬¬¬­¬¬­­¬¬­¬¬¬¬¬­­­¬¬¬¬¬­­­¬­®®®®­­­­­­­­­®®­®­­­­­­­­­­­­­¬¬¬­­¬¬­­¬­­¬­¬­¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬­¬­¬¬¬­¬¬­¬­­­¬­­¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬­­­®®11111®®®11111111111111111¿1111111111®¿11¿11¿11¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬­¬­¬­¬¬­­¬¬­¬­¬­¬­­­­­­­¬¬­¬¬­­®®®®®®­­­­­­­­¬­­¬­­­­­®­®®­­­­­­­®­­­­­­­­­­¬¬­¬­­­¬­­­¬­¬­¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬­¬¬­­¬¬¬­¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬®¬­¬­¬¬­®­­­­­­¿11111¿®­­®1111111®®®1111111®®1111111®¿11¿1¿11¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®®­®®®­®®®®®­®®®®®®®®­®®®®­®®­­®®®®®®®®®®®­®®®®®­­­­®­­¬­¬¬­­¬­¬¬­¬­¬¬­¬¬­­­¬¬­¬­­­­¬¬­¬¬­­­­­­­®®®®®­®­­­­®®­­¬­­¬­­­­­­­®­­­­­­­­­­¬¬¬­­¬­¬­­¬­¬¬¬­­­­¬¬¬¬­¬­­¬­¬­­¬­¬¬¬¬­¬¬­¬¬¬¬­­­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­­­­­­­11111®111¿1®®®®®1®®®®¿1111111®¿¿¿¿¿¿¿¿¿¿¿®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­®­­®®®®®®®®®­­®®®®­­­­­­®¬¬­¬¬­¬­¬­­¬­­­­¬­­¬¬­¬­¬­¬­­­­­­¬­®®®­®­­­­­­­­®­­­­­­­­­­­­­­­­®­­­­¬¬­­¬­¬­­¬¬­­­­­¬­¬­¬¬­¬­¬­¬¬­¬­¬­­¬¬­¬­¬¬­¬­­¬¬¬­¬¬¬­­­­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­®®1111®®1111®®®11®®®®®®®®®®¿1111111®¿1¿11¿¿¿¿¿¿¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®­®­­®®®®®®®®®®®­­­®­®®®­­­­­­­¬¬­­¬¬¬­¬­­¬­¬­¬¬¬­¬¬¬­¬¬¬¬­¬¬¬­­­­­­­®®®®®­­­¬­­­­­­­­­­­­­­­­­­¬­®­­­¬­­­­®­­­­­­­¬­¬¬¬¬­­­­¬¬¬­¬­¬­­¬¬­­¬¬­¬¬¬­­¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬­­­­¬¬­®11®®®111111111¿®®®®®1111111®1¿¿¿111¿¿¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®®®®­®®®®®®®­­®­­®®®®®®®®®®®®®­®­­®®®®®­­­¬¬­¬¬¬¬¬­­­¬¬­­¬¬­¬¬­¬­¬¬­¬¬¬¬­¬¬¬­­¬­­­­­®®®®®®­­¬¬­­­¬­­­­®­­¬­­­­­­­¬­­­­­­­­­­­­­­­­­¬­¬¬­­¬­­­­¬­­­¬¬­¬­­­¬­¬­­¬¬¬­¬­­¬¬­­­¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬¬­11­®®®111111111111¿®®®®1111111®1¿11¿¿¿¿¿1¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­­­­®®®®¬­­­­­­¬­¬¬­­­­¬­­¬¬¬­¬­¬¬¬­¬¬­¬­­­¬­­­®®®®®®®®­­­­­­®­­­­®­­­­¬­­¬­­­­®­­­®­­­­­¬¬¬­¬¬­­¬­¬­­¬­¬­­­¬¬­­¬­¬­¬­¬­­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬®111®®®111111111111¿®®®®®1111111®¿1¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®­­®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®®®®­®­­­­­­¬¬¬­­¬­­­¬­­­¬¬­¬¬­¬­­¬­­¬¬­­¬­®®®®®®®®­­­­­­­­®­­­¬¬­­­­­­­®­­­­­­­¬¬¬¬¬¬¬­¬­­¬­­­¬­¬¬­¬¬­­­­®­­¬­¬­¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬¬­¬¬¬­®¿11¿­®®®11111111111111®®®®®1111111®®¿¿¿¿¿1¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®®®®®®®®®®®®®®®®­­®®­®­®®­®®­®®®®®­®®®®­®®®®­®®®®®­­®®®®®®®®®®®®®®®­­­­®®®®­­­­¬¬¬­¬­¬¬¬­­­¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬­­­­®®®®®®®®­­­­­®®­­­­®®­®­­­¬­®­¬­­­­­­­­­®­­­­­­­¬¬¬¬­­­¬­­­­­¬­­¬­¬­¬¬­¬¬®¬¬­­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬­­­¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬¬­¬¬­®11­®111111111111111111®®®®®®®®1111111®®¿1¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®­­®­®®®®®®®®­®­®®­®®­®®®­®®®®­­®®®®®­®®­®®®®®®®®­­­®®®®®¬­¬­­¬­­¬­¬¬­­­­¬­¬¬­¬¬­¬¬­¬¬­­­­¬¬¬¬®­­¬­®­®®®®®­­­®­­­­­­­­¬¬­­­­­­­­­­­®­­­­­­­­­­­¬­­­­¬­­­­­­­­¬­­­­¬¬¬¬­¬­­¬¬­­­¬¬­¬¬­¬¬­­¬¬¬¬¬­­­®¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­­­­­­¬¬­¬­­®®®®11®­­®111111111111111111®®®®®®®1111111®®¿¿¿¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®®®®®®®®®­®­­®®­®®®®®®®­®®®®®®®®®®®®®®®®®­­­­®®®®®®®­¬­¬¬¬¬¬¬¬­¬¬­¬­­¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬­­­­­­­­®®®®®®®­­®­­­­­­®®­¬¬­­­¬­®®®­­­­­­­­¬¬¬¬­¬¬¬¬¬­¬­­¬¬­¬­¬­­­¬­¬­­¬¬­¬­­¬¬­¬¬­­¬¬­­¬¬­­¬¬­­¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬¬¬¬­®®®111®®®1111111111111111111®®®®®1111111®¿¿¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­®®®®®­®­®®®®­®­®®®®®®­­®­­®®­®®®®­®­­®®®®­®®®®®®®®®®®®®­­¬®­¬¬¬­¬¬¬¬­­¬­¬¬­­¬­­­¬­­¬­¬­®­¬­­­®®®®®®®®­­­­­­­®®®®­­¬­­­¬®­­­­­¬­­®­­­­­­­®­­­­¬­­­¬­¬­¬¬¬¬¬¬­­­¬¬­­¬¬­¬­­¬­­¬¬­¬¬­¬¬¬¬¬­¬­¬­¬¬­­¬¬¬­¬­­¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬­®®®®1111¿11111111111111111111111®®®®®1111111®¿¿¿¿¿¿¿¿¿®®®¿®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®­­®®®®­­®®­®®®®®®­­®­®­­®­­®®­­­®­­®®­®®®®®®­®®®®­­­­­­­¬­­­¬¬¬­¬­­¬­¬¬­­­­¬­¬¬­¬¬®¬­­­¬­­®®®®®®­­­­­­®­®®®­­¬¬­®­¬­­­­­­­­¬®­­­®®®®­­­­­­­­¬­­¬­¬¬¬¬¬­¬¬­¬¬¬­­¬¬­­­¬¬­¬¬­¬¬­­¬­¬­­¬¬­¬¬­¬­¬­¬­¬­­­¬­­­­¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬­¬­­¬¬­®®®®11111111111111111®111®®®®®®®1111111®¿¿¿1¿¿¿¿®®®®¿¿®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®­­®­­®®®­­®®®®®®®®®®­®­­®­®®®®®­­®­®®®®®®­®®®®®®­­­­®¬¬­­­­¬­¬­¬­¬¬­¬­¬­¬¬­¬¬­¬­­¬¬­­­­­­­®®®®®®­­­­­­­­®®­­­­­¬¬­­¬¬­®¬­­¬¬®®­®®­­­­®­­­­­­­­¬¬­¬¬¬¬¬¬¬­­¬­­­¬­­¬­¬¬­¬¬¬­¬¬­¬­¬­¬¬­­¬­­­¬­¬­¬­¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­­­­¬­¬¬­­®®1111111®®®®1111¿®®®®®®®®®®®1111111®®®¿¿11¿1¿¿¿®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®­®®®®®®®­®®®®®­®®­­®­®­­®­­®®®®®®®®­­¬­­­­­­­­¬¬­­¬­¬¬¬¬­¬¬¬­­¬¬¬­¬¬­¬¬­¬­¬¬­¬­¬¬¬¬­­®®­®®®­­­­­­­®®®®­­®¬­­®¬­­­­­­­®®®®®­­®­­­­­­®­­¬­­¬¬­¬­­­­¬¬­­¬­­­­¬­­¬­­¬¬¬­¬¬¬¬¬¬­¬­­¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­­¬¬­¬¬­­¬¬11®®®®®®®®®®111®®®®®®®®®®1111111®®®1¿¿11¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®­­®®­®®®®®­®­®­­­®®­­­­­­­®®®®®­®­¬­¬¬¬¬¬­­­­¬¬¬¬­­­¬¬­­¬¬­¬¬¬¬¬¬­¬­­¬¬­¬¬¬­¬¬¬­­­­­®­®®­­­­­®®®®®®­­­®®®¬®®®¬¬¬­®®®®­­­­­­­­­¬¬­­­­­¬­¬¬­¬­­­­­¬­¬¬¬­¬¬­¬¬­­­­­­¬­¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­­¬¿11®®®®®®®®®®11®®®®®®®®®®®1111111®®1¿1111¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®®®®­®­­®®®®®®­­®­­®­®­®®­®®®®­®®®®®®®®¬­¬¬­­¬¬¬­­¬­­¬¬­­­­­¬­¬­¬¬­¬¬­­¬¬­¬¬­¬¬­¬­­¬­­­­­®®®®­®­­­­­­­®®®®®®®®®­­­®­®®®®®¬¬­­­®­®®®®®­­­­­­­­­­­­­­­¬­­­¬¬­¬¬­­­­¬­¬­¬¬¬­¬­¬¬¬­¬­­¬­­­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­¬­¬­­­­­¬¬¬­®1®®®®®®1®®®®®®®®®®1111111®®®¿1¿¿¿¿¿1¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®­®®®®®®­­®­­®­­®­®­­­®®®®®®­®®­®­­¬¬­­­­®­¬¬¬­¬¬­­­­­­¬¬­­­­¬­¬¬­¬¬­­¬­¬¬­¬¬­­­¬¬­­­¬­®®®­­­­­­­®®®®®®®®®®®®¬¬­¬¬¬®®®®¬¬­¬­­­®®­­­­®­­­­­­­­­­­­¬¬­­¬­¬¬­­­¬¬­­¬¬¬­¬­¬­­¬¬­­¬¬­­­­­­¬­­­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬­¬¬¬­¬¬­¬¬1®®®®®®®®®®®®®®®®®1111111®®®®®¿¿¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­­®­®®®®®­­®­®®­­­®®®®®®®®®®®¬¬­¬¬­­­¬¬¬¬­­¬­­­­­­­¬¬­­­­­­­­­­¬¬­¬¬¬¬¬¬­¬­­®­®®­­­­¬­­­®®®®®®®®®®®­­­¬¬¬­­®®®®­¬¬¬­®­®­­­®­­­­­­­­­­­­¬­¬­¬¬­­­­¬­­¬¬­­¬¬­¬¬­¬­¬¬­¬¬­­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­­¬­­­­¬¬­­­¬­®11®­®®®®®®1®®®®®®®®¿111111®®®®®¿¿¿1¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­®®®®­­®­®­­­­®­­®­­­®­®®®®®®®®®®®­­¬­¬­®¬¬¬¬¬­­­­­¬­¬¬¬­¬®­­¬¬¬¬­­¬¬¬­­¬¬¬¬­­¬­®®®®­­­­­­­­­®­­­®®®¬¬­¬­®®®¬¬¬­­­­®­®­­­­­®­­­­¬­­­­­­¬­¬¬­¬­­­¬¬­­­­¬¬¬­­¬¬­¬­¬¬­¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬­¬¬­¬­¬®11¿1®­®®1®®111111®®®®®®®®111111®®®®®¿¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­®­­®­­­®­­®­­­®®®®®®­®®®®®¬¬¬­¬­­¬¬¬¬­­­­­­­­­¬­­­­¬­¬¬­¬¬­¬¬­¬­¬­­¬¬¬¬¬­­­®®®®­­­­­­­­®®­­­®®®®®®®¬­¬¬­¬­­®®®®®­­¬¬¬­¬­¬¬­®®®¬®­­­®­­­­­­­­­­­­­­¬¬­¬­­­­¬­¬­­­¬­­­¬­¬­¬¬­¬¬¬¬­¬­®¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬¬­¬¬­¬­­­®1111­1®®11111111®®®®®®®111111®®®®®®11¿¿1¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®­®®­®®®®®­®­­®­­­®­­®­­®­®®®®®®®®®®®®­®®®­¬¬­¬­­¬¬¬¬­¬¬­¬­¬­­­­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­­­­­­­­®®®®®®®­­­­®®®®­®¬­¬­¬¬­­¬®®®®­­­­­¬­¬­®®®®®®®®­­­¬­­­¬¬­¬­¬¬¬¬­­­­­­¬­­¬¬­¬¬¬­¬­¬¬­­¬­­­®¬­¬­­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­­¬­¬­¬­®1111­­®11®¿11111111®®®®®111111®®®®1¿1¿1¿¿1¿¿¿1¿®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­®®®­®­­®­­®®­­®®­®®­®®®®®­­­­­¬­®­¬¬­¬­¬­­­­­­¬­­­­¬¬¬¬­­¬¬­¬¬¬­¬¬­¬¬¬¬­­­­¬¬®®®­­­­­­­®¬­­­¬¬­­­®®®®­­­­­®®®®­®®­®­­­­­­­­­­­­­­¬­¬­­­¬¬­­¬­¬­­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­­­­­¬¬­¬¬­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬¬­­­­­¬­¬¬¬¿11111­­11®111111111®®®®®®®®®®®®®®®¿11¿¿111¿¿¿¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­®­®®®®®­®­­®®®­­®­®®­®­­®®­­®­®®®®®¬­­¬¬­­¬­­¬¬¬­¬­­¬­­­­¬­¬¬¬­¬­¬¬­¬¬­­¬­®®­­®­­¬­­­­­¬­­¬¬­¬­¬¬­¬­­®®®®­­­­­­®­®®®­­­­­­­­¬­­¬¬­­¬¬­¬¬¬­¬¬­¬­­­­¬¬­¬­­¬­­­­¬­¬¬¬­­­­¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬­­¬­¬¬­¬­¬­¬¬­¬¬­¬­¿11111­­­11111111111®®®®®1®®®®®®®1111111¿¿¿¿11¿®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®­®®®®®­®®®®®­®­®­­®­­®­­®­­­­®®®®®®®®®®®­¬­¬­­­¬¬¬­­­¬­­­¬­­¬­­­¬¬­­¬­¬¬­¬­¬¬­¬¬­¬­­¬­­­®®­®­­­­­­­­­¬­¬¬­­¬­¬­­­­­­­­­­­­­®­®®®®®®­­­­­­­­­­­­­­­¬­¬¬¬­¬­¬¬­¬¬­¬­¬­¬­®¬¬­¬¬­­­­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬¬¬­­¬¬­¬­­¬¬­­¿1111111®®1111111111®®®®111®®®®®®®¿¿¿11111111¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®­®®®®­®­®®®®®®®®­­®­­®­­®­­®®®®®®­®®®¬­¬­­¬¬¬­­­­­¬­­­­¬­¬­­­­­­­­­­¬¬­¬¬¬­¬¬¬¬¬­­¬­­¬­­®­­­­­­­­­­­­­­­®®¬¬­¬¬­­¬­­®¬­®®­­­­­®­®®®®®®­­­­­®­­­¬­­­­­­­¬­¬­­­­¬­­­¬¬­¬¬­­­­¬­¬¬­¬¬­¬¬­­­­­­­­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­¬­­­­­¬­¬¬®¿¿111¿­®1111111111111®®111®®®®®®1¿1¿¿1111111¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®­®­®®®®®­®®®®®®®®®®­­­®­­­­­­®­­®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬­­­­­­­­­­­­¬­¬­­­­¬¬­­­¬¬­¬¬­­­¬¬¬¬¬­­­­®­­­­­­­­­­­­­­®­­¬­¬­¬­­­®­¬¬¬¬­­®®®®®®®®®®®®®®­­­­®­­­­­­¬¬­¬®­¬­¬¬­¬¬­¬­­¬¬­¬¬­­¬­¬­¬¬­¬¬¬­­­­­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬­­¬¬¬­­¬­­¿1111­®1111111111111®®®®®®1111®®®¿11¿1111¿11¿11¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®­®®®®®®­®­­®­®­­­­­®®­®­®®­¬¬¬¬­¬¬¬¬¬¬­­­­­­¬­­­­­­­­­¬¬¬­¬¬¬­¬­¬¬­­¬¬­­®®®­­®­­­­­­­®­®­­­¬­¬¬­®®­¬­¬¬­®­®®®®­®®®®®®®®®®®®®®®­­­­­­­­­­®­¬­¬¬­¬­­­¬¬¬­¬­­­­¬­­¬­¬¬­¬­¬¬¬­¬­­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­¬­¬¬­¬¬­¬¬¬­¬¬­¬¬®¿1¿11­­®111®1111111111®®®®1111®®®®11¿1111¿11¿¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­®®®®­®­­­­®®®®­­®­­­­­®®®®®®­­¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­­­­¬¬­­­­­­¬­¬­­­¬­­­­¬­­¬­®­­­­­®­®­®®®­­­¬­­¬®­®­¬­¬­¬¬®­­­­®­®®®®®®®®®®®®®®­­­­­®­­­­­­¬¬­­­­¬¬­¬¬¬­­¬¬­¬¬¬­­¬¬­¬¬­¬¬­¬­¬­­­®­­¬¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­¬­­¬¬¬¬¬®11¿1­®111®1111111111®®®11111®®®1¿11¿¿11¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®­®®®®®®®®®®®®­­®®®­®­®®­®®®®®®®®®­­­¬¬¬¬¬­¬­¬¬­¬¬¬­­­­­¬­­­­­¬­¬¬¬¬­¬¬­¬­­­­­¬­­­®®­­­­­®­®®¬­­¬­¬¬­­­­¬­­­­­­­­®®®®®®®®®®­­­­­­­­­­­¬¬¬¬­­­¬¬­­­¬­­¬­¬­¬¬­¬­­¬­¬¬­­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­­¬¬¬­¬¬­¬¬­¬¬®1­®1111111111111®®®®®11111®¿11111111¿¿¿¿1111¿¿¿®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­®®­­­­®­­­­­¬­¬¬­¬¬­¬¬¬­­­­­­­¬¬­­­­­­­¬¬¬­¬¬¬­­¬¬­­­®®­­­­­­­­­­®®®­­­­¬­­­¬­¬¬­­¬­®­¬¬­¬¬¬­­­­®®®®®®®®®­­­­®­­¬­­¬¬¬­­­¬­¬­¬­¬¬­­­­­®­®¬­¬¬­¬­¬¬¬­­­¬¬¬­¬¬­¬¬­¬¬¬¬­­¬¬¬­­­­­­­­­¬¬¿1­¬­®111­­1111111111®®111111111111111¿111¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­­®­­­­­­­­­­­­®­­­­­­­­¬¬­¬¬­¬­¬­¬¬­¬­­­¬¬­­­­­­¬­­­­¬¬­¬­­­¬­­­­¬­­­­­­­­¬­¬­¬¬¬¬¬¬­¬­­®®­­­­¬­¬­®®®®®®®®®®®­­­­®­­­¬­¬­¬¬­¬¬­­­­¬­­­¬­¬¬¬­­­®®®­­¬­­¬­¬¬¬¬¬¬¬­¬¬¬­­­¬­¬­¬­¬­­¬¬¬­­­­­­­­­­­­¬­®®®®¿­­®111­­­1111111111®®®11111111111111¿¿11¿1¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­®®®®®®­­­­­­­­­®®®­­­­­­¬­­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­¬­¬­¬­¬¬¬¬­®­­­­¬­­­­­®­­­­­®­¬­¬¬¬­­®­­¬¬­­®®®­¬­¬­­­­®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬­¬­®­®¬¬­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­­¬¬¬¬­­¬­­­­¬­­­­¬¬®®®1­­11­®111111111®11111111111111¿11111¿¿1¿¿1®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®®®®®®­­®­­­­®­­­­®­®®®­­­­­­¬­­¬¬­¬¬­¬¬­¬¬­­­¬­¬­­¬¬­¬¬¬­­­¬­¬­­¬­­­¬­­­­­­­­­¬­­¬¬­¬­¬­­­­­­®®­¬­¬­­­®®®®®®®®®®®®®­­®­­­®­­¬­­­¬¬­¬­¬¬¬¬­¬­­­®®®®®®®­¬­¬­¬­¬­¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬¬­­­­­¬­­®®1­­­­11®111111111®®1111111111111111111¿11111¿¿¿¿¿¿®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­­­­­­­­­­­­­­®®­­­¬­¬¬­¬­¬¬¬­¬­­­­­­­­­­¬­¬¬¬­­­­­­­­­­­­­­­­­­­®­­­­­­­¬¬¬­­­­­­­®¬­®®®®®®®®®®®®®®®®­­­­®®­­¬¬¬­¬­¬¬­¬¬¬¬¬­­¬¬­­­­®®®®®®®­¬¬¬¬­¬­¬¬¬­¬¬­¬­­­­¬­­¬­¬­¬­­­¬¬­­­­­®­­­­­®®®1­­­®11­111111111®®111111¿¿1¿11111¿¿1111111¿¿¿1¿¿¿1¿®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­­­­­­­­®®®®­­¬¬¬­¬­¬­­­­®­¬­­­­¬¬­­­¬­¬¬­¬¬­­­­­­­­­­­®®­­­®­­­­­­­­¬­¬­­®­¬­¬­­­­®®­­­®®®®®®®­®­­­®®®­¬­­­­¬¬¬¬¬­¬¬­¬­­¬­­¬­­­­®®®¬¬­¬¬­­¬¬¬­¬­­­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬­­¬­­­­­¬­¬­®11­11®1111111®®®111111®¿11111111¿1111111¿1¿¿1¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­®®®­®®­­­­­­­­­­­­­­­­­®®®®­­­­¬¬­¬¬­¬¬­­­­­­¬­­­­­­¬®­­¬­¬¬­­­­­­­­¬­­­­¬­­­­­®®­­­­­­¬¬­¬­¬­¬®­¬­¬¬­­­­­­­­®®®®®®­®­­®­­­­¬¬­¬­­­¬­­¬¬­¬­­¬­­¬­­­®®®®®®¬­¬¬­¬¬­­¬­­¬¬­¬¬­­¬¬­¬­­¬­¬¬¬­¬­­­­­­­­¬11­­­­®1®­­1111®®®111111®1111111111¿1111¿¿¿1111¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­­­®­­­­­­­­®®®®®­­­­¬¬­¬­¬¬­¬¬­­­­­­­¬­­­¬­­­­­­¬¬­¬­¬­­­­­­­­­¬¬¬¬­­­­­­­­­­­¬¬¬¬­¬­­¬­®­­­­®­­­­®®®®®®­­­­­­­­¬¬¬¬¬¬­­­¬¬­¬­¬¬­¬­¬¬¬­­­®®®®®¬¬­¬­¬­­¬­­­¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­¬­¬­­­­­­­­­­­­¬­­­®®­¬¬¬11®®®®®1¿11111111111¿¿1¿¿11¿¿¿11111¿11¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­®­­­­­­­­­­­®­­­­­­®®®®­­­­¬­¬­¬¬¬¬­¬¬­¬­­¬­­­¬¬¬¬¬¬¬­¬¬­­­­­­­­­­­­­¬­­­­­®­­¬¬­¬­¬­®­­­­­­®®­­­®®®®®®®®­­­­­­­­­¬¬­­­­¬­¬­¬¬­¬¬­¬¬­­­­­­®­¬­­¬­­¬­¬­¬­¬¬¬¬­¬­­¬¬­­­­¬¬­­¬­¬­­­­­­­­­®1¿®®­¬­­®111­®®®®®®11®®11111111111111111¿1111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®­­­­­­­­­­­­­­­­®®®­­­¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­­­­­¬¬¬¬­¬¬¬­­­­­­­­­­­­¬­®­­­­¬¬¬¬¬­­¬­­­­­­­­­­®®®®­­­­­®®®®®®®®®®®®­­­­­­­¬¬­¬­­¬¬­¬­­¬­¬­­¬­¬­­­­®¬¬¬¬¬­¬¬­­¬¬­¬­­­¬¬­¬­¬­¬¬¬­­­­­­­­­­­­­11®®­­­­®111­®®®®®®11®11111111111111111111111111®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­­®­®®­®­­®­­­­­­­­­­­­­­­­­­­®®®¬¬¬¬¬¬­­­­¬¬­­­­¬­¬¬¬­­­¬¬¬¬­­­¬­­­­­®­­­­­­­®¬­­­­®®­­­­­­¬¬­¬¬¬¬®­­­­­®®®­­­­®®®®®®®­­­­¬­¬­­­­­¬¬­¬¬­­­­­¬­­­­­­­®®®®¬¬­¬­­­¬­­­¬¬­¬¬­¬­­­¬¬­¬¬­­¬¬¬¬¬¬¬­­­¬­­­­­­­­­­­­®11®­­­­®11®®®®®®®®®11®111111111111111111¿111¿11111111®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­­­­­­­­­®­®­­¬¬¬­¬¬­­­­­­­­­­­­¬¬¬¬­¬¬¬¬¬­­¬¬¬­­­­­®­­­­­­­­®®®®­­®­¬­­­®­­­­­®­®®®®®­®®®®®®®­­­­­­­­¬¬­¬¬¬¬­­­¬¬­¬¬­¬­­­¬¬¬­­­­­®®®®®­­­¬­¬­¬­¬¬­¬¬­¬¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­­­®­¬®111­­­®®®®®®1®11111111¿111111111111111111111¿¿®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®­­­­­­­­­­­­­­­®®®­­­¬¬­¬¬­­®­¬¬­­­­­­¬¬­¬¬¬¬­­­­¬¬¬­­­­­­­­­­­­­®­®®­­­¬­¬­­­­®¬¬¬®­­®®®®®®®­®®®­­­¬¬­¬­¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­¬¬­­®®®­¬¬­¬­¬­¬¬­­¬­¬­¬¬­­¬­¬­¬­¬¬¬¬¬­­®­­­¬¬®®111®®®®®®¿1®®®®®®®®®®®11¿11111111111¿1111111111111¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­®­­­­­­­­­­­­­­­­­¬­®®®®®®­­¬¬­¬¬­¬­­­¬­¬­­­­­­­¬¬¬¬­­¬­­­¬­­­­­­­­­­­¬­­¬®®®®®®®­­¬­­­­­­¬­¬­­®­¬­­®­®®®®®®®®®®®­­­­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­­¬­¬®­­¬¬­¬­¬­­­­¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­­­­®®1111111111¿1111111111111¿1111111111¿®®®11111111111¿¿111¿1111111¿¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®­®­­­­­­­­­¬­­­­­­­­­®®®®®®®¬¬­­¬¬¬¬­­¬­­¬­­¬­­¬¬¬­¬¬­¬­¬­­­­­­­­­­­¬­­­­­­­®®®®®®­®­­­­¬¬­¬¬­¬­®­­­­®®­®­­®­®®®®®®®­­­­­­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­­¬­­­®®¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬­­¬¬­­¬¬¬¬­¬­­¬¬¬­­¬­®­­­­­­­¬¬®®1111111111111111111111111111111111111111111111®®111111111¿111111¿¿11111¿¿¿¿®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®­­­­­­­­­­­­­­®®®®®®­­­­¬­¬­¬­¬¬¬­¬¬­­­­­­¬¬­¬¬¬¬­­­­­­­­­­­­­­¬­®®®®­­­­­¬­­­­­¬¬®­­­®®­®®®®®®®­­­­­­­¬¬¬­¬¬¬¬¬¬­­¬¬­¬­¬­­­¬­­­­®®®­®¬¬¬¬­­¬¬­¬­­­­­¬¬­¬­¬¬¬¬­¬¬­­­­­­­­­­¬­®®®®¿11111111111111111111111¿111111111111111111111¿®1111111111111111¿111111¿¿¿11®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­®®¬¬¬¬­¬¬¬­¬­¬¬¬­­­­­­­­­­­­­®®®®®­­­¬­­­­¬¬­®®¬­­­­­­­­­­®­¬¬­¬­¬­¬­¬¬¬¬¬¬­­­­¬¬­¬¬­¬¬­­¬­¬¬­¬¬­¬­®®®®­­®®­¬­­­¬­¬¬­­¬­¬­¬¬­­¬­­¬¬­­¬­¬­­­­®¬¬­®®¿¿11111¿111¿1111111111111111111111®®111111111¿111111111111¿¿¿¿¿®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­­­®®®®®­­­­¬¬¬­¬¬¬¬¬¬­¬¬­­­­­­­­­­­­­­­­®®®®®®®®­­¬¬­­­­­¬­­­­­­­­­­­­­­­­®­­­­­¬­­­­­¬¬­¬¬¬¬¬¬¬­­¬¬¬­­¬¬­¬­¬¬­¬¬­¬®®­­­­­­®¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­¬­­¬¬­¬­¬­­­­­­­­­®®®®®®®®®¿111®®¿1111111111111111111111®111111111¿1111111111111111¿¿1®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­¬­­®®­­­­­­­­¬¬¬¬¬¬­¬¬¬¬¬­¬­­­­­­­®®®®®®®®®®­¬­¬­­­­­­­­­­­­­­­­­­­­­­¬­­­¬¬­­­¬­­¬¬¬¬­¬­¬­­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬­®®­­­­­­­­­­­¬¬­¬¬­¬¬­­¬¬¬¬­¬­¬­¬­¬¬¬¬¬­­­­¬¬­­¬¬¬­­­­®®­¬¬®®®®®®®¿11111®®¿1¿11111111111111111111®11¿11111111111111¿1111¿11¿11¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®®®­­­­­­¬¬¬¬¬¬­­­¬¬­­¬­¬­­¬­­­­­­­­­®®®®­®®­­­­­­­­­­­­­®­­­­­­­­®®­­­­¬¬¬­­­¬­­­­­¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬­­¬¬­¬¬¬¬­¬­¬¬­¬¬®®®­­­­­­­­­¬¬¬­­­¬­¬­­¬­¬­¬­¬­­­¬­¬¬­­­¬­­¬­­­®­­®®®®®®®®®11111111111®®®¿1111¿®®®®¿11¿¿11111111111111111¿11¿1¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­®®®­­­­­­­­¬­¬¬­­­¬¬¬¬¬¬­­¬¬¬¬­­­¬­­­­­®®®®®®­®®­­®­­®­­­­¬­®®­­­­­­­­­­®­­¬­­­¬¬­­­­­­­­¬¬­¬¬¬¬¬­­­¬¬¬¬­¬¬­¬¬­¬­­®®®®®®®­­®­­­­¬­¬­­¬­¬¬­¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬­­­­­­­®­­­¬­®®®¿®®®®®®®®®®®®®1111111111111¿111111111¿1¿¿1¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®®®®­­®­­­­­­­®®­­¬­­­­­­¬¬¬­¬­¬­¬¬­­­­­¬¬¬¬­­­­­­­­®®®®®®®®®­®­­­­­­­­­¬­­­®®®­­­­­­­­­­­­­­­­­­¬¬­­­­­¬­­¬­­­­­¬¬­¬¬­¬¬­­¬­­¬­¬¬¬¬­¬­¬­¬­®®®®®®®®®®­­­­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬­¬¬­¬­­¬¬¬­­­­­­¬¬®®®®®®®®1®®®®®®®®®®®®®­111111111111¿¿11111111¿¿1¿11111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­­­®®®­­­­­¬­­­­¬­¬¬­¬¬¬¬¬¬­­­­­­¬¬­­­­­­­­­®­®®®®®®­­­­­­­­­­®­­­­­­­­­­­­­­­­­¬­¬­­­­­­­¬¬¬¬­¬¬­¬­­­­­­­­­¬¬¬¬¬­¬¬­®®®®®®®®®­­­­­­¬¬­­­¬­¬­­­¬­¬­¬¬­¬¬­­¬¬­­¬­¬­¬­­­¬­­¬­­­­­®­­®®®®®®®®®¬¬®®®®®®®®111111111111¿1111111111¿1111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®­­­­­®®®®®®­­­­­¬­¬­¬¬¬¬­¬¬­­¬¬­­­­¬­­­­­®­®­­®®®­­®®®­­­­­­­­­­­­­¬­­­­­­­¬­­­­¬­­¬­­­¬¬­­­­­¬¬­­­¬¬­­­­¬­¬­¬­¬­¬­¬­¬®®®®®®®®­­­­­¬­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬­­­¬­¬¬­­­¬­­¬­­­­®­­®®­­­­®®®®®®®®®®111111¿11111111111111¿¿¿1¿¿¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­®­­­­­¬­­­­­¬¬­¬¬­¬¬­¬¬­­­­­­­¬®­­­­­®­®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬­­­­­­­­­®­­­¬¬¬¬­­­¬­¬¬­­­­­­­­­­­­¬­¬¬¬­¬¬®®®®®®®®­­­­­¬¬­­¬­­¬¬¬¬¬¬­¬¬­¬¬¬­¬­¬­¬¬­­­¬­¬­¬­¬¬­¬¬­­­®­­­­¬¬¬¬­­­¬¬¬¬¬1111111111111111111111¿¿1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­­®­­­¬¬¬¬¬­¬­­­­¬­­­®­­­­­¬­®­®®®®®®®®­­­¬¬­­­­­®®­­­­­­¬¬¬­­­­¬­­­­­­­­­¬¬­¬­¬¬­¬¬­­­¬­¬¬­­¬¬­­¬¬¬­¬­­¬¬­¬­®®®®®®®®­­­¬­­­¬¬­¬¬¬¬­¬­­­¬¬­¬¬¬¬­¬¬¬¬­¬¬­¬­­­­¬¬­¬­­­®­­¬¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬¬­­®®111111111111111111111¿¿1¿¿1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­®­­­­­­­­­¬¬¬­­¬­­­­­­­­­­¬¬¬­¬®­®®®®®®®®®®®®­­­­­­­­­­­®­­­­­­­­¬­¬­­¬­­­¬­­­­¬­¬­¬¬­­¬­­­¬­­­­­­­¬­¬¬­¬¬­¬¬¬¬­¬¬­¬®®®®®­­­¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­¬­­­­¬­­­­­¬­­®­­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬­®­11¿¿11111111111111111¿111¿¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®­­­­­­­¬­­­¬¬¬¬­­­­­­­­­­­­­®®®®®®®®®®®®®®®®®­­¬­­¬­­­®­­­­­­­­­­¬­­­­­­­¬­¬­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬­¬­­¬­¬¬®®®®®­­­­­¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬­¬­­­­­­­¬¬­­¬­¬­¬­¬¬­¬¬¬­¬¬­¬¬­¬­¬¬¬¬­¬¬¬¬­­¬¬­­­­®1¿¿1111111111111¿¿111¿11¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­­­¬­­¬¬¬­¬­­­­­­®­­­­®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬­­¬­­­­­­­¬¬­¬­¬­­­¬¬­¬¬­¬¬­¬­­­¬­­¬­­­­­¬­­¬­­­¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­®®®®®­­­­­¬¬­¬­¬­¬¬­¬¬­¬¬­¬­¬¬¬­­¬­¬¬­¬­¬­­­¬­¬¬­¬­¬¬­®­®®­­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬­­®1¿11111111111111¿1¿¿¿1¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­®®­­®­­­­­­­­­­¬­¬¬­¬¬­¬¬¬­­­­­­­­­®®®®®®®®®®®®®®®®­¬­­¬­­®­¬¬­­®®®­­­­­­­­­¬­­­­¬¬¬¬¬¬­¬­¬­¬­­­­¬­¬­­­¬­¬¬­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬­­®®®®®®­®­­­¬¬­¬­­¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­­¬­­¬¬­¬­¬¬¬¬­­¬¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­­­­­¬¬­¬¬¬­­¿1111111111111111¿¿¿¿1¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®­­­­­­­­­­­¬¬­­­­­¬¬®­­­­­­­®®®®®®®®®®®®®®®®®­­­­¬­­­­®®­­­­­®­­­­¬¬­¬­¬¬¬¬­­­­­­­­­­­¬¬­­­­­­¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬­®®®­­­­¬­¬­¬¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬¬­¬­­¬­¬­­¬¬¬­­­®¬­¬­­¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬­­­®11111111111111111111111111111111111¿1¿¿1¿¿11¿1¿¿¿¿¿¿¿¿¿¿¿11¿¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®quakespasm-0.93.0/Misc/fitzquake080sdl.txt0000644000000000000000000000676311513277221017062 0ustar rootroot================================================================================ Beta release of an SDL port of Fitzquake version 0.80, July 5, 2008 Author : Kristian Duske Email Address : deceive.inveigle.obfuscate@gmail.com Author's Homepage : http://www.kristianduske.com/fitzquake Minimum SDL version : 1.2.10 This is a port of the Fitzquake engine to SDL. The main goal of this port is to allow fitzquake to run on all major platforms. Currently there are builds for Windows, Linux and Mac OS X. To run this engine, you need to install the SDL and SDL_net library binaries. For Linux, you should probably use the packages for your distribution which should be available through your package manager of choice. The minimum SDL version you need to run this is 1.2.10. On Windows, you can download the SDL and SDL_net dlls from http://www.libsdl.org/. On Mac, the SDL framework is included in the application bundle. The Mac OS X version includes a simple launcher program. Known issues: - Mouse sensitivity is different than in the original Fitzquake (or vanilla Quake, for that matter). - Pasting from the clipboard does not work. - It is not possible to switch the screen refresh rates from within the engine. - On Linux, there are problems with the default sound sampling rate of 11025Hz. This can be fixed by supplying -sndspeed 48000 on the commandline. Changes since the March 7 beta: - discard mouse movement while input is deactivated - implement maps and mods commands using POSIX functions and added a POSIX wrapper for Win32 - activate mouse input when binding a key, otherwise mouse keys cannot be bound through the menu - fixed shift key behaviour - implemented TCP networking - fixed fog command (use fmax instead of max in Fog_FogCommand_f) - fixed bug that lead to the screen being set to minimum size when the sizedown command is issued multiple times - LSHIFT + ESC and circomflex always opens the console - print everything to stdout - and more... ChangeLog: ---------- 01/22/2008 - release mouse pointer when console is active. - enable key repeats (doesn't work on OS X, test in Win32).c 01/23/2008 - implement Sys_SendKeyEvents - disable mouse input when console is inactive using SDL_SetEventFilter - move mouse input processing to main event loop - add vid.mode to determine window mode - fixed Sys_printf() - enable key repeats when console is active only - return 0 if any of the cl_bob vars is 0 in V_CalcBob 01/24/2008 - changed in_deactivate so that it does not always release the mouse cursor - adapted all calls to in_activate and in_deactive, because they need to be called regardless of the current mode - added platform dependent messagebox code for fatal errors on OS X and Windows 02/06/2008 - fixed numlock acting as caps lock issue - fixed: input is not activated on map command (hopefull got them all this time) - center window in windowed modes 2008/03/14 - discard mouse movement while input is deactivated - implement maps and mods commands using POSIX functions and added a POSIX wrapper for Win32 - activate mouse input when binding a key, otherwise mouse keys cannot be bound through the menu - fixed shift key behaviour 2008/06/3 - fixed numerous bugs in sdl_net.c 2008/06/4 - fixed fog command (use fmax instead of max in Fog_FogCommand_f) - fixed bug that lead to the screen being set to minimum size when the sizedown command is issued multiple times 2008/07/4 - LSHIFT + ESC and circomflex always opens the console 2008/07/5 - print everything to stdout quakespasm-0.93.0/Misc/QuakeSpasm_512.png0000644000000000000000000014366512027635624016543 0ustar rootroot‰PNG  IHDRôxÔú IDATxÚì½yœeWußû[ûœs皺»ªGu·º5¢ !²À̘Ù8 ãg<ãÇÆñþ8q ácÇ/ö‹_ ‰c^ÀŽƒñ#˜`cl3  , $¤–z®î®î®éÞs‡sö^ï}æ{ouµººêv÷úÖç¨îX­Ï:çì5ì5‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ! BJ£QßàåÞ ày @øÀÿ™]n5›")A AöíœùaÇu^ç8Î^¿Õ™""Â7|ôÐìÜ}"!aÊÿ™¾  ‘}]¥K¥ÑÆ,1ø£¾ß~HL@6€z­v/$ð˜ñ¯UPrÈ^ÝÑÎ࿬zÞ;;<{B$' â–ëöÌP)y({.&ÕÜg 3:ÝA¨1¿ìÃu”J.Y­:¡µ)þqðJ;³ô¦^·óI‘º እѨMôUf\°B6ŸۻgfjƒHÿCë$Üš ¤Ê?YxÂωąaÌŒ7î Œ>JD[°ë8435–3>³¿Ož].$r L6JŸÃÅDp\W}¢äÕÿÀ=Íf‹Eú‚¦üë?ð¿®Ã¾<~ŠŒ›„5ÀzT¡6 ZuKlPüúR³y¿H]Æã'Ã=Û§¯ã·¼ '¾†âÒ@"ÂÜ|ÍvÕZ9—`¢+-k´2TzíÞ `±Ñ¨ÿL³Ùúc9Âz#e€ÂF(þz£QÀ’E–ŠõûÈ,¤Ú˜èàü¾Uá;Ìöóñö- /·ZÉ çâÐìÜò¡sÿìЉ9×uÔ[œM®'‹M*À&¯à‰ò{ªDÏus‡ë88µÔÊÚ o4꟭×kâ b—µò3€cnŽõs¬Á9ΡJ2ú¬ ׯ ð£lòŸ£(5vÏ?^©Á EïjùþÏŠä…óåÑCÇÿøKß~lóR³ó†ƒÇÏÜðøY:xü,7ýn¡ÕdÞXeæ\£ […b{ œ\l¦ù)éGÀ+ˆèHµZ}£H^X/$ PXjµš àÌüË(VòBÙõQá97îE/Ð 2ökÌèiƒo}ÿ(”"8ÙV­éß™ñÚvÛÿ¬H_X öl›~l3ªifF¹Rb•±@™Šg+ºÝÆ0N/µf X=.¶Ž›jmˆˆþÀ;}ßo‰Ô‰—ºò¯ø*€_&"& ŠGº ÚßÕ’‡;®Û)ÿTÅ+¥ ”‚Hƒ6Ùx Àu¢ü…µäЉ¹¿°ÀO¼wéu¬ˆ %å¨Ù£¨üãçÆ0Œí(ÈÌücø€W*¿A¤.H@¸”•ÿ_ŠN>Ÿk®RrqóÕÛÓ5 ÌŒ'ÏXhµã¶€ñVÂ3>ìûþ¯ˆä…‹É®éÍ•j½ü¿”R/a 9è…Ô 5ZÝ^Ò¥"nBÚŽ: šl•ohÀ03ÿ;Rô/ÍÎ-‹Ô‰—’ò¿ÀÔ\ÖÇ+€m¨B¤òÞ©¸ÑJÜó7n½º_”¿°;Óq]÷åÌ|€NT%À*š`£TÔß50ïñ'G|Ol™¨cÓX• 'ó~Àã{¶O?K¤.H@¸À/øMf¶Ô¸¯•Ñìt†^†D„’ëô½î(m Î.û]"ú€´Zþ×EòÂF±{ë–?ð®ÀøÝáƒC­AD‰ÇŸ…™±u²a·Àbs™­›ÆÌØ¿sæwëÒ¯ýÅìŠÄ1„QTþ/ðYümSƒØ ¾òZî@/)6ª¥¤ k6‚ð(3ÿÖ‘¹…O°{"uaDŒ€«zZ«Ý ·X}ž´xz@Œfìß1M†Ùg6w|ùÁï?*.ÙÖJñï¯Õjàó‘ò›¢LfA;¿žgÙOßàçì?4;wãá§?.Ê_%Ÿ<}dn±µÀ¿·ö+qjî!âÃDc„‹Êßn‡ d•xÿd ØQ¨¹Žó×?ÿö[Dâ‚D„ ç¦kwÿò£sR…Ò¦xŠÚÖ‰¸àÇ3léÓ VDT)y ^vhvNºø — ãc/gæOkcòø‘³"iA"ºÀÌ_œ[|5)Zum¿íÏŸ†9)šöGýFé·+ž'eOÂeÁÜÙÅ à…·!&)€”-}UÈJ%Ê?L#p•ÚjóðõûvîI .*ÕZm†€OpO´ ´ŒaìØ2‘NDcF©ZØöD?pdŽê›ÊÀG;íöÿ!’.7jµÚuþ€ëQè‹a ƒ…E©¹ r÷ÒŠFÉsyfS tà |ÍÁcsŸI bCùß àËT² XѰÓû×ìÚÍ…¹)Dp•“GOA)ñàWµ|ÿ‹¤…ËØ(3óŸx=rð2¸°0«4nÀ™›izÓ67pÃÞ]Ql2JÑ?ö_|¿HZX Žˆ@XåÂõÏðgd¯™\ä¾hEöïÚ mLægPÔ+Ì8pl.ir”çô-^Öòý¯Š¤…Ë™ t†ÿcód½].9/í…š£®–ı9@v+ ¹ËL¬òã™Ö ¨UKPаmó\lj;¾à–ëöÜöŠ{žõ_}ðÑ@$.H@¸ êõÚ'Àø‘h "Nr–û1ÌØ·s3ŒáLy'«3ãðìYVIº3þ;€où>‹¤…+‰íÓ“Ï\lu¿ED”­ÌEŒÕ+˜«E17^½ Zuàß~âÈìÙ…åÖµ8*ÉÂP$ PXIñïª×kø‘xÒ“]¥œÌ4¾ÜA* l²-Ùþ‡@ t@xMË÷ß.Ê_¸™[xˆ¶øzìŒ1§‰~v+Í@³6&ɧa¶C¯yø]Ã̼ À±gìßõ&‘´0 WD ¢\.oð$/JD¢X‡Sf´i„Ñbg.2ˆÁßÑ­–oDÒ•L«åŸpW½^û0~*kx®‹Ó“Q¼Ív 40 ¦¡¸(ŒßeüöïºúÑG?$Ò,ׂ纽;ˆÍc§vªB™Ÿíð§†zZÃDI€Ùµ)oú®Çž:þa$;›‚ ÀøXã‡|†™a SÉu±uz<³ïO`vLOa¢QG­R‚ãäÓ¸¥ðÀã„ÐÚ þ¢1æÍÎý¤HY€0”gß²âì’ÿ3ÇÚ;INâ•bŽQdŸ™ms"4Û]œ<³ÌQG“cÝ^ø˜ð>‘² ô³´ÜüìøXãVG©ÏìÛ¹i·ap/ÔT¼ë*¥ÆêÕÔ WiN®R„ÅeÊ¡¸‘PÜ…ð{¶OWæ[­·--É–› €0€V§÷õãs‹[ÈÎ6·+EÿˆBÿ»÷P´O‰¨É07ßdÇ4 ÀÁ„M‘° ¬h<<³eê&mø^NÔ(3ÕßJ9p½ ´Ï‰l5(›ªK0~´ìº;ü HZ$ Pˆ¸fߎÚÍ×^õ3_·%ç(59>ìJB™¥"™Båh+òO|ß½ïû¢üaœ:=ß|øñï`æßJ•w4 PÇã(ûX)¡FjôB±QžöÚ¤8ð‚™ÍÿQ¤,H@H/—?àæÐhv"-!YÅΰ ~j¥$ÀÔM!ø1‘® œ?Ö Ô< ':Jõ¿i¯,-7ß'„¡Z­>T«x7«(ª߳̌jÙ…aÆôÔ8˜Ó K ½@'†‚"BÙórw=A/88{Jævˆ \ÊÔjÕO×Ê¥W+"ÚÝ g ÚÛlljÔP-¹ÙŽcL¶Ëè=~äÄOˆ„aãp¼rcªQ~”ˆvƆy§gÛWKÖ˜«,ûmƘ¤r@¡’1â±^`,5*×<üĩ蹌‘$À˘z½öA"zu»p'©Ó s!BN.ŽMÌ‘¦ e^a¶¥Ÿå/ºM"Ú àläÐqmíõ´A  ›>›>˜‘4†íëÁì8jL}ãïxFE¤|ù"}._åÿ~}ÓÄRh…g©‰Ð÷¡¯8À›D‚0Ì]4Scw¡9Jºx2g±¯ÇJÌlǦñU+%¯W÷|÷©cð#"e‰—Žòß àý+)ÿÁʾX/ló…ãid¾qàè‰{;zB‹”at˜_\>h˜¯5†}›ÌGè!º™é€ýÄ“(‚1«WШUШU1V¯ÂQŠ 3‡Ú¼áº=Ûß/¾<‘€ËÓ˜°-ûZ6‘¯xÒCÃoßÚ Æ›Æª¨z.8FDûž8z¢'„ѤV«½ À_eÿFÕ‹vöã2_‚0V«à¦}W¡bïñ>åðð£Üêt‰™Ÿ}øÄéDÂF˜F£þ^¶egˆh@‰_z 2 ´aìØ4Æ®R(•½§&&ªûEù ÂhãûþçüØŠÎ)õÈÎWD(»¶mžÄæ‰1”(ÿŦ¹ùe¡&¶½?îß³}zJ$|y!}.#ÆÇDDŸ'"'ê–$ýÃÉàž¨‡hÔ9¬¸BX&ê(ûåÞäx}ß7¿û”/„Ñ'‚×Ý;V-Ý^rÙ!Ýõr®ãÀQ •’‡›¯Ù…‰F-ÊàœïäÄÌž^ˆF3G³ë‹Mÿ³"áËI¼¼ø"€$k—™QòœÄ£/2V)ÃUƒ@<³i ÕJ©9V+ßüé/?¸,¢„K‡N»ýޱª·Àk<ÇI |êî"†Av‹PGŸµ!Q«Ócf~ €Ÿé^>ÈÀeÂX£þ?€Bê¾ë(x®‚çJ®Jω¦ùÁD©Año°a&ÃLÌ|Û§¿üàa‘® \zŒW«o©—Ë]¥à)‡‘ Ð?(@…WN/ Nn©×k÷ŠdÅF "ú0ô{š,µ:Ïýâ·}J$+—&ŽôÜEDÍÕ® Ív§Î.b¾écÑï`Ñï`Éï$ƒ†˜ùc­c1„Ñ¡V«~Àv (Ü?˜ˆðK:þ ‘¬ \Úœ; à…B«Á‹«D¾R8 C´»:=^hÐ Bͨ–KÑáíÚ<5þoD²—bÉ]âlÛ2q]/4VDj•﹈ã:ùù`ÀÁÙÓ/É ÂåÁBÓ?>9Ö8æ×ÅK„£¶m™@7pòÌZmMßG7À ,¶:IEÇQ‰ƒÐ=µjy¹ÝéÊÀ ‰I7ÐÀc¶ƒ@²GbáG™³~âÊ€¤™q¿HU.·HÀ©ÿ¢G¶>'B ¡„ÚÎ ˆ ¥Õ²‡’窅À~{Ó䨳E²—6Rp 3>ÖxU¨ù.E„JÉXÒWöì)Ž‚%×…ë¤Á­ul#ç:'’„ËmÌOÌ7Û¯Pw]‡gç2}@¢G˜â‹; ükJö‚º11ÞPÌøÍxô'p¦–WÁ¾jf MrçúÝÚ¤Í@µ‰ ÝéÿN¤+—GNž êõúM¾Ï JQ¢ø‡Í ¢þG ÀoÔÿv©ÙúA‘l\ºìð¬þ•Ò–ŸDBƒn í%õpÔ ,s°ýÛØ„2þS.SZ­Ö!ì`²n¬P#-LâÁ &ÆoÉŠ ¬/ÿeðùãÇŠÄiÀïkµÛ‹Xá²çŸèb@ÒðR«ƒãs‹èô‚Õ Š^ µAhÔ+eTËÞŸnÝ´ £N€†  ˜Ó-GP2U‰è¶m™’ª²K ɸīÿ>l;Îóõ;`/å°"zµHV®n½fûNÍ7éá3fÏ,%¿ƒP÷­Ì ­3y~d+‰2£†Àmþ#€ŸéJ@¸Ôjµß#¢U*ÿ•CÿÑ 0bæw/7[2èG® >ó•‡ €÷13ù·;ÚÝ¡6Åu"Ññqé`6_ÀQ6‰P)b¥è§®Ý½õ"ÝK\Ôëõßcæê:ªOù33*¥üHOÊ|ă scÇ7µ1ü`Ë÷oé •Éäxã­o!Vù²ËŒ£8SÈÉ Æ0¶oË–3³9sàè©i‘¬ÂÚ)ÿÝž ˜ÍÀsæ(…‚‹ØöÉX­ÂÕ’ËŽR/>pìäD‚peÒ¨×^ðçõlX#î$Ê „Æ€™±e¼ž[vŒÑÐÆüÙ¡Ù3ÿH¤;úÈÀ¥Ák`Û6“­Õ¥Ü{üê{(¤­€̘ ¹OD+W4_pçØ3«•1^+c¬ZÆx­‚²çôE@mÌë_pÇõ? ¢}$ ðÒàWS_>`f¨X±ˆ d÷ðÿ#?shv.Ñ Â•K³åwõÚÛ|7ª%ªUJhw{0†áDM‚bÏ¿H†€úcöˆ„% \õzíE€Ù ˜´j¯0õ׆¾"‚6ñ b(Ôæ‡NÌ}J¤+B³åÿ53‚™©Rö°e²Ž©F cÕ2ª%õJ)—S”Y\ê¡ÖÉm52»Ÿ{ËþŠtGÉ}à>ÏK÷¬Íf‡û8JõMúËj¿ÏÆbÆUNû¨HWصuó5cµÊㆠO4ªÄÌ8»ÐJÊý²ë 3à÷zL¢F˜eÏTÊ3³µ¹é‰#'¿/–€pþÊÀMHªöb# :8g dnP»-à¨ìM˵ïüiQþ‚ d9zòÌZùwÆëUžë&!ÿ8Šé¦b:M4^Ÿ¥à(…n/¤^ ]cø“"]1„§ý"š $RCèkó;¤ÛïXµ„Éz%1¢ãoÚíöÉ ‚PäþGž|/3¾Ï[‡?ÝSìO:¶Ī¥*ž—Éw8û}A á¼k4n$Ð`D7Xzsq^Égžž£Pö\”=—ˆè×D²‚ w:ð~dŒaÃÚ–õ°¥ÄñQñJÉ:£”ŠŽÊ^íÛ¹u¯HvDÏ·ˆ`ôh4êc: `2 Þ“aΟ­¸Tc;ÃDTªcvDøÖ¡swˆtAX‰gß´ï+®C÷ÌÎ-A) núsË5»£–À cß?<‹¸,9^‰´ dzG(ÎZþ€;Ξ EºVöü§<ÎàÉÈ‹§ävAE?D ¬ (J$ ׬ëü•ˆðy‘® çŽÐ눔¯ ³6HöþEpÛú—¢E%éÈý%ÊÆ2¶aÐm‹­¶Œ –€° ào Ì Dq?››ÚÚÅ®6×ߌñjeÏ&òX+;ÎÎ ‚p.vÍlþ¿ï¼qß?ÑÆ$ËŒ­°¿µa]‹’^" "¡FÔÿ;E02 zAˆV§‡åNËí.ší.Gùÿ\$+€0€z­¾™£,>EÇQ0ÑÀ`™$Üo  @(y H ã?H×?AÎ~m’b̳sìåG¦MˆÀD`6ÐÆ „è!Ú†h2 &cÞ[¯Õ<‘ëè ­€G„mÓã?aL¼‘Æ(¹.´18>·óÿi…]¢´"àÌb‹]GÑÔXí_>1÷{"aAÎË;Tæ>fú&3Ý‘­õÏÿ‰¼ÿøù€~ÁQñ20àKîéJ@Èà¹Î¯—K.•K.Ê%¤¨ÐÇŸ"åŸöHËþÒç°@ÚðOš{¿HW„óåS_|úáXå+EPdì1mlT OùÇ­J2KwÕ«µ×ŠtGInØ¿ë…ÿ-Ò²@„˜=½˜+³IÛnÚrà ŒîÃåVKί Ä+ïyæ¿PDÿö»OƒR*‰26Ûh-â¾Vä D¨¹ l’õë`Ë÷¯ÉJ@˜×E7­Î^£4.0pü/ˆž¹ ‚°~âÿGJõº¡F/Ô£#ŽP›C’IˆrÈZ„½õzíE®äŒÏ;›qµ”I\‚ \0ß;xì 1Üsy“ßí Uú½P#Ðf5 دÔëµ·Z~S$”̦ IDAT,€+©üS^ñÃ\Øk+~š Àÿ± ‚p¡ûŸÇOÕ"YAÖ‚ Ô\.¹œ¨µ¨6&Y´1´1|>©å/©×k{Z-ÿHX"W,F›žÑLF3ì¡Qlô›Fòÿ²ÑLY ŒüaÍ86w&TDæûÚ4;=´{!´1`ƒ¡#Ê“(@þ½ßéŠpŲoÇÌ>fÞ‘Þ±âÏŽúM•~ró!î @Å­¹îì©ùûE²‚ ¬%|E±R¿Âïèq{`€l%3¡hIvY‹»Ëùòz½ÖéŠpEÒ ‚_%¢JÖÝgºA”MËù(à:v*W 5zaˆ^ÚÞÜ¡æN/üŠHU„µæøüÒWgç—i©Õ³LvX™=õûÉÎ*`ñ¾å €W‰tŸâ¸~÷öwyŽóÎAYý‹~Ëí.%ÚVJ.Êž¹Újá—E²‚ ¬5ŽR_TŠ@DC3•²9®ã€ÏQÍ”AJ–Ÿ²Ø¶eê^~÷—ÓpÔ<ûРþ†HW„µfi¹ù$€£‰ïN”tŒG—÷uÌô.‹·2 yL  T¯×Þ-àŠ`b¼qsê/pЧç)âË\èö—‰ Å}ÿ3ó˜6ˆ„A¸ðAD €®£úG©|‰`1ì?ðO‚ü|½^}´þçSXo®Ù³ýÑæªÅf»ïp¤Ü;½0jLO¿ßt.pØ÷Û{D‚ \ Æõ*-ë—¬¬>B­sÑ€þê¥ü †ùõvçS"åõCú¬3·\·çÍìµÁÉùåþ$Ùþ6™¦x‹Å7ç,8FÌù¯"aA.å’×ð3ßÙ B:ïn€™7*%ŽÊ}ÿ#b¬/rYgzað¾n §lÂLîpÒÖ՜Wæ#"aA.sgÀ}Xmô8S˜Ì¢ô-‚a`²Qã‰zmËÔÄØˆ”Ÿ,¹aÿÎÝl³hÏo÷…ÏýÞ€Y‘² ™?]Ų”’Ɉs–RãÀ>÷\‡<×a"z‹ˆwý-€u…Ÿ›¹%†*wzõwŒ^a?LX}Ý ÂÓŒ|mËÔD8HôUd8Æ–ñZ²HUJ%(ÏHÖ¾ ¸,Åý+r¿Ý³š;Ré*:úÕ»¬A0ÑÓ9Ñw8šðg­–/€ ß•aüî@'%j¬ã¡e*=ˆì¸`G)(¢¤g@<[€™©^-ošÞ4yƒHX €Ë‘{°"ÂüRk•›”ÖÍf h •¶Úoù âa=ÐÆ¬˜p<(9ˆÐìôVüN´¼½C$¼>ÈÀúAóK­WP”7{®ìÙ©ñ‚PƒÙ>f@`ààñ³€ÊMÿûq¯ ëhœ¨ÉŒFÓ§Wˆ›7@F‹p9Q«Õÿår«ãœû`¸Žƒ‰FÝ@ƒHÁÄ÷”黽ÀI>-a½`Àx@¶\iEï?‹B´É æxC3)hffž ¯²°ÔëõDx/qÜÕO™aŒ¡­£#ÔÛg&„z@ ¡ï6$Ÿl¶üe‘² ëE³Ùj3øŸOÇÿA¢Û Ðíèõô‚^@~/ôÜry‹HY €Ë"úE"ª$“{£ZX[›¿…€1Æ6œL›D ˜ ÌøC‘° ë¾®¾^ìT«°8%ˆ¢Àh´:8Š1^õª›•OЄŏäi4j{þÛÕ/ýAîqÑè¨Í`Ž¢¸E0;Š =.RaøÛt…Š{¡Ö?ã¶„ZCke×­ÜVAö3³aÜ;³iâGEÄÛˆ.*õzí>"zƒ¡ öV|“d#Æ0öîØÜVc1ž:zŽc EX\nÊ9acÖ·F)Ó²œA0&°+™6÷Þº¡ÖxèÀ±¤ô/§Œâ6çÌ0 &¢£®9uf¡'’–À¥¨ü¯!¢çbô¤(»ÉÌÇEÊ‚ l VùgØD¹M6¿ÉÃ6¿)Ô&3Ý4 `'nv‘*€‹ ãÿÁ‰•wÞ Áé3,µ:™ÎXégün['‰íæ9ê±¥fKä,ÂFñŸüHüDkƒ{nÞcòÙý€Ý8/Çï„”ŠpÉyÿÕšð eú÷¤ã¯Gî–Û‰A™¼Í ®I^óc"iA6ÌÇÑæ!&,h ÃÖËOsLâø] syOѧQ €‹‡l\4矰í-㤘4tŽïæ>0«Üw2ÅaÃðÛíãzŠ(iq¶‚3O}ëÜ@×?Ù È™Ùqï` `ÀPˆ¤AØhZ-ÿ¤]²˜À™Ô>.úEù,¬‘ñkö{vŒº €K‚P›u­³ÓýRg½\r‘M~É^ÛAhh1¢åØ €€‡DÒ‚ Œßð¬¼Æ_½ßÏ ˆqT å/€K†©‰Æìþ?§ƒ.0dè÷ý挹¬ ŸO¦ÚÈÀÃ"mAF„/ä˃ÖÇÌsb $"`äÙ»}óswLO¢Rò˜­i£ «Äi€‘ÌýŸ&²²áhºÃ#€ #O\äâ—DÊbŒ<žã¼¹Ó íHÌÜÁ@a÷üeà¨tb3£Z.¡V.ÁóÛÓuá¹.Jž m8 ‘–šÍGEÚ‚ ŒˆpQÓ8œŸ;â逌•›p—¢ýÊøXC¢kŒä¬µE¥ÔëXàd6Fâã§í#ŠuØ2ÙHn¶¦Bò‘…fÆxŽ¡HZ„2NÇ;==P»ríOèÜ@æ!ð³~_„-€‘äÙ7]ýŽ^œTŠl¢?ì®þ`cw)œï‡½‚, €A%æm8u¤ìU_þÓjl,8éAŠÀŠè"f1FYœ@Ñ0Ÿ(ØŸüD#|RÉ–ÙщâO†²$²adV>¢¹ 0Ü t¡Î¿ô\l0dK@)‚£ŽCpG)RŠž¹irü6‘öÚ![kæýï¿À46lÂF¯aÿÃŒ^¨±Êº™"qAFÂ\ªÆyx›Ól$ P÷ŸËˆfθ§¹éÇÜ,—À¨q‡½BsWxßÁ°­€ãl~m¡ahm_çÕÕÍ~SÄ-¨ÐòýáJEûŒBc4eÏEɵ‡ë8É‘ŽJÏ`7nš¦H\"#Åübó¡Ó ²æî ßàœá%О“Ð&à["qAFŒG"Gh eÏE­TJW?fÔ+¥$0pr¡9ÿCƒ±%ðv\`ßA"kJ¨Ía¨ã¾Õ…ëµÏ’íûC Õ²‡Z¥Äµ²¥èÛ"qAF ƃ+í`:JÁs¸ŽBÍ®géˆs.(¥º2ð¶DF.äË`©éŒ0{¶m†RD¼ÿ‘'q ‚0bÀ·“Ålµë^¶ó_aÝÃðLgóäø¿:³°ô"t‰ŒÊÅ¿/šþ—éˆ|f€ç:ñ´ŒÜADQfÇ/½M„-ÂÈAøÎš/«Ì¨”\TJnœ0Höe~·W.×Dèbl8{·OïJËø²‚þ JúPt¤F@Ñ0`ßÿ‡Ÿü®H\„ä0À½¹0¿ «U0V«¤§`Çý–K.B&ˆtDÔ‚ Œ0¼b³lß‚K_-y}keÑŸ"¢È8`Æ ‚ëFw(ÞœB¦?㎉ã&·eÀ™%z¼]¤-Âó}×z£\r19Vð7YÛB£A ,5Û‰Qà8N´A:Ì“­)ÄX? ófÈ–µœ«À#€câ#ŸP-—†ë—Š´Aaÿÿ; \×_ÈÏp¡\òÀ%=ƒÁLp4AaÙÅŒlŠZÒ(öÌ…fb¬Û7OL‚¹„쨿xÐyØñØLŽºf#~7ÄüR&õúíÍVKrAE௕›¦5fŽõ¶]ɪe/·Þ!·Ê¥Š>[þÛEg_)…‰j ¾ï‹Ì/©¸` ªI(U†R˜¤ @œ¹'†íël Ød4!˜Å˜ØþÌÁd­c¼E„.Â(Òj·?ण¹Ž‚ë(8ÁuTœÁWÐæÙçñ@¨M¤ü“ògÛHã–éF$°¡L¨¬ZàŽ]ÔÃÃ4 ‡@æ•ç‹ÈAUÕò­0“u‚rn=Ip¢1ÂéG 'ç—ADØ:9êxä°ÈZ"ÏNœÐºªk’hp…l!+–“ŸÜgo‘ ‚0ª”K7VmAXoô.×ä<ÿ!NN¼î)"œZl%s^j¤ì—üÕ2<׳Tˆ°áÔk•geçY”À ±Üî%¡|Êì}õBƒá&ÎqQË/ÂÈó\½ˆ@Šúÿ“üV£Ø‰ú×NA €u§äy·Ç–­àÁQ„åv1zy…Î}>ziPÆ AUölŸ~mV¯vϾ鷱Üj§Ë „rÉ¡Rra C땼{­©JrÄ öÆ&©›©O¦•œ7Æ ŒÊùÿ6̯5£ê|Flt—Tʦ§ÉëKË-‘º £È R=5R=Ÿ,ïûÀd½Šùæ¹Êú¬õ@,·¥1ªà)B„ BÔî7íég :öî³_¶·‡1&w‰ ‚ Œ: ~30·Ð´+%N;ªG&¶âª(Ï\Ö×AµëbÚK@`ãø&™J%‚z.ƒó‰|ÑC¥n€J¦!øÜ{þ%ÏÅXµŒn/Ä™¥4B`XrÄØx2¿Õÿ'vµÈY„f¬ÑØsj±éV©;Jå–±N7Äd½ºâZh¢¹'Y¯Ÿ¸J¡K¢ôÅ=þÀ/¤újÂöƒaPá}`‚ Œ,ÏQdGžÇ¿3Óû†~‰™Ñ B,·»Ø>5Ž^ sï÷BS‹M‘®£G¯¶¬Å (RhvzPDùÙÖÚ˜Þÿʱaf ÂèÀÌû6 Ñ¢'þÍLŽ¡P˜ ˆ0ßô¡H咃СÖtj8Jõõ ÄØ<Ç©€Ö½ @³Ó…Cdg[sƇ§´ÉOúꀎŒý}¹ìAE pµC…F> qÿB®µiÖß¾i"2"ⵯnEQA €Q²{ߟUæñåºèwVˆ è†E„JiÈp Ø;»,¢ad!`÷@…¸/”D rP%ÙýÒéO €‘憽;Ê~€™™â>@Œ^ñ×9gáÆ­ç(¸…éVÌ fk8t»ZnAF×¶ÅëVÉslHý¥Ï}þÉGcQþb\2¼)¾XÏ,7mˆóê/^GÊž;ÐV`ünÙÅ$ ‚ Œp€ëñB¶móÆj•H™sg—¢¡>@šá&„̧àâN{ჴ¶]þPèó7ð2Ïõ¾¦s¶ù%¢4dÓX„‘c¼VžŽ§ù%%€ÈÏÿ‰‡¦¥ë?í–¾’ ÀFs]VÕ'#iøEÍ«.vÕU«µ™vÛ?%âaT¸jëTC‘uhBÍ£Ì}h“¬®r.øßbæ´Ú@xÚH¼å  ASRîù艒v\è?‹"}AF…gßtµÃÑÞ'sÞíOû³Ùÿ}KÚÓpŒ$ €ää²€ÀD½œ4°`føÝ 7ƒ¢ë{X-?ƒAâû™ŽAd£gWø¶œAFB‘8î`–MìùƒÚÄæÁUP«ö€¹-A € ! ÍÙø±Ñ&¹(™ÙöˆOO'@Q±cpz±‰@›þ·#‹™‰sSƒ\-Òa„Oüœ!Êœœ\jÁS*çqÔ@öôŸ¤ ¢ùĪ-X¨žkÃýÚl«Y¿pc8ŠðÀ÷Ž ä9‹3°9o;ŽÊ¾w‹H_„bgªýŒáLo‚6iŸ¢üÔ“ c4 ¯>!0n7,ˆ°‘œY¥ KÞ_± Ó-+×TP AF ÞGÀR³n/Lœ˜3Ë~:`ÀòÊC[]$@z\8²‘²Dv0vöàኟ¢³£¢9ñSX¥Õ‚ M«Ý»;5Aˆ a¢1¿±’¦ŒÂ6`h˜ä8—º÷üãòBG)%`ÿ®é‘¾D6г«ÿ耀aIç LŠèAÀï¾aCÿqkß•<ôì&í—b#J)TËyUUöl)a'Ð3¤$Z €õ‡™ÏæøðJÊ›Wó÷’ñ™XÁ6TAF…ɱ±Ö{<×eE (•nNv«3é“2dEdf¸ž%SÛ¦A¶Þß¶VOéDÛ eב€ÃôDãLlé6ÛmZÁûç怲!2Í€R˜Í@-_,ŸUÀQ‘¾ £€6æ:Ý%×)Âÿà2ÿDSó”¢ê©Ø ˆçønÎ/Š'2 €8Èí°K2ý^ÜÈDÞýR³¼ë(…’«à8Žý,òí3K®›K̼ùÝE(Â@Doõ­RC“þúRÀó\˜§W¸E΀‚a>›xè™þþEýØÉùìÍ’ÜJ)x®B)ÚÏb6IF¬1„Z¥œÌÇæÜÄ,ü¥H_„f¼Þ¸ Œ©‚A¬o«t¥ày.´1çì0à]‰ˆ°aœÉöÍÞ…b€Ý Ã0#mç,×MoRÈÕÄ’"ÃllLìë"zA6ÞýG€S\ÿ­¤#àð¼%f†£š~Z¹ï=ªüün(À e€ÀÁÙ9¶"lЊ÷Mú à “ìÿSNóS®>Ä@ðÔñS_é ‚0ÀÀ….^Ó èÌ^Ü 8ëé+"aMŒœãDƒ'ªhmd @"WÀ}Fm48{·O×ÎÎù"AFm :½^NÑjrFD(—\0A˜sïû€v7ìË ŠÔ¢H_"3Ÿ5ÆDžzòâ O¦Íý£v~ƒÿ`!”­àäœÝ#’a£±uÿv+ÓíãÇ~¼f=mb〢¶ÀÑãŠ?v÷ã';iÕØÝ×r$°aTëå‡Ï-<_k“\èj)× §ÕsÙüj¸â/¾–î(fþÜ5»¶]ÿÄÑß—3 ÂF±{Û¦2Àn£o·»èö‚ÄÛ¯”¬Š)Çå} ´ºAT&˜­v~§‡z¥”¤çGm«ˆpz©•«.ÐÆˆ €ƒ߯€}­üg¬•›ü޲›YñžWôÅû`*M ˆ†hp  š½à›×^½c›œA6L(ºC)å)ïÛgœ£²—¬é¾þð¿U\C9z @òýøïEK&—³ € Ãsœï8ÊIXÚø «ÙÑî6û.èb8€´ÚAæâgœÝæ/ynd  ÃŒ«wnnà È` A6f~[¾Ê)Óâ·ÐÌl°wô´þÍìÓ“r$°q$z4ÎtåL¯Ë¸e£0 ƒ2Iÿ0½ HA‘J û‘¹yz¾ÉgZ7ßxÍ®÷ÊYa#ÐÆ¼,›¹_-{vx£ÐjwÑêô’Ž~q®€mfP.¹Éç³éýùu1;"ûª65j`ãBÝÞ½m“ÞÕ4@uP…Åâ•Ôô-Týõ'¡„è!ˆÆX¢ÕîѲßa0Þ}ý¾59‚ ¬'×íÛñŠâk®c›šØÙ4é©ql™šÀÌÔ8f¦Æ1=55M£\t Qêœ<ÀãÏqíÎiɸd à!"°L&«¿xgI/æóˆ}QqzVÎ^ØÍàíÈÙaù±ì“P›(±Ïfë3ús£¸¸/@@¨uRØ 5œ¤ƒ`´êa¡Õ3cª^Í.¾ø»oOÊ¡%°±0¥Ý¥ T|ÑçSû)šg³~9»W–~'ûW2§q•Ä êurAX/nØ·Ë#гâNƒÛ¯Ûƒ;o¸›'Ç1³ybh¿a†6š ŒfÀÄbJ:»¶Ù µF 5<ÏI‡°gÛtYΆD60`{Çï¦FmnFpÑÓçØx-´ àÜ,¬‹Ïœþ 58pð¿+'C„õ õ)ìTD¤£úmŒÍeb3%3Š„¡ý|ìqf™$Ø\‚¥v½PœY[ïð9#blíV'RÌ+S¢Ñ•}³& ­®ÄŒÒOÛ&ò‹åL‚°^Ü~íîëIѸ"‚çZ5¢ ã}ãuNk“æDqÿ”@Em ˆË.–ÚÝ$ppæ”"D¯@ € ¬Xךªñ-ãõè³ÜWØîý–2§ ~¹ÝÇ—~¡‡öSÇO@©Z­NµÛíy9#‚ \lx'C?2ƒ|è…an}3†“š~ÀV hc’ÐÿÀPh¤üç–Z0ÌØ:ш—»äl<}$àѤ ì~38²d¥¢ƒ P*~Íö Vö€JöòÓ}0[ÈIU=QЬ X)P”O@DPJI{`AÖ…8päÇ[ín__<óš=¸ýº½ˆ»£šlòR6‰I¬ìÖ§ëºp\×–@Û%JÙýÿø³¤hPÇÔ­WoŸ‘*(16ÃìfÒÆ&þæÕ}qظÀÌëÃÞòÞÛål‚p±¹a߮ۑK6Æ × Ðë0‘Â×:í‹Â™fÈúÈ 8Šà¨•wü³ëc<&8Ô¦qôìbIÎÊÓC¶.œ€!@æ¡Å}qÌ8366ŠAiÞÔÀ&þ!©’‰B]éßöÜ>» xYKÎ… Ÿ÷Äê8ÞËOBüÑ/¥gÏÂs¼îVÔ7%ˆ’hõIuºFR¿± ˆpvÙÇôXƒEgBm¤P"F€Ž÷¥jåRæbåä?¹°ŒÙ³K6Œ)»fØ Z*¡^.ƒ‰`¢{(Ž|3Æ%"°"L¡TÆ"0crjbü­r:A¸ˆÞÀ›ûs"tÃ0°G/@¶C Š·*ãlf Ú>µñd›¤1ƤGlÃ<»Ð¤S‹þW9 zrf$°!fŒ@YgÆ]È„´¬ÕšØ¸ÑÞ}êäŠæÃãW¸085ˆ(o]Û/þ€ËYáâÀ¨’së£õíñCiS>• ég—¯({?·ÍIX)ìOýùψæ¤}ßoû?)çD € cÇôÄ23'S|Nž]FwѼrAS) ­Üò2¹ˆ†m30€É©‰ñ{æ—î“3#šzÿûw쇡·6l S«ÓMJÿ²3NÀ™úþ>w†lÂt¦IP<)uµÀÄLOñ½rVÄØØ€á&€^ª£9—¨ZpÞAQ‹k6Œ¹ÅV®ÑÅôøàdV¥:Ý–ÝÈlŒLŒ¸SÀ@„µõý÷€0І³§rûù®ãä9 ñ侹¿Zð‹ ¯Á=­VÀrV. ɸ@îäÉ€™ÛÙž×iBL~z•0;¿ŒÛËd IDATóË8¹ØLªìþŒôEÎyBš ‚°¦liŒ9:0?F5ýJÙlÿ04è/i²J»V­ ^-#СֵAj°a¨Ì˜è'·˜E ƒ”RÉÁ¬Žô"ßå/€Ás‡ìÇqà9 Ì ×s1lo‹™¶gØ*ZÐA³‰mجÿ-ðB9#‚ ¬%±Ê{Â@«ìR£µ-õs3¹HYçgj¼¥óË~)ˆÇš÷/\”¬mçðqîjµüŽœ‰Œ_JŠù G7Æ*¿É¶†– ZË7¿¯f¢™Ú¶õ¯}-Ô&„öhÔk¯”S"ÂZÁÌw'Sþâfœ–4yùöÐZãôüNŸ]‚"ZýzXôPÏMŽ%ß÷ËÙ`Ôn/Ľúvl™Àm×îÂx£Rõ—+iál‹?²ÊÿDT*¨¢ºØØà¨ÄpûÔ˜M°‰~Lf Á˜è°7é{䜂°fŠB9u¥(ÅQ—¾Ô±N9©nŠüû:D}>žs:œ\A_H«s1F¯ýâWнõTï÷ñ‡ÏHúcSÿMQœ°rLÏ«793‚ ¬•ß&,5},,µ0¿ØÄÙ…å\¿;Ü7Uÿ<8Ê8=Ž£à¹ù-QöLŽÝ"§B €‘ãÀÑS>qìæ›çהг˜¨Uú*€Õ¶æø¿Uól9+‚ ¬%«¤SmoS‚€0Ô6Á/£ÄûMûÃÜ1µ¯«à`~[NÅÚ!I€k„£è~7(Ê\Èq Ë 1¿ñëQO`w¸ƒÙÀpšG`¢F¶™Pv[!ŸyMИ"õK¾,gF„ áê[ËnŒŸ7;]tƒ¸]/¡ÖsÓ®§ýZ¸çC«ñŒúxñäøØôÂÒòœœ1F‰¼ýÌb §šPD¨xV¼žëä®ç’çÂu”µ†™¡CµêÕ2êU/¹;ÂÌ,m»çÆ™'_s{r±™XÚÆ˜×ÖkµÝ-ß?,§F„§ ³¹[Õ€£90vè¯R±Î¬ÕÓ‰ÁŒrÉEÉó’O·:ÝÕ' Ú è)bˆ0R<€E©üÉÍÓŸôÂIŽ@:AP1õ½ 0=Ë~–r­3iÁÌÑÿÄð95‚ <]N/û¿ÀÁ ïžÏåêGïfÚùÖ«eLÔ«É{ínÍçÑhAΊ#…6æ)Øñ}7J„¹›@E;;õO)|“ÌÀÀ;®88_e¹ßÀskÕêŒßnŸ’3$ÂùÒ¨Uo‹œ´Ïè³/õB»Ié¹ù*€øQ½â¡x®ÏuQòòKäD£ ÃŒ…åBþì  —›²ž­’¸FÑ"2qL6&}L‰zN>)sÊæÄ¥”™÷b µˆ™J©Ÿ³#ÂÓÓô‘¨v™rãø’uËvùë…:7å’ ~ÁsTK%Œ×ªð77°^+c¢X´D£\òP)gŽ’÷÷rRÄ9Nœ^о°†D¡|¦`gZ‰ûdÿ–1æCõ­5’3$ÂyyÿÚUnˆ±â°Êû%Ù9åQ“3Š·5©ð-ÎW×¶¸ Š™1Q¯ Q-ÿO93bŒ*ÿiÈ­‘Õð€V§“LÒJÚ2°ÜêàÄ©Et»A¿‚7ŒmÓã¨V<wý™Û¢fAÌy#Mü†œAΓ÷Öÿ­Ê0qÜŸWÐ4Q#3u Œyʉºþ9Q‚t²°%ýÔÊ®‹rÙûS9-k‡ä¬![ÆÆþ|¾ÕB¨uV3#Éé#kØJìk '› ÇÎÓVmdªl9`<>89àÁ Æ[ëõÚ[-?³$Â*¼ÿi?/ C-N=ýv7D£êe]÷~o(0ÅÍÓs7†±kf2Iž.gªzAxú»O•ý1F“‡Ÿ:Âûv̆€ÈŒ‚f@ñ$ÒEŽ8O²â@.ìÚqgÂR2„Ȭì ×ëÇŽK…ÍÐG f`®Ó4Gë›}y%ýާÜvãMnbæ›ç;í—æÃá‰3«ËËgÖ–AXfÆ "¬ ÂÜæ0^J ç ´À€=/X”Rî¹éÈÞù†ì0Ÿ$Ò‘`À©Ñ8úÔʹõßÛìõϺÕs)½ÿâH{f+~A)›ƦÔQ-BŽÜSú\*©ö/Ìa¾ÓVŽ Ë:BàÁ“§ ΃€ƒŸzà¸kÜar5—€˜yà ñ=›HMÿ A’a€eL¢ãñ$ÁPVY^ù'$Q!…À|ß+uÙĉ4†r+Ú¸¶ïðkŽK—PÁw;mO襞àFבŸóBX%Vž'°´8iGŠúú‘$ ç㥧P3ð"ƒ˜.ZújµJmS" 4²®æüS¾ïÁ÷=¾GBˆC%°)By*ºYŠIç ˆ²:¤<ŽL…‚Šef±düÒü\ø·|ïúfï·Ê.M^Â,fUl½&E†R. ŒUÍO¹+–JKFfΉ¹Žˆ€ûŸV†>”Ç™yÝñÇW}ö‘eù´'Üüç?ÄXœqôàþÊc£1¤eh@¯ýÑ$ÆÇÏ€ˆ0¬@¼éð:­†%dW×ûXï P"¨?Cú¹0ì¾¶×ë'ŽS;Onø… ~‹$ <‘74)#Ï·Ü*–ªÑ*:OLåÜeæ®âpýÁ}˜Ä1Ö6z…‰ÈæFäm¥†`-¹peíœE’|¥ø)+ØRßE‰jQ„†J]ezž8¿ÙÃò¹5ÄqqY忀éžï*«¹Œ™É,1OÔwª~53ra.<²¾ÙÛt«m'½ÿ΀ïÛÚ»¹ø°1°v­õÅ †“HwäiOm€Ü`2vlrÀD¿¦ßXZBýá ÊŠ/ˆÌv'ˆâ¯Z¡=µ´ ÕôAã(‹ ¤é /æØ·ÐÅþŬ¬b³?Îd+)€êøàX´ÃÊ¿Ó9$ï(TèhSú4´\¬vNžŒÖ§ì_ßHXµO¢:Êc"REñ¬TåÛÆ!72üv¢B êÌ$̘詗©÷–¸"ÐʃҪ” Ä*"ÄQ¡§ÀŠÈHÝS ™ù¥~Ç­¸¥—AÁþbëŒ}]©ñÖÖaem£ÉÄúÔ$J0œ(ãw®ÓB8N×-ø»~ÀŽEθbèc÷>”<í 7ÿ€—,§/‡.¾žŠb¢’`4·é"ÁT$œýYJ6}å÷ð»Öëõßâ8µ£t'hÎäeÚ:“xdÆW=ãI˜ÄŸ:Sòä唸¿'@„X&e8öíô‹êóœÝè—>4ßmb1èd×Zú]¤ÖfõÐ0²Ú«cp•§oÑd¾Ö•­D%CÌx¦3vŽæºa'Aò+dÁïÐŒŠ½Ð® •Þ"pM*2-l6¿]™ AÛ3ßA06gÿ¹ã’3®8š$ñGÁxY”$ˆ" g!`Ò^Pä žgµÁ¤ùþ8–ùÒYÃlSÌpE†Ýçõzýw;N]p^4%¥•µê5ReI¤r£DPèÆ—¥ŠÞ]Â[-…ü‹éí[ciå°öÖ«V鎻§[J†ŒÕ5 òߣÿk>Àª8Õ>«ú-k›°d4<ffʲ˂íF€(–˜Ä1´ÜŠÛ »]ÁwW›Š´-I“r]J‰‡OœÉŒ=†šäG”¬m pxïZÍ l,á¦#{E1<³¾Üï9N9àŠ#b)øE"Ú×_­}Bÿ€¥‘¯ÔsJ‚ª Êe§”Œ$‘Yø·`n«pkyܦùô®n·³¯ßœsܺH^ö2O“šJ YœË¢3‰”xúí7#Ir%?á ÛÂ8ŠñÀòŠ‘‚ÀÇ¡½‹’±¶±Q Í™RM¯$C`«aÿˆþhd¥Lïm½7Äzoˆ[®?P2šÍF€áh‚ÑdRøåŒÀºÐ/.+"lôG`)±!4¢ ©w(Ð |0GE "rE€;GßàN­ýíP£ à?ÕŸÈÓUiSºîâ8Q&`!m%j¾–Á B]E2˱ÈW¨VPÿœßìミº_ú´;”Çg(åv«‰Ûo¹ç7zX>uÖêh6|<áÆØèñÈò*¬¹Û <¼²Š†ïáÖ£ûsa#½þÀ"ˆ÷?“ Ý|‡µsïît:O ®(ð9̉Ȟ ™Î‰°æ<ÔzæeA9íœDÕ CUi(@ƒImn>W¹RÜxË-£¦i§Zùô“neíˆ÷€?*‚…?¦¶žØ|zÑ?‘ž†ºÙá®Ük¤ò§ Á&@DRJùÇ)g\™Âßï€ñd’‡| v2‰”'(Q°RJ]h _¡Ùð!Y‚ãÔ["°iÑ_A¸WnóÛ¼À·;Ž] ñ¿#-¤ÒuûºYQ§/ü,CÙõÇckD‰A P…9½à*÷îuÛ-´Û lô†YQÞ–W^ªAáJÇoƒÂó<Qè ×%ªÅÂÅJ5@”I¥µ+X=¤Ã„¿xå0ø­J#ïU–`*€ìç”óp¾Û¶€ÌâDf­}r‹žÕ8–YÙaI>1³ :5Ž“Ž[θ"i<Šî3…ìÇN‚ÏÎ Ç6û£J_,Í©â«(–¥¸z¾Ÿ ̰ÛD[OÚšUsxY§ÓùëÁ`ð—ŽkF½Þà\·þ*À¯4\•ËÖY—„%ù¯¥ž|PZà™·á¥•þi}“ÂO·²ÿz¢šªf _±•®€ÞF£7³4Jl.´"‚0ŒíZ}hxh4¶%"ž°‚Ì’Aàa<‰AºÖ`}ï OµâuçÎ÷»w‘æ*øè6+À²Í:û(NðeO¿Qœšq’ÌTôJD8³¶‰ùnKG¬Èè~bŒ†x'’ص{:à ÝdLŸ B@WÍ2wZ¡²Ÿ¨ÊK/yjUÕ×Dùd 8‘˜L¢ÌccÞrW§ô θê÷{ÿe> _D„'¦LŽ“Ì"»ÑiŸ<$‡ËexÅjyA„ŽŸA«©ÀŸ¶p(Eä3 >˜³˜flMDè4ý’Ø¥DM¹D¥ñÀÌh5ë5!T7@3ð3¥ÀöüWœ>¿Éi‰ÌDE»@À+™ÙáÁï ½¾dá±(<—ydFA8,CÊáÇMƒArÅ`Â`¤ CÂ6$3æ»-Œ£›ƒaÉ™‰£Dô®]qág\ñô3Þ@¤Æ+¡)¬Êï¬wŸUÖVflŽ2UÝ_ ¦r”~è¿ø6Dz §^?ðän§ó3 þ7›oœ$ð„`faHÉ|’Ã'îžðÒ hXë2æxžÀ8R„N³añ<’ ¬a[íf€†ï—"HV F4΂(‘ø‚ÛoB"%άod‘ÉD&˜[DØè-£´ ‘"72(n·'®ÐsÈÞzü ‡çÁ|ìà¾ù…tŒ&ùpËg׳IZĽÂþmè<ëêf_M$¬Ã+øÊ }¿øôêù¿vœÛ]ºõèÁ—ѯt$IœÞèÔu.q‡÷-(ÅêQV¨ÅG?ŽTˆ=ðý Hꋞx+ÆQ"`Eð<æ!òPÆbH†õÉÚ³z¥Z¦>º-³ÛD¥&߇ï8¼´€½ó¡^VIêÙ"!è­¯ÿÛúzÇñ]SþKNJÈU˜Šy£f½÷Ÿ'9[g)ʼnÄ×?ïéˆâÇWN[]%)YëAJµ³¨UŸ#Ž’»<±òeŽs—žë¸·‹ëd£ÏуK¯‘ÌÈD~MØjrØjÒÆ`‚,t¯‹%36z#Œ£a§™Åü‡£ 6{£LÀ>鿣¸þÀ^\·oA·h)f«õ,ŸÛ°`…… xž—yü‰”ÊÛA#ÌK ºhAzB@xÝVí¬N!…±ÄÉDòsÿäwÿºãöîQ£Ñx €Û443]˜'˜WI–Â6ÚA€VC=¾ÀfX;ûb0š¨%ÊœTƒ3!%¿zm³÷!ǹKO.°KôÀñ•ß|üõ‡À-B*æv[´Çz”W™;3´ëâ³LÇ$YðÏ+½VôôšÀšß-%@o½óö›ü¾‡W÷v>qÿ±Àÿ¹åÈÁ[ü§Ø¾`ø"ÇÇOÿDˆâ$[ƒá$¬¤…uŠ8¨ø­ŒˆšÕž)0Ý Lk¾Ào¢ÝÉ#IJ.•~ôSÌøÂ?ÿû÷—wÕûÿ9Ï/Š‹"Æ[*žËæ1ÀŒ™Ñð¼LIfŒ&HiCWYu’ˆ%gíË£79Î9àj¤ÿàÿaÛóÙPœ1SKƒñÃI„†‰É½ÂǾŹ „F“ “Âáüû'<îÈSïý܉رnwéÁ§~ü–ë< ¦¯µ›~…bfllö3á-UVª–»üußó2o=-,Í MÎ ‡|íUfõýV‚7E­ÌfY½“/þ³w¾ßtßEêv;OeðUÉ›jTG£Ö +,…m˜Ý}ÒüÏ‚!ÄVíéh<ÉjH„Þp‚Ñ$€þf¯ÒqÏWÝ|ù/ýáwüåMWpù –8 ÂÙó=KQW™iK¡d»G|º—WÚ¾ÄJêÞá¥Åo¹÷s'~ßqo÷‰|ñM­À;X”v÷犟@ /›•›z‰” H™ ‘R|µ>¤j#å4Ÿ«§ûéå$áÈÞyD±ÄÙµ^ÍÕI€“¬û …Ÿ™$2FÅDD|û>𕎛»®ü÷xŒ4oQéÓ´""äÑ!f†ïçiC)¹ 4‰¾§á«Uz3]Kym“Z³2–Ç±Õ ÝFéùöÿ.’p·`wißbøfœ-︺@-ÖY÷xÏX'8‰®Püq"1Žâ|DlÚ¬­F€fPÃ÷Ñi4~ïk¿â®Vä1 Ž­ôöÌw~¾Õð©Õð™·ô³rР(N0ŽbDq‚H•NvR0"¸G{rÆëBÔŒRæãª‹f«Â0`N&kƒñøÅŽ“»®ü=oÐÁ¶~·tä+WÝHý[Vï?~Ÿx`ýáÃñƒÉýÉ8[/F**…Jý]ÇA¸jéýŸ¼?þ’;oû·BÐß”¶ë;äU΄  0çQ¤*·#ü_ß긷ûôÞÝÿªÃ¾ÀMj&Êvc%ŸË³ ÜûèrÖò%™qôÀb½jù÷\•#V±Þ„U8"ÈDGòëIâ¾ï¯ÿéc§w~À3Œ@Q¥"ç*$küËÛ”U—éNþȉ ‘Ël¥P¨•‚)Éq"tª6XƒJ~º?¼Ï±o÷ȵ>Fôì§ÝÞð.€ïd¦€@´r~ÓåÊV{õÃ'Îå-63ä݈ªqR Âу{¬ãÀpã™o~÷‡\î.Ò_¼ãCɳžzë/Ñ«·ûY¡ÂIÚÃ*§ìÒBåÙ™m£±”ºŽ ÉÖ“4Û !1‰’ì;$³Yì‰8Îí¦çßÝÆ=€«ý;ì¶ü¬X¸²8¸&))ˆ°|¾‡C ¡BýÛè—e†þ?Q§k´¾q¶ÿã¸è €k™¾x'¾¤Ý ÐjY xÙ¶Ü3×ÁÞ…9+VöžÛèáø©UC1‘LÔf–Àh4!¡ªÈßwóáý/xhùŒв‹t÷¿<ð¿ï_xµÙ¸•h- f|Å*ØBŽÄ–™JÖ ?œ`MjÏ®– ÒqmWé­s¯ígkuLWþ\kP =©Ò”)ûíÌ ËBþùÒ‹O[ÿMÇBg\“tã¡ýÏÚ‰ï `¹é IDATafÌuÛY`"cxBd½øè¶›éÁð|ia£/0ÑH‚醎“$S cýž&zçÍ×xöC'O¿×qo‰é>0Ýž+l“4”8êÜ–Èc,Èí _@20‰íñÁ“(@H‰I$õhÞ4ÏŸ”æaN±À¦cØîP·Ûù]_¬‚þ•.yýójAR.NN®m–FAW؉S DÂæ §¸ßã¸è €k™ž¸sJ"âãqTòòl%À•’а…‚pR8ï¼ùº_úÐÉÓ.°[Dø€Û`Íó£úP®á fí&[9ÅãŒÍªÂ›±¾©æ´'’1Žb¬÷Gª<€ó©ƒY¤€K‹Å»§ü¿…@ß ›æH˜)ä$Rþâ$ÙöiÓ(1g‰ðFÇIg\»rhåM/mǪšÞ—$ )ºQñ~#ð5Š'#ÒÖF—ŒDUßVRzN®ö!šBÐo>tàù­œþÇÅ]‰|¢$m5¬o"eQJ[O•»ïênôF¨ ïª5—w¤Œö=DëË“åZÁpí—^ù5€×¡ìâ½íù )ç3ˆZ7vß<®7ŒÒ¿S“âí½þ`ÝqÓ×,yBtÌçRr¦|c)u!•z÷•õ РfSaü¯÷‡0‡º¦8ј¯Â¯þ|Úr(SÄ.¶Û¥” ÐÛ®Û·ô”“gWïsœ¼Ä$äiÐUí3eé_é‘–pg=‹ïüæB° ’ tDBƒCQ&ÕM­’!2Jßò<01 cgÃ.…ÝÎõÞÈØ"_SP529\£ð«ûìÎN¶^Hþ€ã¦3®í0gw9µ¶!„®¨VE7BT@jƒ Òa8fο°ãk˜Vß÷Ð|û}eˆ“ã8Î&ÚÂA5€¥ygÓøgBcE šÍÛ¢ñøQÇÍKG ÏïÈ&ùUQ’H$f`mcP;ŽÀXŒ!ˆ0×i•…„'0×j`Ç˜Ä ˆxš €Î÷zƒ‰ãØ¥Rþígø( ±í‘˜ÑM „À¾°‹bÏPZ"$ˆ°²vɳ8¯ëõŽ£Î¸¦iemó~S« !ÀÌXè6´·Eð=¯ pI¼°Š¤µc§×à ‹a&ýS…~fm§W×qôÀ–B#’À¸nß"Žî_Â'Nᣠ5þO¯+@˜C{æŒ^b%P,„-,à £(þ’cËgÇÑK´Y=/J €:’’'\ByF«Â¿¸–FU/2ßi•Çžh5H–êÜµÙæì“tܺTÊ¿ûd€ß°0]sžê¨sÉP+–ö0lØ@«„·2*¦G*¢ L„˜ÿËqÔ×< Aï1¶üê)lÌÖû @dxî: `ôf£PØcÖ¨ ré! ò!ò/tËC¶‡`$*ñEç62øÒZo8!͆‡­Úù!à™Õ ¡ª×U€jÚĬ«ø'Ç­K¢ü_àÏxVÑîT†NkqÐT›B#—æi¡âáQ’T}ッÁà^ÇUg\ó´Ñë}f> ¸€^Ñ(¸nß<n8´7óÔ3`½3Ï÷‡8óèŠÈ~KZšƒ Óø>I8~vÝ /€˜%ÁDøÝnçýþàWwžâ„GÐÔuûvRˆ‚÷FµJbûÅâ”Áºe3ÿ`÷”X5w;ní¸òÿ:o€‰¾%'i[ïQM­@#ð-ŸVš(¹AYi›«ê;Wà(§¿ðï.êt¿ ¥´FÃ’1ïUµ # IjÃAá¼^@ésr¸jxdff~=„÷w‰+»„4]´o_Ágc¤¥ò³Ú¨ïa[ÌB½^ߥvVù/€×^°õvQN°4×±F™áû>Înö ¥ÌàȧÚôÁ~ß ýq€#“Þà%—lƒkÍ̪f`‹¬¡:¾ ®Š$3-„*8¼€Ï1ãI¬œ9æØº£´¿ìï×1o;kƒ²µADjŸä<Ò_@ÊáåÙRzy¸–®Uþ?àd8UŒ·ðþ¹rßßIS}ÅY"̬aÁõþ ß7¢?¹à´MÀ´ Ç ÆXMu¹g8*ÐÛ œw« Á¡·gK¥_ã?rÓá}_öðòÙ»kẅ;ò¨ŒÏD£Žê Rú> :ß«ÿL †”^Ö ÀL àAǨSþÿD¯T!:¶u+ÊUü¬f?r%‘BxÖWJfì_ìDØèräÐ%†a^•„Û¯Iorܽ_èsäë"íNò„`Oóïözýw;_>ä¹[pyQ³Ñx!ñ8J(Š%†ã£qŒÑDý;G› H©„¿2­ “8†”ª•o1lƒ%´Rƒrxd…}D– ÉR?ˆXJ] Œ£Ä÷1ˆ!ÒùâM䵆¬ãÄ“IÔM$¿\ÿìhÆr÷*¡>²÷xÊQ0ü;k@…°¼¶‰´ÁZlBľy> xÁF¯ç¼‚Ù©W% §s õUÿÕoNý‚é¥ ì8tv»ÏK¤|—¹c-㜊&^=Tï,õ:U%„éÇ„Žô¥i&!ÈžBÀ`¬ ¥ÇQŒÁ8† ‚çÕ…ø‡àh{FÀ_χá×øK ¤» Ó‰8ŸÏ^ÉS<¯öžÉƒ,«™²Ñ!„j ³Bœõ§²åy>3†Ûèõœæ˜6§ñ¢ hZ}ãTá¼Sõ) 2Ÿ g\×?×`æ·K–Ï©Ñ)ÔW¨B>‹¶×]Öÿ45Fd¦‘ :nÏDýjŽÏÐã9õimeÕ…åQ2[žÄQö,Ìýp’$g¥”Ï3ÌešeûdÿQ‘/yA/•¦zÕsnz‚ôCdh‰”ˆb‰D2FQ„ád’=f@üc$@?â8í"Ž.ÜøÉùnø‡ þKÎ^VlÇgÑ›Ÿ:ªK©õ‚·~J}fÒ×™~íu˜ëv—6ûýÈq|ê}ëUÈñ£ØvK¤y UL˜Î=}ªx*Ž«2ÕÑÒâBàÓÌò†íß4.ðØôÊi˲qìp#‘÷;Ï## *­z"}˜:ú/‡%¨:¦O8Ž;ÀÑÅýÞósá~fþqfþ ¯²:yn3“ Zu˜ Gi«w$òBž Í‹¹$¤µç« ŠY`Ò)söfÉx€g:nOcmæs 94+Uå~¬ém\Ë»²1G?ÓÊæ²)B¼œ½þç1Ë7 ÔÉóé\Sæ.¤­µ¤‹l™Š¬Oûûiêv¯ÓÝ\sn"dé€"tp:U4m#•2Ã#€ŽøÚÕó›®íÏŽ.ÚØìÅ~¶Ûíœgà×SÊ”—ï Ë‚ð¨Þñ²àšP㢠%2ð!] X9ýì4‰äiÊ«N-Tg´Ûíw ‡Ã¯tÜ®&)¹Ÿ xJ¸Bçæ € ½Xù (»m,m×LÛûÒó¥a´2ÇUµfWêBtxÿÒwǓק7ÑóDjç³Ü3—1"em¡?žTNÞ“¬öi•‚‰Þ°ús B_÷ñϵ‚’a'áôê:ÀŒý{2' Ž/¯)ƒDäR(s>~cõü¦Ëý_îN…»Wu»ðùS{Æà)á‰K™d¢à¡[BAaˆ«s,v[|/[H§Öûô{ô9R-ò'ƒÁàß;.—©Óé$ÂJ•ž"¼eíY5* €\±{B€ sრiáýSa‰âŒÌŒƒ‹¡Å[!< ÇôFùwÝlŸÌ†3{ g€a…­)fìß»A© q|eU¯5aÅ}T0îÙì >ÏqÞEí41ÿ j €,$UþÛ Ë`*+ÿ¢¢©þ,xÂAMDßÕÆZÌóã¬qÓH=T¨4ÍÀ/=Zß÷àyú!Deñ´G§Êfut…Ã'üÛŽûÀ´jîéþek€,h`{Ú‹ÊÏá{ ßÓ³â¹Ô%¢Ós5ßùn§óyÌ|’€g·>·>šOÝv aG){£ûRJ )uµ¾ÌßH÷¬§§î¥7Ý÷D¾·|oj+nÕ¾³b;ˆÍ\õ°ÖTñær#0ƒôšù7n^¹ä€€®Ú·gþ-zñpodÝw̤ZÁJö¼nVJ!>=!øæÚÍZ#¢© ›L¹Å $3Æã¨\Ÿå<³Ë£ÉAÒÊEúL(ÔÁ—õúýß¿–ø†Ý€aÙHïŽ(¼NS×´’}óÝ “ˆÐj`fDI¬£;1Ò‰BR€P’ç6‡`N²5@ æ+{½á?\|xÒãoø/QÿÌx5ˆ}_ˆÀ’á{ªï†Cû0¶3˜Þ”G’¾'ð÷¼‚ûºz/©¶º8Ž1‰b„ÝV¥W/¥„'†ÃIÉ( "ÄI)%V7‡Hu K% YªâÁRF‰a1¦1YÝ"Ú|ü¹Á ÿÿ9 |å’K\tvmãkî]ü0€/ØJò׆ ð 6z )áÕ »²dOès°¼DÄ Xra†qn˜¯ç³<Õ:• &¢×ÞphÏ›­¬w6üÖŸl¼«9 ˆXæÊÀlTB)¤€´§ŸÈ ñ-UžlNž é„ €¯¶;}Ó¡½]ß~ßøËX¶~E'ˆ¢8›{a¶Ç13Ã6ßW°½é>a`'Vä­næŽ0"mUȾçe@Ìl÷riHæÛ”¿VUb•P„k°ËÃŽ.Kjøâ«ûÀ2®‘´ºQÂÿÀ_ClŽíxÍì”B¸\˜³ä©’N§¹å0™mX$“^oððÕtãÓu‹­ ¸Àa1«ømäÞ}ÏÃßÞ­À-¯[ZÐ _½×ŽÖFU:uo ^A8µ¶i× e$Ib]AjÃä"kuÀCƒ~ÿ~'y¯lr5W ?µz†™oÐ`Wí•\­Êê±aed²m̧­O¿ec[^?"ÕüÐáý‹_s­ð¸×ëÇP}ÖT}÷*Ð|p>l¦8GÞ†œ¥ºuc$‚ÍuP¯yÛÕtßo¿ùÈKÑ*N7sá–Àè!ÀóüÂ#ð=ĉDœ¨0~¥K…v>TòÐ0~³b].¦ç §?›ê©™Ê0 ½ [·ÜPÂVοxE|“º.àè2¢þ`ð`†/ðžZåo‘©³ASíÀ²ö™ÛŒ©¾A­Î„  !ØløUïï_|Âò™ó×JWÀøÑr¯V1¸KV=?°fèié_6¹]{¢\ð uƒ_uó 1@H1Õô%ä\ 7úŽ[Ž ¿ODÿ!­°¥ŠÎ˸%‚ãƒ÷<ˆQ”Øc˜$ÚpjúؘÜ9-’@DÇÙ±¾ðpjmSÁó Ê €Óë‹e~Fsô³5ë#18Íê–•è’ oìõûnÒŸ3]~^bï½a¾™ÿ&Ó5QSøDqO$ƒ ÕaI3\Y4L%}ÁíHÏé] ŒD2|æ†Ã{íºý‹¿Æ>O9µºqçh]Æ ¼À?,Ÿ=?º Xüze\èíÝ™ºßZ¸Y½ÞàíWúM^;×Ñ<SR.\´”ÇRŽÀlMn6Fjc¶Bé^k¾Þ¯úL9Kµ7ëÓô½VÖýé=ø¶^¿ÿF'iàèò5Þ¶ïžðËuQ¢¼Ÿ?IÏûüÇçð¯DºeIÍObœï aNô )1Œáû ªªôî3‡Õö^}_A›…”YìtË‘½Ð©ÒÌÿM2þÙÐrjí[ügßàž•+ÖàOCÕø[ßéê·È(ø,*(¶ªÏ¸ÂØcŒ¢±ÌÌWM?Ñ¡}‹mO“’ÿM.Mð³§÷ O Õ ¬=AQŒXÊ2;b‰­bgD„ñ8ªÜ³Ì€äDíO³ ³24¡¨øXì´²½µ9c8‰¬H€ïyÖïJ¤,@D1xy¿?x½“°W¹€«”Μ[{¤ünSvg÷`–FB?Õ“„8‘HâI’ Ñ-`RªÞeÉE¯‚3¥Pj” ¢RiÓév"¶r˜êC”M¬õr @ ÈÄ’@ùd=]Rš“Õ°k_à#î;´oñ§¯Pûõh…‡¾Ù"EëcòÀj¢¤H'¼îpBf‹{DœÁÿ‚`„¡ù쫇÷/î9´oñw|–™ßp‹4ek”÷¬&â©ð»0¦oš5%³1;Ÿw)¥Ô©'^Vþ©>·é}W;'‹3ˆt¨™’:½cä+Bê6@G¶÷1 eþHËlXÀø¶~ðëN²^]äp®rÚ·gá׈è­® î;³êçŽã à‹Ÿ|+â$ΔmŠô¦°Â'ØŒ-a• —8N01¼«ã“q¥!A90J…Bk7=¼²”’Ñn¸ñà’í«€µp8~jQœ÷¨ëß«£¯A…ÓÿüÌêúÊ•ÀÏ0ì<À?ç}Z¦·^ÆòO«É=Í«ÃKó–·hó‘ ´[$3†ƒ±>,O&q5"²£â‚냉~6ñz½è²‡…ív;MÉ|gØ ^àÒn=O¨Â ©ø}‹4ï±åsð–çlCU0šA"àÄ©õlýeQ,@{àj´n»é—Ì8Oi¶‚ 2Mày_œ8·©Œ7ƒ¡¬÷a±ÛÒ)=ÎökV ™~ÖuPÕ^¨÷ÿÏöúýŸtÒÔE]atvmýå~²26h%ß÷tï>W֦ЂˆJsç‹^ºu ¡ò½tƼ)XëÎY&õ<*ä´:z4‰,ákTO) øU÷\ZxíÂүߖ…o`4 ’g)4è“Ðíg0/v Tð>3¹,>ùG¯åÿã"àn¾A{ï™Uì!èKï‘"«â'AÙô¼´Ã"]×R–#"*À0±ú*¯jö eûÙàÃíÐ=Ûµ¹’Ÿ]“Úó'àœò¿zÉÕ\tfõüÏî_Z8Ðw#‹÷—gާÒ+1 ¹Ï =‘}Ž’à«Œ  T©ž*¡Tp³Ž($ºå*‘ŒÑ$)æ×t‰ðýNû;ˆèü§ƒþð³—);ÿ]ñ—oÐS•áê¸ãg7*¹A¤¼Îƒ{Bìª}+—¾+µ—+»^oxìr¼qív療ðRß®”}d*ÄA"`ãѳH!ªÓhGÂ\©p­nL²­dÆ­×€”Œqƒ 3×6úð<ÀËý±DC×EÆŠý|Á›°¼‚ì™#0^ýÙãË¿á$¨‹8ºâ€õïa«µ££kzò¬až¾Õ°¤%/œ°1ïŒò7¼K°ì«*!¹ÐËQÿ?¿9ħ>w gוQŠ,çZð¦´GEDÄDÔðSº'ìvßv»·^N< ÃN  ›ó_ŸùU‘¡r·YýD9Š"AÊsÙTœOÖƒJãa…B$a–É]ví˜NçNçãå`ø¥aÙàic}é:Ožöú=Ý[/ˆ2¸ìRXÈrëÖl Ÿ;q6Ÿ  sñ¶ΚöógØÿTèé`Ô@Ó”‡ `½Pò"üSŸ=¾ü#Nrº€£«Å8wþGì]@¯(á€SâͲFú:U·ñ´<þÅ“”Œ„e^yο… ±Ô+g7°|vݾ>äi­ý!€àùî»]H`LÀüR¿ß Anèóª.›™õ„G xhøå©s‰T h³€!Ánc­kJw_k½ÛíÞàÛ™ù›É­@û/" XqO¤‹%Mãi{ë{1l¡Ùðá{Âè˜Pi–œ Ï’BÁOèI*Wo†ü ¡ º¤`å¥Ïœ IDAT?r+>Kf^š3»CÒÚ˜×Üwlù§Ät€£«ŒNŸ;ÿ#û—žà_A×û°¡POžÛ„4À¤Ìó”Ÿ{ô,t}”-HÀh>æ³V£íG2Ä:.+ÿÇ_¿ýq\o¦žŒ&1>~ÿɬ° ¤zs`Õb̦ ¹ àÙžÓív>àßôûÔ-?qªR®sÈζÏXTùú…´Z=÷–³?ßû˜(üNGhùLô Ì|k…æ¦êõGÛ´s`{è5o3CwÐäûÉÓu¹ ÕkTš4z`Îs .Nk,_œã¯ú‰ù¾`fÖè¿óÀñåt’ÒŽ®RŠ“øë˜Å "“(¶ ’âD"Ñ}Æx^&ìÓb/Ϙ"˜ª•L§F¹N–2+Æ£<¬Zò\‰Iœ€@HXZÂ2!g  ¥·)­í¶º¬—‹<ÀÝnççûýÁ.GñÁ²–"Õ×)Í›_”ô¬ÊÛ¦M ÔZ¼¾›\ó’óaÍF`)"æóý3à­»yGÂn·ÉÌßUy'ˆBÎ-Ú4¿³Ã˜ü枇ómû^1eØ‚žç]ÐIˆ€ÕÞ0óüɰ²˜€„e!u›äuToïB·dè"œ]íø‚Éÿûþã+ßã$¤3]Å´¶Þ_Ù³0÷4KõbZ)üࣧAD¸ñð’r”ÌxüõÀ`<ôè¹R$`8‰0˜Dhú¾À,žVú4ý®^ÖFf+ŒóÇNƒA¸åÈþRås«àæ£ûÐŒqö|¯Ò›Ûùo•( ?ÑívÞÒï>¼›¶[-¨²¾<† ÷ QíÙ¦-c‰¬FmŒuë[»óÒïV핞'*Ðä˜|O¬œ_ï]òÈH§ÛÞ+XÜÂxAé†PE•ëÌ!Ž­©áûùÀ ùx.* ¥˜À„v«‘çóÚðšãM·Þ˜Å…ôúÅFZ°ó´°Õü±v#x ‰R)C–«"2Î^1È®º¸ÃÄ.2@#k•¯º9yZi‰„…˜­i®ZãŒIgë.Idõn -6†×!¥}­Ö\Èl´°¹¨ àüSþÎptͽÚ3?÷W‰”ïÚ^²tÆ+9† >{júI /½±­ë¦Ò,÷Ä:Õ ‘áôío ÃÎo÷zƒm‡ ºÝn£ßïO¶á¯>ÇÆX¤­o™þ±‰.D‹%C&;´¤ê< °'ÔèÚ|½0¨„=[μ^ àç/Lù·_à™9п.O>P^—¾È…fš¦4Í«.L­+e:-ë<ݹvv7˜M™òK.NÏ­–4fo¥¶± gÜcdá Æb·™áu0„J½ 0àýwâþí¤ 3]ËFÀÆæ]Nç€Wø‹3ûÁ3zÙÓDØNÐp4ÙÑ{²}€9ÂNØíÞ !¿€@OgægÑDtÄö ÝnÇ<Ûi€?5Îù-ýþà>íðGnÛò¢YyôqbÉHæB½" 3„d,¦™}虿ûEwÞ~ÓÓ?~ßÃ[ÖE„awÑëõO‡açÛ~Å4ÔêW¡è]ÀúdftšAþ‹Œûe©2¢b•×E¹a»ÞªP¿>n4™d©2*úͨÃì68Mµû¬¯!Bà{éwk‰ÏÑ œôs€#G g|+ÌÏ…_ÊÀO1óó —{¦¢¥ QTzJÛü˜ùsYC "ôGcx5SìjÜbãûê«ßgÇût°½É,¼ÀhןÛÔ¦Väâ€x€_êt: ˜Íj­XT „„”™$Õ?½xÆuÌš>×­”ÓŽ*N'º À€H/zÚ Íøz€¾ Às `˜†íz®+U…´³ Nȳ)FÞ:äf£r*fÖÖQ"ÂêÆ@ßïÔHOiÝ›e̾2K+­VMøÂ÷âþóNò9À‘#‹66{ïÝØì}ù¾0ì´ÛÍ;IЂ^˜ƒÎ«–‰P—+))«#TVET¿ldaA >|bµä5¥ºÇj åyTQD4¼-ia Ù)&š5âx€'¸•sŸ˜óZùüžä70¿Wv¾Ñ̨~R³Ê©+á&ÜêX§Ìæu™ÿºB%gw•A:5b:ÿ½áëýaU QØí¶%¢¿ÐJþñÝðáÜ7.þrù(뉃½Ñ8MmëåZ\dÆ¡Óxi¯Cõ<0Ú÷˜YM¯, +F±ÄF!&QÇZ“Då_:.O‚l¬Ô ’*(`šÅJU€®nôô²& |~ðчœ¤sä Gµt¶× ×{?€ÝtxÿÑ,ôX—„S kÍL'r’e‘–ȼ8*£eT)ú³³Uà×ÛÏʃ¨Â Ø†÷ŸÒ‹ŠŸáš_K¨ê\+à ^ÛÒô¡ dE›c<‰ÑŒ­ûV€ÚðL¤ MúŒ£±LÀf‰óô ¯Pî;•n²°&HµØ©{4u˜=KÔxúy”!_{ÅМpÁ¨¾U)¤ˆˆûÃ1˜™„ï8qzõ—tsä G3ÓÃËg¾õæë¼ ÀŸM;.ð}´›AA•©‘ÃQœ ‘ÉájN')¨P¦JoVa·ó–zNÛ¹R9 lk™¥’ÝÁ[Ów6cEšWŽ9ÉЙ¹òjS¥kÇ¡ÊHÒŠNÔF¶º¿4㺘2Û ætÕH•ª—?‘¬ÝJ©kZ׿©×$Me®×˜94É#š÷[tÈ=K=ÿs›ìë†xçúFï]NJ9r€£KJ›½—ÌÏ…?Ư©ZòÄü1êÓð~±€yzk[¥÷XaY¤ ~±Û‚ÔcÌΦ¦©±‡ŠLeO—­H«™©rJ hfŸ¸dX0×+2ó«"D„8QJ3ð=é ¶¼|fÝI%GÎp´›FÀXv"ÂöL45³xÊxÛ«Êε½ Qá&ì’AÇR½ ¯ Âs]mc„p±‘«¿ÏŽ&L1bf̵¥kH¤ÄpcëùwTqq<åâ«oÐë0G˜k6°Ðn•¾'jÂÿ[\š¡äÒß¼w®“að›óÊÚ¦**M?û-~Κ™v¹R2dЇPs §ÎoZž½©ìc™ NŒ ‘áåONÔ­fã½ÂI#GÎp´ë´Ùïø¦#þ½Œ4q,±¶1°JF­„Rå «ÈãœïÁPEˆEEæyM 0‰b˳L£s&ˆë½á¶™)nö_f·*r1m æ¡ ¨éDP•÷d)£üQT¤dx¸©¢.v¦3òz±Íëâ|êOåD;†Š4HbW½•}2…¡©aH4ýx³F&íh0 _g6ìD£–…Üóðò™O:Iäh[kÛÝG;EO¾íú®ddó`'Q‚å3ëvY!»€tŒ©Ùe†ÈSoì[èênÙ£E1â ¬ÜN³A„Sç{Ö…PázgÐØºÔ/ýM‚ê½÷´ˆŒ˜ë:ÅÁ%¥'£(ÎŒ"ªÝéæ8X© ß®[ZÈfH©òàD*/ï ¡°ô½TÅq¹‡Ûi4¬ž*¶8‘ AXYÝ4 ‰ ‹É †/øyȾj¶½Yx¨» ;½ªGXO»¿¢ÖBK+è/ÖÈ#šaàÒº!"t›ÉNâR¡:!³:(í6`Ýéú/Ÿù '…¹€£Ç„>ùÙãý;n9ú €[Rá+™a§ùmOõ‚¼ål,Íp ÐjØ3ßÍ^]]ïa°Åð Ú!F[ à3#» À†Îͯ± Ú¸ü·Ý ^ŒXj·Ñ >/ HdÕBØm†Y$ MåÈ´ž@y±Ó§¬¨sÄá cˆ¨Übyü a"C–”yƒÉH×ÇU׊qÖ ÏT†Ö§œòµšÖ“ØY"®©Û(“'ÈZ¬,Ü»^9딿#g8zÌéÃ5qLy¬FKØÅ´û¥^%™[Rˆ¦×l(††1/½˜D‰L*=å´{áB®uZ­@ý×ͼgVÀJiX=I¤F¦­£)é}„¾‰sZA%¹2KtÑ™'EqÞq¡ãÿêÜ5þÌhø^ž¯ HxB ÝôK¯ ²'„( þYmð=u $ë€KìÜVɃ6|zÃIV@hvP Ç%äûÞ£·ß|èk­œu’Ç‘3=ÆÄx;ßïá†C‹ŽcœK[¢¶ƒÇJ5k ]2phq. g§Îrà{8¸w±$L—ºØ3ßÁɳç1‰’’'·±›¥¶SÌÆ¼·§šTÈ›$•ÕU4{B‚ Ú”*\õå`ˆlçý‹Æ…ݲW,üܾÂåª>{ªN¥¨cIáÐŽ^ì ¥ƒŠa“·ÅYD\´O&‰¼ãoßûñž<Ž.hº[àh§éŽÇ=ÂRú|8šàÌZOÉ,1ã—ZϬ09S6g]ªfvÝ·áåP¿Q,á{‡÷-Ô*š“gÎcŵ^ÚÊj/Ç/àòâ¢òŸÖ"ÈS†×ëìI`˜kç}äi ýúI”d0¾):?Q±¯_Ø÷¢ˆVl´(˜×Õð=4üt¾=kåLÙˆ£n³!'ÏmX^»LØjw³z¤ª¨Q¹_¤]Èï¦úJJÆuKs•µÓB Eï—·¨'`fXèUòÀÊZ¯Ò¸Žh•WÍÆÉŠUó™™áÆì}U÷¢Ì‡l´³Y7kàDáZå”i‹Å.Èé„üd¬ !¥”‹)‘‰ízæùo°GBKçLÎêSMo,Ë\íaf&?öÈÉ3¯r‚Æ‘3]vô™}ð ;ò|"º‹ Û¶„òÌÜ(Ò)Œ rè_C9¾‡À÷jsò)¢]œHÄI’åÖM_jï8msÛR™î°ò7óÂæk›Ã¨âXi)Ž¢‚¥‹¼“ÆQ‚(ÉñõÓïÞN2³”³óXq ¥ÍLbĉć÷Zµi4Äó„ …= H÷ìï´=ûØ3phOf&Éü6"zÅ#ËgîuRÆ‘3]¶tïçN¼À\§Ý~Ý)„VÅ,Èr·ëùBW†S:±'±ñÊÞãlC‚§ÖòTªdÆÑ½óÆ –­•¿©óŠaým‡ªî™é;s»çûjnwWSÕûtÕúöÚk ÚðË/·â‰à¹ëãVé*ì+ÖÌ›E}E€a†D¥&xÖͬÛ-ŒÛ÷ÕÖ¼U–—ím!aY^Tû=µ,’S˜¢ðÒæ¾;J fD¶Z¡_£8RU;ÅZdIl`­"ktűªxÓça¸xÎù9„ëñF=yÆæËyVúe5¤¹ÚšwÂrû±‹MõEá_qŽCjEŠ¡»n-¾>Ÿý.?=È­"âWƒÕ*ÕÕ*}g¯×3¾G,4"è̼úOÐ$Ž v[åä¹¼ùm‡ÎÀ5ªÏR] Y¥9ŒTÏ®¡o÷{ó¿Í îörûžíž/Í WTº'±Aäy—hÎæ¥6ÛT ý:æ­²n>ê*õ/žb{+Ád±ªú„Á²ÙPµQ(¨å ¹.FÚ-Ë.ŒÒl¶S¿ô.{ Î› Ü_oš´7™ãÆx†7^:[ÿ;ðd¹b»ßÃx¶„n(ÄoÖT¼Ôýûí?ÄÎo;áŸS<¢Š¯™ÏgŸã'¹•p €¼ªL&“= ðƒ`ú¹ù“7,l«yÏ»µç网M-°H³ ½8¹Ñ6Õv:¿êëv½D¤s¦g­Å[¾äRmßöþtå2Ã2MË_a]º¹-êR¶¾ƒ¨T)zmÚ(»™|ÇèŬ whMð”ÕûŒâûæzIÐe¯l;×0÷8T3J1[»”Q$µÙq% 6į́߿X¤õ?@÷hãâI(àjø°?½ÀÝ°ÌÆ S9Bø¯ž§kË_«"Cõ—f³ùñ“‚PS*fï†ÿÀÿðÖMŸ ¶\ h×(€Õ*;D;HÍA°=;sÞö¥'I»ÇùþZ›U»ª5k«^ñªŠÑv£íö¦åë(Ü÷ÿ5‚z·#R÷€ ¶z83ÜÆj•–Å‘…÷¿ˆ`•eXkË×:±nx\+÷Ðqœéà£ÔNˆ×…¯ÁþxQ ¨å„JÕZW‚àÄÛZØÖM]ë•“ª1³4í›»×ÜËÐ ZÊæ¯Ìf³{ù A^-X@^SF£áßð³€^nåTµš‡Ö¿µ"2mÌ1§¨dϬÅv?Áh«ßFÆ\ÛŸv…U‹oýº·¶+ÈQyæ?ð䳈j‹ÄNx,W+ä¹Å*uÛÙB“åNx¤ aPóÿoí l¬G£A¿|mý$A/quªŠÌ×@„ 2Óå Fãù²>Ó¶áÓéÑ» 6S#APß´Îã¸ï;܃kqéܪÎt¨v ý“Lf ä8˜/Ë!3Á‹sý‚"ÒÎTíb´ÎÓ¬hƺ U_Zx?€ïN§ò0@^Gـ黼{4þ2`ÿ€^u‚Ï×UºyöœDQó3ºô (·êk] åPÎ,Õ¥ý]'6'2zqýmcDÐO’vçyUloõ±JS¬üR@8û+DM3;Q³^cjS<&2‚Aß=¿Vs(€ÜÒ<³­âAÀPà`¶Ü¸&-¨§½×‹°µ ê=Œ›ûµ™³@ÖÌ”Ý8_ߟ!·£Ë|@}yc°ÕC–+æ‹J=®­]42ûhlh(š` ¥³é‚®9ÏúáUÅg³éwð“€P׳øÇ£Ñö/òv¾ Ë›Z¾v}ÖÖ{À(6í:HâoýòËhmRGQg ÈU;óÔ¡?}½Àìð´wóë°¾AT°)]_ÌРàÐ )Úýƹ_gé{¥å°ÆÄI˜zV¦–¤Ñnà²ëÓã‡. È2ÛyŸ¨–é~ ºïEµWoµ9;×Î5“oŸÇ‘*Œüï*ƒ4›MßÉw?¡ “ù¾}4|€·¸ëHJ©_Ÿ"ÓZ˽8Aâÿ:@¢X¦¶z1z½¸%0ýžó°X¥Á=‰¢²ž ³˜Päð9av¥[컊sš#ˆŽ"°š²Sâ1²¡u²nèÂûJü´áø×2H*ÔŸ„ãªG|ù›Fè°û-U\Á_ÍæŸâ»žPR³_¿£ŠÁ¿ëœJµ‚ˆlœÝŸncºXâÆxÖù˜sàÜÎ`}ÀA½W}W‚WUSŠ‘æë´V‘æyicÜGp‡M¿Qdb\bE¾H±x=ÆŸÃv¦ëÓ<ƒãÌ:+ÓqHíÞÑ ûn%Ár•ÁªÛõצÐZ?‚Ìâµ®v ÿvÀ;g³;ø BÖˆ€ €¿½=x€wˆà›ÖÍ2»‚Xßû@8Š`D&Âv?A–ÛF«U·Æ?/a¤^Ò~[,Ó`;úæõ´3âk ¬7ÿ Í}|7Û`»™ExvM{`ñÁÞù)ö°ú¼rÌ+j c ­™ÓÔöú›ÕS®¡G;×ùýkMœµûÚ%N®Õ|åI$8_–Ûrº<T›òª-j´6Æò²»XµÜ†7ü€ŸÍfs¾»ÉíwÁ`0øK>`«êëéÎþÒÙQ½$Ë*'q„ñlE£ pqÿ=ÏâÎ ;ên½ýîsÊ%€°[Þ#Ï>È<}õòŽuåúšx;ÄIg& #*ÊÆ7–†Û=\83@êw¨­4_®Ê †ˆ õN€î÷VLY»]sCزO(ê!V—Î Ëß¢è± ÒlªMo…µQy¿µyç’F¸e³¸Þn_:w8káÀÈ)¨°buþ¦z-µßpëüÒ!†ÂûÜ:¿V:ã§¢ÿi2™¥|fyÌf³ƒ ~FÿÜÿíª¤¹‡ßúöÀÍ™ç¦%g9Äžm¾L]·ºónË ñ±öÜÞ Ø~xœÚ»(ô³ÍŽ9Þ^7Í zÛh\O¯#ŒW³ IDATl`;Z+KCœÉ1¶J ÅWѬÈR_ÑåšØþúWFri àEð³³Ùl—ï^B@È+s?6þ#€ßðõÅìÚ¸rr±Öúmp¶ýɬ•WpW›ÝÏ=_ÁZÅ#W^(ÝýB¢ŽÍî…ç}=®pGm‹\T÷+î:·E;Pì«jöê“?ïh3X´Âí6 º«¤ ë‚·zS µêW±¥Ì5¯«Þ¢7„‡l¨¨: 5A±cx~@Z[Ýõõst*iÕiÔ†M%°ð±e´mŠª¼kL‹q•0; ë·ŒúñÜÙJƳÕúTÚš¡Q}»¦Õ¢u€€ÿàÓéì ß¡„€[ž˜À§üЙÑ×ø#òUÜáCÓ‹p‹³÷ ðyy*³úÔ2Íqý4€TÕ!5b$‰Ý6Áù"…1n–ݵ<Í©µ•¡ÐúÙj»`ΪâÎó£µjaF!ËrdÈú½Äßgk-žÛvx¨*"ï[Ûr6î<·ãµXf9ö¦ó#uÎQÕšQRÑ¡˜½‡Ûµ±•BÚߊU…Ís[õ8S/–4M éb aín>©@§I‘@€]ÿf:ý2ß„€×ˆƒñäOüIñýþøÐ]V?|vgôoGÛ½§$Ň{šeHÓ ½$.g aÑZšeìB=µž[ …´f¯M³Ÿuq¾ÖJÝÁ‡Ä­öëBg¦ (ˆ —#жÈE Euósô5þõZ€f†vihýûòêª&N(³"F€í~ÕMw^I]¯øy_˜Ng–ï>rRá.òºæMw_ügþs9«Ï-¬µØêõGQ«ªÜyìƒa«4kY×FÆé=õüõÖ}E`ÞêÅ¥S_]`¸•úa¿_朓8Ú8Û^·P<§Âú®®Pî¹›ãvªÞ«ª¸|nÕzc#wn`ºL1]¬"#¾sÈiÀpÈëœÀ­ávØîêq¬™]W;ºfçÒ(6ׯ,{ýü}}ðï üMa!R{¥/€QÍbȮׯ5ÿ‚úã‹s†6ºáï”[‹¼ø=ûb«êî³Vm%:VTµÆ»¶npiýrÕÕã‰X,W)«¼¸rQÅ Ÿ`¸•Ô¼ U‹$Šõ®;Îâüξ[sùjršày]³ZeÏŠ`Àå—{Át:¯Íz#ìÐ× ÀqµfÙGþ}ÒÌ9ŠÖœ zšÈb©ænýQe‰o€4ܪ™YäïÂNMÙu–ɨ·\vc\ßj˜Äõ΋i¨µÐþW´±ŠÒn ªAGE-ÒˆL$~ãÌp뇞|öêU¾[!§ˆ«×÷æ÷\:ÿcÞ…r· ÖgÎA *SÜh• Ì ˆtšÞ¯_ÖÎàßÕh¨–9èxžæ~Ys®u|ÝÎàzÀÆ¡aǾpy ž5èȺ4+õ‚sö“¡!sšg‡^kWœÙ6q¶ª¸t~ˆ¿ƒU–µªÀm ¼q0ƒ‰ šis@±Ý‹Gâ}ë«#‹§mõkñùåóô’_÷g¿´•®/NóÌîu·¦îchnm`“ L3§PЩÖçï<ëjr[™[ŸîÏ—X¤) ¬5X«øŠ7\¬½.·4Rc6]”Ù†b²¨á;˜-Ê:ƒ(2.KÒ¡0š-™Wy°LóŽ% ñÙ–~l F°\å¸//.žßyG™ø£‡öß„B^'<·{óoÞséüx›ŸªªJÑs¾ØNθ͆™p™Lî˜W…rõЭÚl©«åyŠuvã¢6DV-Ê×ו}hÁâßЄG‚ÍwÆmö²R3ø±y¬ÏW³øb©!ò ílü.¦ ¡´ú&hÃU01&° ¶­¼F‘©Hb÷»,Ó Í:[viìù^«,,óóãÉäÇoìù& „¼ŽEÀ7Üséüwø¯vDDoLf. U ½Øº¦T¦ºÆ¾ÎK¯b2¯; Ö+îQ×FæÜ‰mFµ/ÕÚÖÿ"ØÚîµvì[)ÏW™´kÎ ¦±Šï´…BŒAì+ò»†+Ž ¶|;æzfdͨ–ñ™‘8BÚÝòYÈ"Í`Džð€|`<™ìó/ŸPBðÜîÍ÷xï=—Îÿ*€¿-À@àÛòI±nÝtTQˆ•V¼vØåe^H¨ ¬ïÚ'º¦. iW¬õ";Q,+ÁPU`ƒwù*¬n*ґ¨×!TÏÛÎ`KúRõÚñ¿³´Å€tûï#ˆ#gê#çY/Ù“zGe•€Dp_fí/O'óò/ BÖ xÃ¥óÿ2Íì?µª?dn²"ð~Š\¬ Z‰w‘1p΂U0[¬²ZØmšá¨ÖÍu"#ˆ½/@GH⸼?öYŠå*mÃçžÌ][ãÚ~w¨™U¢àÒ˜×Cv£Wž †É iÌØ]­«#¨§Nrïh(¾ž $B³§A%ÄÎûš…ýñ«l!^@|П‡ÊGìN¦lÅK!GàÊîÍ«~*êõþU”$?*"À[DäŽ"º-W™ë£ª6·µ©n½C_£Ñ޶ƒ}×úym*ð7™#2‚ÁVyê¥ïuù4xV¦ÃƒÕûæTZZOŠÙr ßtx¶¯X¥y)@B¯¢ˆÑxQeŠßà ![Z)W» ƒ¤¢…oÑU12¦fÈädzüŸß>ùd™Œ˜ß2jÞµ?=`À'dÜ@È93Ez¾ À7ªêWø‹ œo4 – *ÿ}õ©møúÝø&Ô µnl*gÀ"å½3ØÂW~é]e¥àSW¯ãÆÁÔ…_f‹Ô¹Üù·»ªm¸Ý…Þ1ÏŸ;‰#Ü}é,Šõ¿^–»ÀýÂ1VZ€,·øú¯üˆŸ{ü9_ÀW÷S?Nƒ~\/`ô®‹ªŠ4·µÕŽÈHQ ¨Ö*î8¿S8ŒUõíy–ÿ—~/¹öùGŸYò/•f9V&“ÀÜß~ÛßJƒÁ[|%€¯ðuþ€/ ºò©”›ï*—»Mkòe c«{Ä™ãd6ÃñÄ/× «bKbÕÈF‚bº¼¾¬ß!ªFAQd`$°ÐõO?Øê!‰cóþeB@ÈkÊl6{ ÀcÞˆ‚€ïä¬Í¿ÑGfS=i¸°á9Ó,ÇîͱŸ- æËUg±\9Ó>‚#`eÜÓ8Ö}f8À…!Œ¯9è<¾ö"Zk k·BÔŸúÆ;Ï!‰ãOŠàíÏœyïï|üO3xá‹÷ ¡ äöïðŽáp°ç3ð—| €oÜ6«À.žmoŒÁ3/ܬöã뚘zêgçGé€gŒàž‹gGî¾x³Å »{-ÐÄbƒ\¼~çc -âð9ˆþâ*ÍヌžzšI„Prb™Ngc¿ïo€Ñhô5ªú=ªö¯ .‹H®Î  6FðÔÕ›¢‘ŽØ"shå0X7Ð)² Zkª¦Øë³óðXg¢s~gQáÜÎçv†xãÊǬҴånÜe/¬PUUé#(_cíî±*þD?rõÚÞýpu—3}B(9…L&“Ïø €Ÿ‘>€3vŒlè­ÒlñÖ/¿üáÇŸ½6ʲ½$FK¹Ó`¾tÆ<½Ø”.ƒE ï'. ÏW¹Û.7Ün•ª*î87ô¶¾=»‹ÜQäœU]žõÁ:ŠcÀ Z{"Ä‘èb•I/Žà–Dž0öÂæÍFä²µzÙ9 àQ¿¢Š½p}Mwy•à.BNßðç¿âæç»rÎˆÛ ýnÿüV¯Ú!М‘O)¬UÜunÛ¨+°Vq÷¥²å¯+þ“²½ñ`«».œ‡ Ó l–CŒÁÓWw‘fÕn€ÁV¢d•Ú÷âþÇþ:¯!ÌB^¾V/cv–[dy=ïÚãjãÑU1ß…ÑözÕßèjh­ÂDΰ'jøõ‹¶z1T̸íŒ[½QT-&±ªïýÄý}7¯!„W€ªuëóâRúúí–QÜÔfõÁÑÎyÀwm4 vKÆãj ¬‘âKØÀ‘PqQ6Ï•ÂÄHÁ}_x6]eÙOóªB@yÅ m'jDd¶ÌZS÷,·˜Ì-z±)³¡ÁÎ|•"Ž ’(rûôkâ¢ÊÄQ„8Äq\¿ÝO0ØJücëÝüœ©|ü9‘‘G zòB@yE#לmŠ‚MU<Íýû«,w@9<¿»ð¦Ëçj?€Ùb‰'®¼€³£!.œa÷æÆÓy¹îoÄ5ðùì£ÏއöÚÍÉŠW BÈ+ä±gw3FÞ _á¯~†ßÿE\¿jµP4òIâ¡÷~œÄ­€“Ø-¬¼ÁèõªÙ¿óp.QÔ¤ÞØ©í@ „PB^!‘‘ÿè·»uw­*î:vÙ»jþ* àeÁÚÔxo¢û¯µŠÏñ*Œ¸¦=ý~PàŽsCÜua§lé[Çí(vBN†C@ȉ཭h/ÅÿÂ[õ3ñ[“8Úì¨Õ±‹•ó0âšï¿ üzÓ¹D”„PBމ®ïïú‘b¢Þв¶ûU`Cn¥éÝ <üô‹øâ•ëE罗ŲÑ!B@yeo×ß1²³Õǰ߃µŠÜZäÖBÅÀÂ@ÄÀHUAn¹­RóyžÃªë1P4R ÇÞ­^‚^WA\k-Vi†ƒé‹•3r7¿|Å`«‡áv¯ìäG¹½a !'‡O—“vÿ¯ˆÔfóQdÐO,Ó¹ukôÅý"‚EšBDG}ˆòܵêu‚Ààìh Vë{“ò˜¢Ú<]â`²ÀÅsœßÙ®•!Xîºã Œ¦ n „€rl¤Y¾–ô ú½R ·ÍoÍõûæ÷¹UØ\‘Yo,•äÈó `•æ˜.ÒVýá|™AÜ!æ ÎÊ•û, „€òJYe¶_|mDpaà€Û{¹­µ#6Çß°  ÐÖ*Òüâ;óѹ,Ò[ÿt \Gxÿ*uÞyfaˆÀªZƼb„PBŽ»ÎŽ4œ³‡%{£AÎ aÕÖî¹çâYˆžya¯tí ö!Ðt±h †[ rë·þ³|UÅÍé|ÝKÍ\å#„€r ¨Èuúæœ]ŒyqO•®—#ÎìEÄÛþñgÒǵ“ b)¡ „›ÐÇ ÀÅϵ#í/µ-‚›ÒþŠÉ"=4à¯9‡(ð©édrƒŒ BÈ1æùƒ|:0Æx“[˘F›Þbí¿H„µn£€¬­È­…øçê!ø{¼Z„PBމ§¯^{æM—/Z¬éâ© #Ÿ° PÁt±Ð`Ï¿ϳZÀ—ÒæW!‹,ƒÞ7™LžâÕ"äö‡}; 9AˆÈÿí ü½$ÆV/ÁV/A¿— Ž â8BGˆbS‹ì"ƵñÁÕ‘Π…&ø9^%B˜ „3/îMÞ÷…ooÎøÇ³%ö§‹2§iæ¾Wúe ¯)8ï Å™A`ÍoË™¿­)€ª­ï­Ð&qd”W‹ BÈ11Üîµ~ÖOb,V©UÜÝê9“ Ù"-ƒ¿›åK«M`Q!pQ[¼¸?áÅ"„€òj nJßÙ¨ þX³ r l¾Ô,Á-Ê*B(!]¬ò ‰£.kD~k_ž+ -Ó_ˆ"‰`Õ- û ¬*fËôHA9B(!¯Q@×Ùöjs†nK‰ Z?Æ%ô%EuÀy?!„[å_ÎA"Èr[ËÑ}g»·v‡À-a„×úrrè­Ÿ…k-K#(—®~þ޵5ÁãõeŠB3„[ˆˆŒÁp«UÅ*Ë0]¤H3Ûž™KðµV"`<_AìlµµEd£íòÜb¾ÊŽV  ªÂJ@B(!LJÓ÷‹…ªu@E6@‚àº;àa]ÿ^âü_"#)¯!„cb¶ZM$ÈaܶV±LórÖïÿk3·ª7xµ¡ „×›52»>ÀV*@*PdÞÛköím3ýCz¨jíÐ.!"PÅ‹¼Z„PBމí^ÿE˜În ?(áMbƒ$‰°Js¬Ò¼ðëš ¾qOD0Y¬¸]€`»—”²!·I!Í,–YœE×e®ójB@9>®Y7 oo½x€unÎ%0Ž"8¬É HÓ)‘10=j†U&eðßÀs¼T„PBŽ‰ÇŸ½úâÝÏ¿ìãUo¸ëœoÜÍl¶8ŽÚGyµ¹ý¡!'<Œ®EüZ7_iq-½þ0F:o‘DÆøÓ53 ]O«]Bã~^)B(!ÇŠ~  ¼õ>â…@þ¥&ü㛂Z7ªß'•à<¶&$ø¯” ªØô>ËëD!äÉrý?y®°9fŠ4³Èr·p™æÈ­º†>kªùEÜL?Ï-òÜ=®æè3‘ÄÆ Š¢RT(Üõ·Rƒøc\_ÅÕk{»¼R„Üþб‹ÄÎpØÈ¢(ú+ȃu}iuT¸€-"°Vñe÷Ük]—Àð±Ög³•·äyî·÷¹âÁUž#óÝÝPðøîƒ·ðJrûÃ"@BNVz ÀE3qÞþü¥/a¿´Ûkå èzø ƒþ81þß¼î<Å–ÿ/×ÿ ¡ „7â¢öîPT¶ûªUÖ_Ê9¹4%@ýÛº¯>èOW ÷­²Róhªà3¼J„œ X@È b2™(€‡\σâö’…„od¡¾´Ï×ÔnÆ×DQTféõói^%B˜ „Ü>&"ÿè×Zñ¿@0/ê-„}`ß6Ðý+µY‚­±ãI\2€B(!·ˆ{»ãïšð¯‚8®Šý¬Uôûq-½_Tò7Ħç ïõrañâõýgxy9p €Æd29‚Ó^Ë)¸œáGÆàùëc¶z½†h—†[x¯!„[˃/ûH…sþ“Cvk§Ë_%/BOÇÿàe!„€rKÑלxŽ’ Уd Ö¬î£Ê,V9Æó½8Æp«^#6Fäx]¡ „ÜB¬µµÖ¢¸5+óC›Þ"ˆ—3õ†ƒOå%à÷õ—®~î±*•°ÖŽw I£—$ô{½œW†“‹ 9‘È}ò|<ÓÜÅæ^\½å\½9†Bq÷…3Pu. `woܲIAˆÄ`±Ê°JsŒ¶{jD~üÊ –×…f!·2ü ,Ž|@¸R¬”~V‘[ « kµÚظ¥™…µîÛU–c¾J¡ªà>^B˜ „Üz–öl­‹÷áÔ]‚RýºG ÛðâþÔgÕo ìî8¼!ëp// !ÌB^= Gy°•€ÒœØC*‡?A٠놠rࣼ$„PBn1Óél à^bGOµÎhÝ­Ûüç°“âsOrýŸ BÈ«…¾Ï—ñýbS@×ÿâöOiUׂ BÈ«†¼ËåìÂjÝ ˆâª^#¸|–+²£U·â Œ`ß–¼„œ<è@ÈÉϰ*Ä|™ ð…úbûßzÊ_­nHØšP z`Å+A!äÕåÎ`ûpµ å?¹ôqÔÎØ òkGZ ( Ì-ЃÙlÆ !„WÅ>€$Ú+ï)Ûù6“ñå‚Eë_Y§''x¡ „¼Úˆº @ͽWÚA=x€ ½õÛà €imÐYQ>æÝ¼„œÐ!'›Ñhð¸µxs=n3~?Ó/R÷a€W4{Ô½?3é‡ÉtÆÏBN(Ü@ÈÉç3Ç=+híð[´vÃÿäÐrrá!'Ÿˆà;{n¼G‚­‚ŽÈÜ1ÚFnË,k¥ sÕÿ6Í8ú„œP˜¾#䄳½=8 `o]¨ °þUçÜ[+ò³UOŸ(ŠpagPÒØð¦¼õÉç®=Í+@ÈÉ„K„œpæóÙ¾v«¶½R»Õ½}¥ñ_×´`S3)Î1äYŽ>!„×>v¨™ßñ’=ùÜ.[B@y-à£e»í°øíê Pü¨æïµý€«Í€ @û_vÏÅmŽÐëEèõb$IÜJó—±¾l!hüM*­ Ê&@„œ2¸@Èéäaù¶ã9Do쟅ˆ ö½~RÏ ¨³6¹EÑ,h•gPUl%®©*îsL a€rÛóI7»÷ýÐ5vÀÝgyŽÜZ$‘A™ù™ÁÖ‹RB˜ „Üæ$±ùTäy®PÆóEÇ£ƒ~€"Ë-rk«½.m𼸺B!ävææþø¡ógwÊ÷þ—²M@)lϬZïPTˆªêUcLÊQ%„€r2x À[à†wô3Æ`}#€ND'~ú9Ëá$ätÁBN/÷ºÙáh;bÿz1 Už€[ ¡ „œ >Z‹äˆÑFá_æá\ÿÒÜ–·,Ï‘åù§8”„PBN÷¹9¾†1¾ÓpI †ç!„œ.X@ÈéeÀT CkÝ6ÀÅ*keýÀͱÅV/lƒ(Dù‹×÷YH!ä1ö·¡K|¤/´@–[Læ+ì zHbƒsgFEÁ?}ñú>G’S—9¥¤i>QÕ‰–kÅô¾ýØbY ‰ bçüçG8’„PBN“Ù,·V¬UÑ#îü;wf„3;ÃÐ"ðŽ$!„F™‡âÈ Ž‘‘µÖ¿Úø7à)Ž"!§ôóC@Èée»Ÿ|> ëãYÞzŒ¬•Ê®‚„ BÈI æ{IX¥9D¤üÀÁdl÷¡ÖPVB@9<Äwé'Á*íȈàúþ "‚Ñ ‰ ìq 9°€SÌ•o<á×ýËɾlx|Ñ4èÌ` ;Û}ìl÷•£H3„ˆˆy4Ëó?£E 2«Æ´QàŸ$eW@B3„“Éo¸¸/X¬rÌWykÚ/"å-È(ºB@9‰¤Yö~r«È­vöè`îo„S—9å,Sûku`€@ÛûýµJ"#€bb­p a€rÙO& |\½ã¯1ÖÝD!Œ Œÿ0P¸¾»ÿqýăONÿÜ—]žr ¡ „œ\~£þ­`]y¿ˆ Ž"1"/üâ¯dÅ¡#ätÂ%B^|¸ë‡ …0Òÿ?ä°B@9É(ƒhk¦¯n7 ,F¤¸Ö¿Å#äôÂ]¾„¼N¶ý_ªw¾Ö¿DÆ­ îíùù@3„S ÷SI—ì7D‘ Óÿ÷r¼9ݰ× ºÖ׿£ðû8^„PBNEü—=ø5ÿòÔ}™@àc0B(!§C<… ÊÄEýâK÷Ó¥T] !„àw*E°¯õTgl*øÍ›ûãŒÓþ‘@y]°3½ÁZûl«@õ­DÆ$7÷ÇGŒf!§€ñdrED~iÍÝ ¨XÅÏ0ø !ä2? à[ŸÐ?œLfßÌ"äõAÄ! äõE¯×{€'à|@ÞÁ'|ïd2ûŽ!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„By=óÿ=L¡c¹IEND®B`‚quakespasm-0.93.0/Quakespasm.txt0000644000000000000000000007171713203362342015341 0ustar rootroot QuakeSpasm ____________________________________________________________ Table of Contents 1. About 2. Downloads 3. Hints 3.1 Music Playback 3.2 Controller Support 3.2.1 Cvars 3.2.2 Buttons 4. Compiling and Installation 4.1 Linux/Unix 4.2 Windows 4.3 Mac OS X 5. Known Bugs 6. Changes 6.1 Changes in 0.93.0 6.2 Changes in 0.92.1 6.3 Changes in 0.92.0 6.4 Changes in 0.91.0 6.4.1 Bugfixes 6.4.2 Visual improvements 6.4.3 Interface improvements 6.4.4 Code cleanup / Other 6.4.5 Raised limits 6.5 Changes in 0.90.1 6.5.1 Bugfixes 6.5.2 Performance 6.5.3 Visual improvements 6.5.4 Interface improvements 6.5.5 Code cleanup 6.6 Changes in 0.90.0 6.7 Changes in 0.85.9 6.8 Changes in 0.85.8 6.9 Changes in 0.85.7 6.10 Changes in 0.85.6 6.11 Changes in 0.85.5 6.12 Changes in 0.85.4 6.13 Changes in 0.85.3 6.14 Changes in 0.85.2 6.15 Changes in 0.85.1 7. Todo 8. Copyright 9. Contact 10. Links ______________________________________________________________________ Page last edited: Nov. 2017 1. About QuakeSpasm is a modern, cross- platform Quake 1 engine based on FitzQuake . It includes support for 64 bit CPUs and custom music playback, a new sound driver, some graphical niceities, and numerous bug-fixes and other improvements. Quakespasm utilizes either the SDL or SDL2 frameworks, so choose which one works best for you. SDL is probably less buggy, but SDL2 has nicer features and smoother mouse input - though no CD support. 2. Downloads o Project Downloads: http://quakespasm.sourceforge.net/download.htm o Automatic Builds: http://quakespasm.ericwa.com/job/quakespasm-sdl2/ 3. Hints Visit the FitzQuake homepage for a full run-down of the engine's commands and variables. o To disable some changes, use "quakespasm -fitz" o Quakespasm's custom data is stored in "quakespasm.pak". Install this file alongside your id1 directory to enable the custom console background and other minor features. o For different sound backend drivers use : "SDL_AUDIODRIVER=DRIVER ./quakespasm" where DRIVER may be alsa, dsp, pulse, esd ... o Shift+Escape draws the Console. o From the console, use UP to browse the command line history and TAB to autocomplete command and map names. o There is currently no CD Music volume support and SDL2 doesn't support CD audio. cd_sdl.c needs replacing with cd_linux.c, cd_bsd.c etc.. o In windows, alternative CD drives are accessible by "quakespasm -cddev F" (for example) o Quakespasm allows loading new games (mods) on the fly with "game GAMENAME {-quoth/hipnotic/rogue}" o Use "quakespasm -condebug" to save console log to "qconsole.log". SDL2 builds no longer generate stdout.txt/stderr.txt. 3.1. Music Playback Quakespasm can play various external music formats, including MP3, OGG and FLAC. o Tracks should be named like "track02.ogg", "track03.ogg" ... (there is no track01) and placed into "Quake/id1/music". o Unix users may need some extra libraries installed: "libmad" or "libmpg123" for MP3, and "libogg" and "libvorbis" for OGG. o As of 0.90.0, music is played back at 44100 Hz by default with no need to adjust "-sndspeed". o Use the "-noextmusic" option to disable this feature. o See Quakespasm-Music.txt for more details. 3.2. Controller Support The SDL2 variant of Quakespasm supports Xbox 360 style game controllers. The default configuration uses the left analog stick for movement and the right for looking. If your controller doesn't work you can try placing this file https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt in your Quake directory, it is a community-maintained database that adds support for more controllers to SDL2. 3.2.1. Cvars o joy_deadzone - Fraction of the stick travel to be deadzone, between 0 and 1. Default 0.175. o joy_deadzone_trigger - Fraction of trigger range required to register a button press on the analog triggers, between 0 and 1. Default 0.2. o joy_sensitivity_yaw/pitch - Max angular speed in degrees/second when looking. Defaults are 300 for yaw (turning left/right) and 150 for pitch (up/down). o joy_exponent - For the look stick, the stick displacement (between 0 and 1) is raised to this power. Default is 3. A value of 1 would give a linear relationship between stick displacement and fraction of the maximum angular speed. o joy_invert - Set to 1 to invert the vertical axis of the look stick. o joy_swapmovelook - Set to 1 to swap the left and right analog stick functions. Default 0, move on the left stick, look on the right stick. o joy_enable - Set to 0 to disable controller support. Default 1. 3.2.2. Buttons Some of the controller buttons are hardcoded to allow navigating the menu: o Back - alias for TAB o Start - alias for ESC o DPad, analog sticks - mapped to arrow keys o A Button - alias for ENTER in menus o B Button - alias for ESC in menus These buttons can be bound normally: o LTRIGGER - Left trigger o RTRIGGER - Right trigger o LSHOULDER - Left shoulder button o RSHOULDER - Right shoulder button o LTHUMB - Clicking the left thumbstick o RTHUMB - Clicking the right thumbstick o ABUTTON o BBUTTON o XBUTTON o YBUTTON quakespasm.pak contains a default.cfg which has been updated to give some default bindings. L/R shoulder buttons are bound to weapon switching, and L/R triggers are jump and attack. The controller support started as Jeremiah Sypult's implementation in Quakespasm-Rift and also uses ideas and code from LordHavoc (DarkPlaces). 4. Compiling and Installation Quakespasm's (optional) custom data is now stored in the file quakespasm.pak. This file should be placed alongside your quakespasm binary and id1 directory. To checkout the latest version of QuakeSpasm, do: svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm 4.1. Linux/Unix After extracting the source tarball, browse the Makefile and edit the music streaming options, then ______________________________________________________________________ make cp quakespasm /usr/local/games/quake (for example) ______________________________________________________________________ Compile time options include o make DO_USERDIRS=1 to include user directories support o make DEBUG=1 for debugging o make SDL_CONFIG=/PATH/TO/SDL-CONFIG for unusual SDL installations o make USE_SDL2=1 to compile against SDL2 instead of SDL-1.2 Streaming music playback requires "libmad" or "libmpg123" for MP3, and "libogg" and "libvorbis" for OGG files. The project can also be built with Codeblocks (project files included). 4.2. Windows The QuakeSpasm developers cross-compile windows binaries using MinGW and Mingw-w64 . The project can also be built using Visual Studio 2005 (or newer). 4.3. Mac OS X A Quakespasm App (including program launcher and update framework) can be made using the Xcode template found in the MacOSX directory. Alternatively, have a look at Makefile.darwin for more instructions on building from a console. 5. Known Bugs Brightness issues should be fixed with GLSL gamma in 0.90.1, if your system supports OpenGL 2. For reference on older systems: Some versions of Xorg and SDL have brightness issues. Try setting "export SDL_VIDEO_X11_NODIRECTCOLOR=1", or if you have Xorg >= 7.5 and broken brightness, these patched libSDL binaries may help. o Gamma patched libSDL (i686-linux) http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download o Gamma patched libSDL (x86_64-linux) http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download 6. Changes 6.1. Changes in 0.93.0 o Raise default "joy_deadzone_trigger" cvar to 0.2. o Raise console buffer size to 1MB. o Raise MAX_STATIC_ENTITIES from 512 to 4096. o Raise MAX_STACK_DEPTH from 32 to 64. o Raise command buffer size from 8K to 256K to support large configs. o Remove MAX_EFRAGS and MAX_MAP_LEAFS limits. o Remove "Loadgame buffer overflow" limit, which could happen when loading DP or QSS saves. o Adjust "exceeds standard limit of" debug warnings to include the actual QS limit. o Change "game" command to now exec quake.rc. o Change "games" / "mods" commands to list all subdirectories. o Restore vid_refreshrate from fitzquake-0.85 for SDL2 builds. o Alpha-masked model support. (MF_HOLEY: 0x4000). o Invalid skin index now draws skin 0 (WinQuake behaviour) instead of blue checkerboard. o Change default screenshot format to png. The 'screenshot' command now supports optional format (tga, png or jpg) and quality (1-100) arguments. o Revert "always run" changes from 0.85.9 and move the QuakeSpasm customizations to a new "cl_alwaysrun" cvar: Set to 1 in order to scale forward/side/up speed by "cl_movespeedkey" (usually 2), and to make "speedkey" act as "slowkey". o Change "always run" menu option to offer three choices: - off (cl_alwaysrun 0, cl_forwardspeed 200, cl_backspeed 200) - vanilla (cl_alwaysrun 0, cl_forwardspeed 400, cl_backspeed 400) - quakespasm (cl_alwaysrun 1, cl_forwardspeed 200, cl_backspeed 200) o New "r_scale" cvar. Set to 2, 3, or 4 to render the view at 1/2, 1/3, or 1/4 resolution. o New "r_viewmodel_quake" cvar. Set to 1 for WinQuake gun position (from MarkV). o New "find" / "apropos" command, searches for commands/cvar names for the given substring (from Spike). o New "randmap" command for loading a random map. o New "gl_cshiftpercent_contents", "gl_cshiftpercent_damage", "gl_cshiftpercent_bonus", "gl_cshiftpercent_powerup" cvars for tuning the strength of specic view blends. o GL2 renderer: use a GLSL shader for world faces. Fixes reports of integrated+discrete GPU laptops having inconsistent fog rendering. o Fix macOS startup delay (avoid calling gethostbyname() for ".local" hostnames). o Fix memory corruption in PF_lightstyle with out of bounds lightstyles. o Fix crash in BoundPoly with polygons extending beyond +/-9999. o Fix QS window to stay on the current monitor when changing video modes (SDL2 only). o Fix possible freeze in SV_TouchLinks regardless of what QC does in the touch function. o Fix for maps with empty strings for vector keys (e.g. "origin"); don't read uninitialized memory. o Support for Open Watcom compiler. o Update the third-party libraries. 6.2. Changes in 0.92.1 o Fixed large menu scale factors (was broken in 0.92.0). o Fixed PAUSE key (was broken in 0.92.0). o Updated some of the third-party libraries. 6.3. Changes in 0.92.0 o SDL2 Game Controller support. o Contrast support with new "contrast" cvar, behaving the same as MarkV. It may be a useful alternative to the existing gamma control for laptops in a bright environment, etc. Raising contrast gives less of a gray/washed out look than raising gamma, but at a disadvantage: colors near white get clipped to white. o RMQ protocol (999) support, adapted from RMQEngine. o New "-protocol x" command line option. Accepted values for 'x' are 15 (NetQuake), 666 (FitzQuake, default), and 999 (RMQ). o New "setpos" console command. o New "vid_borderless" cvar for getting a borderless window. o Increased MAX_MAP_LEAFS from 65535 to 70000 and MAX_LIGHTMAPS from 256 to 512 in order to handle the oms3 map pack. o Server edicts are now allocated using malloc instead of allocating on the hunk. o gl_clear now defaults to 1. o Fix items falling out of the world on oms3.bsp on SSE builds. o Worked around an OSX 10.6 driver bug when using FSAA, which was leading to an unplayable HOM effect on the rest of the screen. o Fix wrong trace endpoint from the tracepos console command. o Updated some of the third-party libraries. Other fixes/clean-ups. 6.4. Changes in 0.91.0 6.4.1. Bugfixes o Fix unwanted fog mode change upon video restart. o Work around Intel 855 bug in status bar drawing with "r_oldwater 0" and "scr_sbaralpha 0". o Fix an obscure GLSL bug where changing gamma would result in the screen turning to noise. o Fix GLSL gamma causing the tiled screen border to turn white when "sizedown" is used. o Fix an alias model VBO renderer bug where a model not precached during map start wouldn't be drawn. o Fix the order of OpenGL context creation and window creation in SDL2 video. o Fix a calling convention issue in windows DPI awareness function pointers. o Fix a random texture recoloring after video mode change. o Fix a liquid turning to garbage after several video mode changes and "r_oldwater 0". o Fix a wrong alpha-sorting bug introduced in 0.90.1. o Fix "flush" command not reloading mdl's from disk (bug introduced in 0.90.1). o Prevent a possible buffer overflow in Cbuf_Execute (old Q1/Q2 bug). o Prevent a possible vulnerability in MSG_ReadString (old Q1/Q2 bug). 6.4.2. Visual improvements o New cvars r_lavaalpha, r_slimealpha, r_telealpha for fine-tuning specific liquid opacities (from DirectQ/RMQEngine, non-archived, default to 0), and new worldspawn keys _wateralpha, _lavaalpha, _slimealpha, _telealpha, _skyfog (unique to Quakespasm, similar to the behaviour of the "fog" worldspawn key). o GLSL gamma is now supported on older hardware without NPOT extension. 6.4.3. Interface improvements o New r_pos command to show player position. o NaN detection in traceline with "developer 1" set now warns instead of errors. 6.4.4. Code cleanup / Other o Update third-party libraries. 6.4.5. Raised limits o Default max_edicts 8192 (was 2048) and no longer saved to config.cfg. o Default heapsize 256 MB (was 64 MB). o Default zone 4 MB (was 384 KB). o Raised MAX_SFX to 1024 (was 512). 6.5. Changes in 0.90.1 6.5.1. Bugfixes o Fix dynamic light artifact where changing lightmap are rendered one frame late (bug introduced in 0.90.0). o Fix texture memory leak when changing video modes with SDL2. o Fix rare incorrect mdl lighting on 64-bit builds. (details here: http://forums.insideqc.com/viewtopic.php?f=3&t=5620) o Fix fullbrights turning black after "kill" command (bug introduced in 0.90.0). o Clear all fog values on map change to prevent colored fog carrying over to jam3_tronyn.bsp. o Allow loading saves with } character in quoted strings, fixes issue with retrojam1_skacky.bsp. o Fix viewmodel not lerping on extended-limit maps. o Fix crash on out-of-bounds skin number. 6.5.2. Performance o Use multithreaded OpenGL on OS X for better performance. o New, faster mdl renderer using GLSL. Disable with "-noglslalias". 6.5.3. Visual improvements o New gamma correction implementation using GLSL. Fixes all known gamma issues (affecting the full display, persisting after quitting, or darkening the screen on OS X). Disable with "-noglslgamma". o Use high-quality water by default (r_oldwater 0). o Shadows use stencil buffer to avoid overlapping artifacts (from MarkV.) o r_noshadow_list cvar added (from MarkV.) 6.5.4. Interface improvements o Support pausing demo playback with the "pause" command. o Autocompletion for "game", "record", "playdemo". o Experimental windowed fullscreen mode available with vid_desktopfullscreen 1 (only in SDL2 builds, takes effect upon entering fullscreen mode the next time.) o Silence "exceeded standard limit" messages unless developer cvar is >= 1. o Some spam moved from developer 1 to 2: "can't find tga/lit/ent", "trying to load ent", "bad chunk length", "meshing", "PR_AlocStringSlots: realloc'ing" 6.5.5. Code cleanup o Clean up IDE project files to build on fresh systems. o Update 3rd-party libraries. 6.6. Changes in 0.90.0 o Fix issues on Windows systems with DPI scaling. o Unix/Mac user directories support. Disabled by default, 'make DO_USERDIRS=1' to enable it. o SDL2 support. Disabled by default, 'make USE_SDL2=1' to enable it. o Revised keyboard input code. o Revised/improved the 'game' command, i.e. on-the-fly mod changing. It now accepts an optional second argument for mission packs or quoth support i.e. -hipnotic, -rogue, or -quoth. For example, for WarpSpasm: "game warp -quoth" o Command line: "-game {quoth/hipnotic/rogue}" is now treated the same as -quoth, -hipnotic, or -rogue. o Console speed now resolution-independent. o Disabled gl_zfix, which caused glitches and is undesirable for new maps. Replacement .ent files to fix z-fighting for several id1 maps added to quakespasm.pak. o PF_VarString buffer bumped to 1024, avoids truncated centerprints from the 'In The Shadows' mod. o Support for opengl non-power-of-two-textures extension (disable with command line: "-notexturenpot".) o Support for OpenGL vertex buffer objects (VBO, OpenGL 1.5 or newer) for world and brush models (disable with command line: "-novbo".) o Antialiasing (FSAA) support (command line: -fsaa x, where x can be 0, 2, 4, 8). o Fence textures support. o Dynamic light speedup. Speedup loading of tga and pcx external images. o Brush model drawing speedup. o Support for BSP2 and 2PSB map formats. o Support for Opus, FLAC, and tracker music (S3M, IT, UMX, etc.), as compile-time options. o Music and sfx now mixed at 44100 Hz to avoid downsampling music. Low-pass filter applied to the sfx if -sndspeed is 11025 (the default), to preserve the same sound quality as 0.85.9. New -mixspeed option sets the rate for mixing sfx and music, and output to the OS (default 44100), setting it to 11025 reverts to 0.85.9 behaviour. New snd_filterquality cvar, value can be between 1 (emulate OS X resampler) and 5 (emulate Windows resampler), controls the sound of the low-pass filter. o Better Hor+ field of view (FOV) scaling behavior. o Better cross-map demo playback support. o Fix screenshots when screen width isn't a multiple of 4. o Fix a lighting glitch due to floating point precision. o Fix a looping sounds glitch. o Fix a vulnerability in file extension handling. Tighten path handling safety. o Initialize opengl with 24-bit depth buffer at 32 bpp. o Reset all models upon gamedir changes. (Fixes failures with mods using custom content.) o Fix broken behavior upon gamedir changes if -basedir is specified on the command line. o NET_MAXMESSAGE and MAX_MSGLEN limits bumped to 64000. o MAX_EFRAGS bumped to 4096, and MAX_CHANNELS to 1024. o MAX_ENT_LEAFS bumped from 16 to 32 to work around disappearing or flickering brush models in some situations. Also, if an entity is visible from MAX_ENT_LEAFS or more leafs, we now always send it to the client. o Fix cvar cycle command not working sometimes. o Host_Error upon missing models. (Prevents segmentation faults.) o Change sv_aim default value to 1 (i.e. turn off autoaim) o Add 'prev' and 'next' keywords to the 'cd' command. o Work around a linux cdrom issue (playback might not start for a while after a stop). o Quakespasm content customization moved from engine-embedded into a new optional quakespasm.pak file. o Version bumped to 0.90.0 (because Quakespasm has a decent life of it's own) o Other fixes and clean-ups. 6.7. Changes in 0.85.9 o Fixes for several undefined behaviors in C code (gcc-4.8 support.) o Implemented Hor+ style field of view (FOV) scaling, useful for widescreen resolutions. Configured by new cvar fov_adapt: set it to 1 and your fov will be scaled automatically according to the resolution. Enabled by default. o Adjusted string buffers for PR_ValueString and friends to fix crashes with excessively long global strings seen in some rude mods. o Toned down warning messages from PF_VarString() a bit. o Fixed Fitzquake's map existence check in changelevel (used to leak file handles which would end up in a Sys_Error() due to consuming all free handles if many maps reside not in pak files.) o Fixes/cleanups in chat mode handling. Client no longer gets stuck in chat mode upon disconnect. o Mouse grab/key_dest fixes and key cleanups. o The "speedkey" now acts as "slowkey" when "always run" is on. o Support for demo recording after connection to server. (thanks to Baker for a patch) o Corner case fixes in COM_Parse() for quoted strings and support for C-style /*..*/ comments. o Changed lightmaps to GL_RGBA instead of GL_RGB. o Better parse for opengl extensions list (from quakeforge.) o Vsync saving/loading fixes. o Fixed pointfile loading. o Multiple cleanups in gl_vidsdl.c. o Opus music decoding support (as an optional patch only.) o Several other minor fixes/cleanups. 6.8. Changes in 0.85.8 o Made Quake shareware 1.00 and 1.01 versions to be recognized properly. o Fixed control-character handling in unicode mode. Keyboard input tweaks. o Made the keypad keys to send separate key events in game mode. o Text pasting support from OS clipboard to console. (windows and macosx.) o Support for the Apple (Command) key on macosx. o Fixed increased (more than 32) dynamic lights. o Music playback: Made sure that the file's channels count is supported. o Support for Solaris. o Switched to using libmad instead of libmpg123 for MP3 playback on Mac OS X. o Better support for building the Mac OS X version using a makefile, support for cross-compiling on Linux. o Fixed a minor intermissions glitch. o Increased string buffer size from 256 to 384 for PF_VarString to work around broken mods such as UQC. o Restored original behavior for Quake registered version detection. o Minor demo recording/playback tweaks. o Minor tweaks to the scale menu option. o unbindall before loading stored bindings (configurable by new cvar cfg_unbindall, enabled by default.) o New icon. o Miscellaneous source code cleanups. 6.9. Changes in 0.85.7 o Added support for cross-level demo playback o gl_texturemode is reimplemented as a cvar with a callback and the setting is automatically saved to the config o Fixed execution of external files without a newline at the end o Reduced memory usage during reloading of textures o Fixed compilation on GNU/kFreeBSD (Debian bug #657793) o Fixed backspace key on Mac OS X o Disable mouse acceleration in Mac OS X o Worked around recursive calling of the anisotropic filter callback o Console word wrap and long input line fixes o Verified correct compilation by clang (using v3.0) o Several other small changes mostly invisible to the end-user 6.10. Changes in 0.85.6 o More work for string buffer safety o Reverted v0.85.5 change of not allowing deathmatch and coop cvars to be set at the same time (was reported for possibility of causing compatibility issues with mods) o Several cleanups/changes in the cvar layer o Minor SDL video fixes. 6.11. Changes in 0.85.5 o SDL input driver updated adding native keymap and dead key support to the console o Fixed a crash in net play in maps with extended limits o Verified successful compilation using gcc-4.6.x o Added workaround against GL texture flicker (z fighting), controlled by new cvar 'gl_zfix' o Read video variables early so that a vid_restart isn't necessary after init o mlook and lookspring fixes o Added support for loading external entity files, controlled by new cvar 'external_ents' o Made mp3 playback to allocate system memory instead of zone o Some updates to the progs interpreter code o Fixed r_nolerp_list parsing code of fitzquake o Made sure that deathmatch and coop are not set at the same time o Several code updates from uHexen2 project, several code cleanups. 6.12. Changes in 0.85.4 o Implement music (OGG, MP3, WAV) playback o A better fix for the infamous SV_TouchLinks problem, no more hard lockups with maps such as "whiteroom" o Add support for mouse buttons 4 and 5 o Fix the "unalias" console command o Restore the "screen size" menu item o Fixed an erroneous protocol check in the server code o Raised the default zone memory size to 384 kb o Raised the default max_edicts from 1024 to 2048 o Revised lit file loading, the lit file must be from the same game directory as the map itself or from a searchpath with a higher priority o Fixed rest of the compiler warnings o Other minor sound and cdaudio updates 6.13. Changes in 0.85.3 o Fix the "-dedicated" option (thanks Oz) and add platform specific networking code (default) rather than SDL_net o Much needed OSX framework stuff from Kristian o Add a persistent history feature (thanks Baker) o Add a slider for scr_sbaralpha, which now defaults to 0.95 (slightly transparent, allowing for a nicer status bar) o Allow player messages longer than 32 characters o Sockaddr fix for FreeBSD/OSX/etc networking o Connect status bar size to the scale slider o Include an ISNAN (is not-a-number) fix to catch the occassional quake C bug giving traceline problems o Enumerate options menus o Add a "prev weapon" menu item (from Sander) o Small fix to Sound Block/Unblock on win32 o Lots of code fixes (some from uhexen2) o Sys_Error calls Host_Shutdown o Added MS Visual Studio support o Add a "-cd" option to let the CD Player work in dedicated mode, and some other CD tweaks. 6.14. Changes in 0.85.2 o Replace the old "Screen size" slider with a "Scale" slider o Don't constantly open and close condebug log o Heap of C clean-ups o Fix mapname sorting o Alias the "mods" command to "games" o Block/Unblock sound upon focus loss/gain o NAT fix (networking protocol fix) o SDLNet_ResolveHost bug-fix allowing connection to ports other than 26000 o Bumped array size of sv_main.c::localmodels from 5 to 6 fixing an old fitzquake-0.85 bug which used to cause segfaults depending on the compiler. o Accept commandline options like "+connect ip:port" o Add OSX Makefile (tested?) 6.15. Changes in 0.85.1 o 64 bit CPU support o Restructured SDL sound driver o Custom conback o Tweaked the command line completion and added a map/changelevel autocompletion function o Alt+Enter toggles fullscreen o Disable Draw_BeginDisc which causes core dumps when called excessively o Show helpful info on start-up o Include real map name (sv.name) and skill in the status bar o Remove confirm quit dialog o Don't spam the console with PackFile seek requests o Default to window mode o Withdraw console when playing demos o Don't play demos on program init o Default Heapsize is 64meg o Changes to default console alpha, speed o Changes to cvar persistence gl_flashblend (default 0), r_shadow, r_wateralpha, r_dynamic, r_novis 7. Todo o Add uHexen2's first person camera (and menu item) o Native CD audio support (if desired). cd_sdl.c doesn't have proper volume controls and SDL2 doesn't support CD audio 8. Copyright o Quake and Quakespasm are released under the GNU GENERAL PUBLIC LICENSE Version 2: http://www.gnu.org/licenses/gpl-2.0.html o Quakespasm console background image by AAS, released under the CREATIVE COMMONS PUBLIC LICENSE: http://creativecommons.org/licenses/by/3.0/legalcode 9. Contact o QuakeSpasm Project page: http://sourceforge.net/projects/quakespasm o Bug reports: http://sourceforge.net/p/quakespasm/bugs/?source=navbar o Ozkan Eric Sander Stevenaaus 10. Links o QuakeSpasm Homepage: http://quakespasm.sourceforge.net o Downloads: http://quakespasm.sourceforge.net/download.htm o FitzQuake Homepage: http://www.celephais.net/fitzquake o Func Quakespasm forum: http://www.celephais.net/board/view_thread.php?id=60452 o Inside3D forums: http://forums.insideqc.com quakespasm-0.93.0/Linux/0000755000000000000000000000000013204512422013545 5ustar rootrootquakespasm-0.93.0/Linux/CodeBlocks/0000755000000000000000000000000013204512422015555 5ustar rootrootquakespasm-0.93.0/Linux/CodeBlocks/QuakeSpasm-SDL2.cbp0000644000000000000000000002440013136360611021024 0ustar rootroot quakespasm-0.93.0/Linux/CodeBlocks/QuakeSpasm.cbp0000644000000000000000000002430613136360611020327 0ustar rootroot quakespasm-0.93.0/Linux/sgml/0000755000000000000000000000000013204512422014507 5ustar rootrootquakespasm-0.93.0/Linux/sgml/Quakespasm.sgml0000644000000000000000000007133613203362342017522 0ustar rootroot
QuakeSpasm <toc> <verb></verb> <em>Page last edited: Nov. 2017</em> <sect> About <p> <url url="http://quakespasm.sourceforge.net" name="QuakeSpasm"> is a modern, cross-platform Quake 1 engine based on <url url="http://www.celephais.net/fitzquake" name="FitzQuake">. </p><p> It includes support for 64 bit CPUs and custom music playback, a new sound driver, some graphical niceities, and numerous bug-fixes and other improvements. </p><p> Quakespasm utilizes either the SDL or SDL2 frameworks, so choose which one works best for you. SDL is probably less buggy, but SDL2 has nicer features and smoother mouse input - though no CD support. <sect> Downloads <p> <itemize> <item><url url="http://quakespasm.sourceforge.net/download.htm" name="Project Downloads"> <item><url url="http://quakespasm.ericwa.com/job/quakespasm-sdl2/" name="Automatic Builds"> </itemize> <sect> Hints <p> <em>Visit the <url url="http://www.celephais.net/fitzquake" name="FitzQuake homepage"> for a full run-down of the engine's commands and variables.</em> <itemize> <item>To disable some changes, use "<bf>quakespasm -fitz</bf>" <item>Quakespasm's custom data is stored in "quakespasm.pak". Install this file alongside your id1 directory to enable the custom console background and other minor features. <item>For different sound backend drivers use "<bf>SDL_AUDIODRIVER=</bf><em>DRIVER</em><bf> ./quakespasm</bf>" , where DRIVER may be alsa, dsp, pulse, esd ... <item><bf>Shift+Escape</bf> draws the Console. <item>From the console, use <bf>UP</bf> to browse the command line history and <bf>TAB</bf> to autocomplete command and map names. <item>There is currently no CD Music volume support and SDL2 doesn't support CD audio. cd_sdl.c needs replacing with cd_linux.c, cd_bsd.c etc.. <item>In windows, alternative CD drives are accessible by "<bf>quakespasm -cddev F</bf>" (for example) <item>Quakespasm allows loading new games (mods) on the fly with "<bf>game</bf> <em>GAMENAME {-quoth/hipnotic/rogue}</em>" <item>Use "<bf>quakespasm -condebug</bf>" to save console log to "qconsole.log". SDL2 builds no longer generate stdout.txt/stderr.txt. </itemize> </p> <sect1>Music Playback<p> Quakespasm can play various external music formats, including MP3, OGG and FLAC. <itemize> <item>Tracks should be named like "track02.ogg", "track03.ogg" ... (there is no track01) and placed into "Quake/id1/music". <item>Unix users may need some extra libraries installed: "libmad" or "libmpg123" for MP3, and "libogg" and "libvorbis" for OGG. <item>As of 0.90.0, music is played back at 44100 Hz by default with no need to adjust "-sndspeed". <item>Use the "-noextmusic" option to disable this feature. <item>See <url url="Quakespasm-Music.txt"> for more details. </itemize> <sect1>Controller Support<p> The SDL2 variant of Quakespasm supports Xbox 360 style game controllers. <p> The default configuration uses the left analog stick for movement and the right for looking. <p> If your controller doesn't work you can try placing <url url="https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt" name="this file"> in your Quake directory, it is a community-maintained database that adds support for more controllers to SDL2. <sect2>Cvars<p> <itemize> <item>joy_deadzone - Fraction of the stick travel to be deadzone, between 0 and 1. Default 0.175. <item>joy_deadzone_trigger - Fraction of trigger range required to register a button press on the analog triggers, between 0 and 1. Default 0.2. <item>joy_sensitivity_yaw/pitch - Max angular speed in degrees/second when looking. Defaults are 300 for yaw (turning left/right) and 150 for pitch (up/down). <item>joy_exponent - For the look stick, the stick displacement (between 0 and 1) is raised to this power. Default is 3. A value of 1 would give a linear relationship between stick displacement and fraction of the maximum angular speed. <item>joy_invert - Set to 1 to invert the vertical axis of the look stick. <item>joy_swapmovelook - Set to 1 to swap the left and right analog stick functions. Default 0, move on the left stick, look on the right stick. <item>joy_enable - Set to 0 to disable controller support. Default 1. </itemize> <sect2>Buttons<p> Some of the controller buttons are hardcoded to allow navigating the menu: <itemize> <item>Back - alias for TAB <item>Start - alias for ESC <item>DPad, analog sticks - mapped to arrow keys <item>A Button - alias for ENTER in menus <item>B Button - alias for ESC in menus </itemize> These buttons can be bound normally: <itemize> <item>LTRIGGER - Left trigger <item>RTRIGGER - Right trigger <item>LSHOULDER - Left shoulder button <item>RSHOULDER - Right shoulder button <item>LTHUMB - Clicking the left thumbstick <item>RTHUMB - Clicking the right thumbstick <item>ABUTTON <item>BBUTTON <item>XBUTTON <item>YBUTTON </itemize> quakespasm.pak contains a default.cfg which has been updated to give some default bindings. L/R shoulder buttons are bound to weapon switching, and L/R triggers are jump and attack. <p> The controller support started as Jeremiah Sypult's implementation in Quakespasm-Rift and also uses ideas and code from LordHavoc (DarkPlaces). <sect> Compiling and Installation<p> <p>Quakespasm's (optional) custom data is now stored in the file <bf>quakespasm.pak</bf>. This file should be placed alongside your quakespasm binary and <bf>id1</bf> directory.</p> <p><em>To checkout the latest version of QuakeSpasm, do:</em> <bf>svn co svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm</bf></p> <sect1> Linux/Unix <p> After extracting the source tarball, browse the Makefile and edit the music streaming options, then <code> make cp quakespasm /usr/local/games/quake (for example) </code> <newline> Compile time options include <itemize> <item><bf>make DO_USERDIRS=1</bf> to include user directories support <item><bf>make DEBUG=1</bf> for debugging <item><bf>make SDL_CONFIG=</bf><em>/PATH/TO/SDL-CONFIG</em> for unusual SDL installations <item><bf>make USE_SDL2=1</bf> to compile against SDL2 instead of SDL-1.2 </itemize> <p>Streaming music playback requires "libmad" or "libmpg123" for MP3, and "libogg" and "libvorbis" for OGG files. <p>The project can also be built with Codeblocks (project files included).</p> <sect1> Windows <p> The QuakeSpasm developers cross-compile windows binaries using <url url="http://www.mingw.org" name="MinGW"> and <url url="http://mingw-w64.sf.net" name="Mingw-w64">. The project can also be built using Visual Studio 2005 (or newer).</p> <sect1> Mac OS X <p> A Quakespasm App (including program launcher and update framework) can be made using the <bf>Xcode</bf> template found in the MacOSX directory. Alternatively, have a look at <bf>Makefile.darwin</bf> for more instructions on building from a console. </p> <sect> Known Bugs <p> Brightness issues should be fixed with GLSL gamma in 0.90.1, if your system supports OpenGL 2. For reference on older systems: <newline> Some versions of Xorg and SDL have brightness issues. <newline> Try setting "export SDL_VIDEO_X11_NODIRECTCOLOR=1", or if you have Xorg >= 7.5 and broken brightness, these patched libSDL binaries may help. <itemize> <item><url url="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched.tgz/download" name="Gamma patched libSDL (i686-linux)"></li> <item><url url="http://sf.net/projects/quakespasm/files/Support%20Files/libSDL_gamma_patched-AMD64.tgz/download" name="Gamma patched libSDL (x86_64-linux)"> </itemize> <sect> Changes<p> <sect1> Changes in 0.93.0<p> <itemize> <item> Raise default "joy_deadzone_trigger" cvar to 0.2. <item> Raise console buffer size to 1MB. <item> Raise MAX_STATIC_ENTITIES from 512 to 4096. <item> Raise MAX_STACK_DEPTH from 32 to 64. <item> Raise command buffer size from 8K to 256K to support large configs. <item> Remove MAX_EFRAGS and MAX_MAP_LEAFS limits. <item> Remove "Loadgame buffer overflow" limit, which could happen when loading DP or QSS saves. <item> Adjust "exceeds standard limit of" debug warnings to include the actual QS limit. <item> Change "game" command to now exec quake.rc. <item> Change "games" / "mods" commands to list all subdirectories. <item> Restore vid_refreshrate from fitzquake-0.85 for SDL2 builds. <item> Alpha-masked model support. (MF_HOLEY: 0x4000). <item> Invalid skin index now draws skin 0 (WinQuake behaviour) instead of blue checkerboard. <item> Change default screenshot format to png. The 'screenshot' command now supports optional format (tga, png or jpg) and quality (1-100) arguments. <item> Revert "always run" changes from 0.85.9 and move the QuakeSpasm customizations to a new "cl_alwaysrun" cvar: Set to 1 in order to scale forward/side/up speed by "cl_movespeedkey" (usually 2), and to make "speedkey" act as "slowkey". <item> Change the "always run" menu option to offer three choices: <itemize> <item> off (cl_alwaysrun 0, cl_forwardspeed 200, cl_backspeed 200) <item> vanilla (cl_alwaysrun 0, cl_forwardspeed 400, cl_backspeed 400) <item> quakespasm (cl_alwaysrun 1, cl_forwardspeed 200, cl_backspeed 200) </itemize> <item> New "r_scale" cvar. Set to 2, 3, or 4 to render the view at 1/2, 1/3, or 1/4 resolution. <item> New "r_viewmodel_quake" cvar. Set to 1 for WinQuake gun position (from MarkV). <item> New "find" / "apropos" command, searches for commands/cvar names for the given substring (from Spike). <item> New "randmap" command for loading a random map. <item> New "gl_cshiftpercent_contents", "gl_cshiftpercent_damage", "gl_cshiftpercent_bonus", "gl_cshiftpercent_powerup" cvars for tuning the strength of specic view blends. <item> GL2 renderer: use a GLSL shader for world faces. Fixes reports of integrated+discrete GPU laptops having inconsistent fog rendering. <item> Fix macOS startup delay (avoid calling gethostbyname() for ".local" hostnames). <item> Fix memory corruption in PF_lightstyle with out of bounds lightstyles. <item> Fix crash in BoundPoly with polygons extending beyond +/-9999. <item> Fix QS window to stay on the current monitor when changing video modes (SDL2 only). <item> Fix possible freeze in SV_TouchLinks regardless of what QC does in the touch function. <item> Fix for maps with empty strings for vector keys (e.g. "origin"); don't read uninitialized memory. <item> Support for Open Watcom compiler. <item> Update the third-party libraries. </itemize> </p> <sect1> Changes in 0.92.1<p> <itemize> <item> Fixed large menu scale factors (was broken in 0.92.0). <item> Fixed PAUSE key (was broken in 0.92.0). <item> Updated some of the third-party libraries. </itemize> </p> <sect1> Changes in 0.92.0<p> <itemize> <item> SDL2 Game Controller support. <item> Contrast support with new "contrast" cvar, behaving the same as MarkV. It may be a useful alternative to the existing gamma control for laptops in a bright environment, etc. Raising contrast gives less of a gray/washed out look than raising gamma, but at a disadvantage: colors near white get clipped to white. <item> RMQ protocol (999) support, adapted from RMQEngine. <item> New "-protocol x" command line option. Accepted values for 'x' are 15 (NetQuake), 666 (FitzQuake, default), and 999 (RMQ). <item> New "setpos" console command. <item> New "vid_borderless" cvar for getting a borderless window. <item> Increased MAX_MAP_LEAFS from 65535 to 70000 and MAX_LIGHTMAPS from 256 to 512 in order to handle the oms3 map pack. <item> Server edicts are now allocated using malloc instead of allocating on the hunk. <item> gl_clear now defaults to 1. <item> Fix items falling out of the world on oms3.bsp on SSE builds. <item> Worked around an OSX 10.6 driver bug when using FSAA, which was leading to an unplayable HOM effect on the rest of the screen. <item> Fix wrong trace endpoint from the tracepos console command. <item> Updated some of the third-party libraries. Other fixes/clean-ups. </itemize> </p> <sect1> Changes in 0.91.0<p> <sect2> Bugfixes <itemize> <item> Fix unwanted fog mode change upon video restart. <item> Work around Intel 855 bug in status bar drawing with "r_oldwater 0" and "scr_sbaralpha 0". <item> Fix an obscure GLSL bug where changing gamma would result in the screen turning to noise. <item> Fix GLSL gamma causing the tiled screen border to turn white when "sizedown" is used. <item> Fix an alias model VBO renderer bug where a model not precached during map start wouldn't be drawn. <item> Fix the order of OpenGL context creation and window creation in SDL2 video. <item> Fix a calling convention issue in windows DPI awareness function pointers. <item> Fix a random texture recoloring after video mode change. <item> Fix a liquid turning to garbage after several video mode changes and "r_oldwater 0". <item> Fix a wrong alpha-sorting bug introduced in 0.90.1. <item> Fix "flush" command not reloading mdl's from disk (bug introduced in 0.90.1). <item> Prevent a possible buffer overflow in Cbuf_Execute (old Q1/Q2 bug). <item> Prevent a possible vulnerability in MSG_ReadString (old Q1/Q2 bug). </itemize> <sect2> Visual improvements <itemize> <item> New cvars r_lavaalpha, r_slimealpha, r_telealpha for fine-tuning specific liquid opacities (from DirectQ/RMQEngine, non-archived, default to 0), and new worldspawn keys _wateralpha, _lavaalpha, _slimealpha, _telealpha, _skyfog (unique to Quakespasm, similar to the behaviour of the "fog" worldspawn key). <item> GLSL gamma is now supported on older hardware without NPOT extension. </itemize> <sect2> Interface improvements <itemize> <item> New r_pos command to show player position. <item> NaN detection in traceline with "developer 1" set now warns instead of errors. </itemize> <sect2> Code cleanup / Other <itemize> <item> Update third-party libraries. </itemize> <sect2> Raised limits <itemize> <item> Default max_edicts 8192 (was 2048) and no longer saved to config.cfg. <item> Default heapsize 256 MB (was 64 MB). <item> Default zone 4 MB (was 384 KB). <item> Raised MAX_SFX to 1024 (was 512). </itemize> </p> <sect1> Changes in 0.90.1<p> <sect2> Bugfixes <itemize> <item> Fix dynamic light artifact where changing lightmap are rendered one frame late (bug introduced in 0.90.0). <item> Fix texture memory leak when changing video modes with SDL2. <item> Fix rare incorrect mdl lighting on 64-bit builds. <url url="http://forums.insideqc.com/viewtopic.php?f=3&t=5620" name="(details here.)"> <item> Fix fullbrights turning black after "kill" command (bug introduced in 0.90.0). <item> Clear all fog values on map change to prevent colored fog carrying over to jam3_tronyn.bsp. <item> Allow loading saves with } character in quoted strings, fixes issue with retrojam1_skacky.bsp. <item> Fix viewmodel not lerping on extended-limit maps. <item> Fix crash on out-of-bounds skin number. </itemize> <sect2> Performance <itemize> <item> Use multithreaded OpenGL on OS X for better performance. <item> New, faster mdl renderer using GLSL. Disable with "-noglslalias". </itemize> <sect2> Visual improvements <itemize> <item> New gamma correction implementation using GLSL. Fixes all known gamma issues (affecting the full display, persisting after quitting, or darkening the screen on OS X). Disable with "-noglslgamma". <item> Use high-quality water by default (r_oldwater 0). <item> Shadows use stencil buffer to avoid overlapping artifacts (from MarkV.) <item> r_noshadow_list cvar added (from MarkV.) </itemize> <sect2> Interface improvements <itemize> <item> Support pausing demo playback with the "pause" command. <item> Autocompletion for "game", "record", "playdemo". <item> Experimental windowed fullscreen mode available with vid_desktopfullscreen 1 (only in SDL2 builds, takes effect upon entering fullscreen mode the next time.) <item> Silence "exceeded standard limit" messages unless developer cvar is >= 1. <item> Some spam moved from developer 1 to 2: "can't find tga/lit/ent", "trying to load ent", "bad chunk length", "meshing", "PR_AlocStringSlots: realloc'ing" </itemize> <sect2> Code cleanup <itemize> <item> Clean up IDE project files to build on fresh systems. <item> Update 3rd-party libraries. </itemize> </p> <sect1> Changes in 0.90.0<p> <itemize> <item> Fix issues on Windows systems with DPI scaling.</item> <item> Unix/Mac user directories support. Disabled by default, 'make DO_USERDIRS=1' to enable it. <item> SDL2 support. Disabled by default, 'make USE_SDL2=1' to enable it. <item> Revised keyboard input code. <item> Revised/improved the 'game' command, i.e. on-the-fly mod changing. It now accepts an optional second argument for mission packs or quoth support i.e. -hipnotic, -rogue, or -quoth. For example, for WarpSpasm: "game warp -quoth" <item> Command line: "-game {quoth/hipnotic/rogue}" is now treated the same as -quoth, -hipnotic, or -rogue. <item> Console speed now resolution-independent. <item> Disabled gl_zfix, which caused glitches and is undesirable for new maps. Replacement .ent files to fix z-fighting for several id1 maps added to quakespasm.pak. <item> PF_VarString buffer bumped to 1024, avoids truncated centerprints from the 'In The Shadows' mod. <item> Support for opengl non-power-of-two-textures extension (disable with command line: "-notexturenpot".) <item> Support for OpenGL vertex buffer objects (VBO, OpenGL 1.5 or newer) for world and brush models (disable with command line: "-novbo".) <item> Antialiasing (FSAA) support (command line: -fsaa x, where x can be 0, 2, 4, 8). <item> Fence textures support. <item> Dynamic light speedup. Speedup loading of tga and pcx external images. <item> Brush model drawing speedup. <item> Support for BSP2 and 2PSB map formats. <item> Support for Opus, FLAC, and tracker music (S3M, IT, UMX, etc.), as compile-time options. <item> Music and sfx now mixed at 44100 Hz to avoid downsampling music. Low-pass filter applied to the sfx if -sndspeed is 11025 (the default), to preserve the same sound quality as 0.85.9. New -mixspeed option sets the rate for mixing sfx and music, and output to the OS (default 44100), setting it to 11025 reverts to 0.85.9 behaviour. New snd_filterquality cvar, value can be between 1 (emulate OS X resampler) and 5 (emulate Windows resampler), controls the sound of the low-pass filter. <item> Better Hor+ field of view (FOV) scaling behavior. <item> Better cross-map demo playback support. <item> Fix screenshots when screen width isn't a multiple of 4. <item> Fix a lighting glitch due to floating point precision. <item> Fix a looping sounds glitch. <item> Fix a vulnerability in file extension handling. Tighten path handling safety. <item> Initialize opengl with 24-bit depth buffer at 32 bpp. <item> Reset all models upon gamedir changes. (Fixes failures with mods using custom content.) <item> Fix broken behavior upon gamedir changes if -basedir is specified on the command line. <item> NET_MAXMESSAGE and MAX_MSGLEN limits bumped to 64000. <item> MAX_EFRAGS bumped to 4096, and MAX_CHANNELS to 1024. <item> MAX_ENT_LEAFS bumped from 16 to 32 to work around disappearing or flickering brush models in some situations. Also, if an entity is visible from MAX_ENT_LEAFS or more leafs, we now always send it to the client. <item> Fix cvar cycle command not working sometimes. <item> Host_Error upon missing models. (Prevents segmentation faults.) <item> Change sv_aim default value to 1 (i.e. turn off autoaim) <item> Add 'prev' and 'next' keywords to the 'cd' command. <item> Work around a linux cdrom issue (playback might not start for a while after a stop). <item> Quakespasm content customization moved from engine-embedded into a new optional quakespasm.pak file. <item> Version bumped to 0.90.0 (because Quakespasm has a decent life of it's own) <item> Other fixes and clean-ups. </itemize> </p> <sect1> Changes in 0.85.9<p> <itemize> <item> Fixes for several undefined behaviors in C code (gcc-4.8 support.) <item> Implemented Hor+ style field of view (FOV) scaling, useful for widescreen resolutions. Configured by new cvar fov_adapt: set it to 1 and your fov will be scaled automatically according to the resolution. Enabled by default. <item> Adjusted string buffers for PR_ValueString and friends to fix crashes with excessively long global strings seen in some rude mods. <item> Toned down warning messages from PF_VarString() a bit. <item> Fixed Fitzquake's map existence check in changelevel (used to leak file handles which would end up in a Sys_Error() due to consuming all free handles if many maps reside not in pak files.) <item> Fixes/cleanups in chat mode handling. Client no longer gets stuck in chat mode upon disconnect. <item> Mouse grab/key_dest fixes and key cleanups. <item> The "speedkey" now acts as "slowkey" when "always run" is on. <item> Support for demo recording after connection to server. (thanks to Baker for a patch) <item> Corner case fixes in COM_Parse() for quoted strings and support for C-style /*..*/ comments. <item> Changed lightmaps to GL_RGBA instead of GL_RGB. <item> Better parse for opengl extensions list (from quakeforge.) <item> Vsync saving/loading fixes. <item> Fixed pointfile loading. <item> Multiple cleanups in gl_vidsdl.c. <item> Opus music decoding support (as an optional patch only.) <item> Several other minor fixes/cleanups. </itemize> </p> <sect1> Changes in 0.85.8<p> <itemize> <item> Made Quake shareware 1.00 and 1.01 versions to be recognized properly. <item> Fixed control-character handling in unicode mode. Keyboard input tweaks. <item> Made the keypad keys to send separate key events in game mode. <item> Text pasting support from OS clipboard to console. (windows and macosx.) <item> Support for the Apple (Command) key on macosx. <item> Fixed increased (more than 32) dynamic lights. <item> Music playback: Made sure that the file's channels count is supported. <item> Support for Solaris. <item> Switched to using libmad instead of libmpg123 for MP3 playback on Mac OS X. <item> Better support for building the Mac OS X version using a makefile, support for cross-compiling on Linux. <item> Fixed a minor intermissions glitch. <item> Increased string buffer size from 256 to 384 for PF_VarString to work around broken mods such as UQC. <item> Restored original behavior for Quake registered version detection. <item> Minor demo recording/playback tweaks. <item> Minor tweaks to the scale menu option. <item> unbindall before loading stored bindings (configurable by new cvar cfg_unbindall, enabled by default.) <item> New icon. <item> Miscellaneous source code cleanups. </itemize> </p> <sect1> Changes in 0.85.7<p> <itemize> <item> Added support for cross-level demo playback <item> gl_texturemode is reimplemented as a cvar with a callback and the setting is automatically saved to the config <item> Fixed execution of external files without a newline at the end <item> Reduced memory usage during reloading of textures <item> Fixed compilation on GNU/kFreeBSD (Debian bug #657793) <item> Fixed backspace key on Mac OS X <item> Disable mouse acceleration in Mac OS X <item> Worked around recursive calling of the anisotropic filter callback <item> Console word wrap and long input line fixes <item> Verified correct compilation by clang (using v3.0) <item> Several other small changes mostly invisible to the end-user </itemize> </p> <sect1> Changes in 0.85.6<p> <itemize> <item> More work for string buffer safety <item> Reverted v0.85.5 change of not allowing deathmatch and coop cvars to be set at the same time (was reported for possibility of causing compatibility issues with mods) <item> Several cleanups/changes in the cvar layer <item> Minor SDL video fixes. </itemize> </p> <sect1> Changes in 0.85.5<p> <itemize> <item> SDL input driver updated adding native keymap and dead key support to the console <item> Fixed a crash in net play in maps with extended limits <item> Verified successful compilation using gcc-4.6.x <item> Added workaround against GL texture flicker (z fighting), controlled by new cvar 'gl_zfix' <item> Read video variables early so that a vid_restart isn't necessary after init <item> mlook and lookspring fixes <item> Added support for loading external entity files, controlled by new cvar 'external_ents' <item> Made mp3 playback to allocate system memory instead of zone <item> Some updates to the progs interpreter code <item> Fixed r_nolerp_list parsing code of fitzquake <item> Made sure that deathmatch and coop are not set at the same time <item> Several code updates from uHexen2 project, several code cleanups. </itemize> </p> <sect1> Changes in 0.85.4<p> <itemize> <item> Implement music (OGG, MP3, WAV) playback <item> A better fix for the infamous SV_TouchLinks problem, no more hard lockups with maps such as "whiteroom" <item> Add support for mouse buttons 4 and 5 <item> Fix the "unalias" console command <item> Restore the "screen size" menu item <item> Fixed an erroneous protocol check in the server code <item> Raised the default zone memory size to 384 kb <item> Raised the default max_edicts from 1024 to 2048 <item> Revised lit file loading, the lit file must be from the same game directory as the map itself or from a searchpath with a higher priority <item> Fixed rest of the compiler warnings <item> Other minor sound and cdaudio updates </itemize> </p> <sect1> Changes in 0.85.3<p> <itemize> <item> Fix the "-dedicated" option (thanks Oz) and add platform specific networking code (default) rather than SDL_net <item> Much needed OSX framework stuff from Kristian <item> Add a persistent history feature (thanks Baker) <item> Add a slider for scr_sbaralpha, which now defaults to 0.95 (slightly transparent, allowing for a nicer status bar) <item> Allow player messages longer than 32 characters <item> Sockaddr fix for FreeBSD/OSX/etc networking <item> Connect status bar size to the scale slider <item> Include an ISNAN (is not-a-number) fix to catch the occassional quake C bug giving traceline problems <item> Enumerate options menus <item> Add a "prev weapon" menu item (from Sander) <item> Small fix to Sound Block/Unblock on win32 <item> Lots of code fixes (some from uhexen2) <item> Sys_Error calls Host_Shutdown <item> Added MS Visual Studio support <item> Add a "-cd" option to let the CD Player work in dedicated mode, and some other CD tweaks. </itemize> <sect1> Changes in 0.85.2<p> <itemize> <item> Replace the old "Screen size" slider with a "Scale" slider <item> Don't constantly open and close condebug log <item> Heap of C clean-ups <item> Fix mapname sorting <item> Alias the "mods" command to "games" <item> Block/Unblock sound upon focus loss/gain <item> NAT fix (networking protocol fix) <item> SDLNet_ResolveHost bug-fix allowing connection to ports other than 26000 <item> Bumped array size of sv_main.c::localmodels from 5 to 6 fixing an old fitzquake-0.85 bug which used to cause segfaults depending on the compiler. <item> Accept commandline options like "+connect ip:port" <item> Add OSX Makefile (tested?) </itemize> <sect1> Changes in 0.85.1<p> <itemize> <item>64 bit CPU support <item>Restructured SDL sound driver <item>Custom conback <item>Tweaked the command line completion and added a map/changelevel autocompletion function <item>Alt+Enter toggles fullscreen <item>Disable Draw_BeginDisc which causes core dumps when called excessively <item>Show helpful info on start-up <item>Include real map name (sv.name) and skill in the status bar <item>Remove confirm quit dialog <item>Don't spam the console with PackFile seek requests <item>Default to window mode <item>Withdraw console when playing demos <item>Don't play demos on program init <item>Default Heapsize is 64meg <item>Changes to default console alpha, speed <item>Changes to cvar persistence gl_flashblend (default 0), r_shadow, r_wateralpha, r_dynamic, r_novis </itemize> <sect> Todo <p> <itemize> <item>Add uHexen2's first person camera (and menu item) <item>Native CD audio support (if desired). cd_sdl.c doesn't have proper volume controls and SDL2 doesn't support CD audio </itemize> <sect> Copyright <p> <itemize> <item>Quake and Quakespasm are released under the <url url="http://www.gnu.org/licenses/gpl-2.0.html" name="GNU GENERAL PUBLIC LICENSE Version 2"> <item>Quakespasm console background image by <bf>AAS</bf>, released under the <url url="http://creativecommons.org/licenses/by/3.0/legalcode" name="CREATIVE COMMONS PUBLIC LICENSE"> </itemize> <sect> Contact <p> <itemize> <item><url url="http://sourceforge.net/projects/quakespasm" name="QuakeSpasm Project page"> <item><url url="http://sourceforge.net/p/quakespasm/bugs/?source=navbar" name="Bug reports"> <item><url url="mailto:gmail - dot - com - username - sezeroz" name="Ozkan">, <url url="mailto:gmail - dot - com - username - ewasylishen" name="Eric">, <url url="mailto:gmail - dot - com - username - a.h.vandijk" name="Sander">, <url url="mailto:yahoo - dot - com - username - stevenaaus" name="Stevenaaus"> </itemize> <sect> Links <p> <itemize> <item><url url="http://quakespasm.sourceforge.net" name="QuakeSpasm Homepage"> <item><url url="http://quakespasm.sourceforge.net/download.htm" name="Downloads"> <item><url url="http://www.celephais.net/fitzquake" name="FitzQuake Homepage"> <item><url url="http://www.celephais.net/board/view_thread.php?id=60452" name="Func Quakespasm forum"> <item><url url="http://forums.insideqc.com" name="Inside3D forums"> </itemize> </article> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Linux/sgml/sgml2rawtxt������������������������������������������������������������0000755�0000000�0000000�00000000152�12257116274�016745� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash sgml2txt -b 2 "$1" cat "$1.txt" | sed -e 's/[^m]*m//g' -e 's/.//g' >tmp$$ mv tmp$$ "$1.txt" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Linux/sgml/Makefile.sgml����������������������������������������������������������0000644�0000000�0000000�00000000266�12403446630�017123� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# sgml tools are from linuxdoc-tools default: Quakespasm.sgml sgml2html -s 0 -T 2 Quakespasm.sgml 2>/dev/null all: default text text txt: Quakespasm.sgml ./sgml2rawtxt Quakespasm ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/LICENSE.txt�����������������������������������������������������������������������0000644�0000000�0000000�00000043254�12407555166�014321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quakespasm-Music.txt��������������������������������������������������������������0000644�0000000�0000000�00000006346�12405526374�016425� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������QuakeSpasm supports ogg, mp3 and wav external music files to be played instead of cd music. What you should do is simple: 1. Use your favorite cd-ripper application and rip your quake cdrom audio tracks, convert them to ogg or mp3 so they occupy less space, like track02.ogg, track03.ogg, etc. Note #1: There is no such thing as track01: the first tracks of original Quake and the mission pack cdroms are always data tracks. Note #2: Since the Quake cdrom audio tracks are pre-emphasized, you should de-emphasize them during or after ripping to make them sound right. (thanks to Sander van Dijk for this note) 2. Go into your quake installation directory and create a new directory id1/music (for windows users id1\music). 3. If you have the Scourge of Armagon mission pack, then create another directory hipnotic/music . If you have the Dissolution of Eternity then create another directory rogue/music . 4. Take the ripped music files from step1, place them under id1/music. If you have the mission packs, repeat step1 for the mission packs, too, and place the ripped music files under hipnotic/music for the first mission pack or under rogue/music for the second mission pack. 5. All are ready to go: When a level starts, the engine will first try playing the necessary cdaudio track and if it doesn't find the cdrom it will use the ripped music files instead. New console commands: --------------------- - music <filename> Start playing the requested music file. Example: music mymusic1 Notice that you don't have to type the file extension: The requested music will be searched with ogg, mp3, and then with a wav extension, automatically. If you do specify the file extension, like "music mymusic1.wav", then it will honor your wish and try only the given type: this is good for testing/comparing the same music in different formats. - music_stop Stops the playing music - music_pause Pauses the playing music - music_resume Resumes playing the music if it was paused - music_loop 1 Makes the background music to loop (default behavior) - music_loop 0 Makes the background music to play once and then stop New cvars: ------------------------- - bgm_extmusic (0 or 1): Disable or enable playback of external music files instead of cdaudio. default is 1 (enabled). New command line options: ------------------------- - -noextmusic: Disables the playback of external music files instead of cdaudio. Music files in PAK files: ------------------------- PAK-contained music files are fully supported. Music file directories: ------------------------- - The music files are always searched under the "music" subdirectory of a game. Music file search order: ------------------------- The engine can handle multiple audio formats. The map-dictated music, i.e. the ripped cd music, is always searched by the order of searchpath priority: the file from the searchpath with the highest priority is chosen, because it is most likely the one from our own game directory itself. This way, if a mod has track02 as a mp3 or wav, which is below track02.ogg in the music_handler order, the mp3 or wav will still have priority over track02.ogg from the id1 game directory. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/����������������������������������������������������������������������������0000755�0000000�0000000�00000000000�13204512422�013514� 5����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_warp_sin.h���������������������������������������������������������������0000644�0000000�0000000�00000006523�12407762022�016206� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gl_warp_sin.h * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pl_win.c��������������������������������������������������������������������0000644�0000000�0000000�00000005326�12407762022�015165� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include <windows.h> #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #include <SDL2/SDL_syswm.h> #else #include <SDL/SDL.h> #include <SDL/SDL_syswm.h> #endif #else #include "SDL.h" #include "SDL_syswm.h" #endif static HICON icon; void PL_SetWindowIcon (void) { HINSTANCE handle; SDL_SysWMinfo wminfo; HWND hwnd; handle = GetModuleHandle(NULL); icon = LoadIcon(handle, "icon"); if (!icon) return; /* no icon in the exe */ SDL_VERSION(&wminfo.version); #if defined(USE_SDL2) if (SDL_GetWindowWMInfo((SDL_Window*) VID_GetWindow(), &wminfo) != SDL_TRUE) return; /* wrong SDL version */ hwnd = wminfo.info.win.window; #else if (SDL_GetWMInfo(&wminfo) != 1) return; /* wrong SDL version */ hwnd = wminfo.window; #endif #ifdef _WIN64 SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR) icon); #else SetClassLong(hwnd, GCL_HICON, (LONG) icon); #endif } void PL_VID_Shutdown (void) { DestroyIcon(icon); } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *PL_GetClipboardData (void) { char *data = NULL; char *cliptext; if (OpenClipboard(NULL) != 0) { HANDLE hClipboardData; if ((hClipboardData = GetClipboardData(CF_TEXT)) != NULL) { cliptext = (char *) GlobalLock(hClipboardData); if (cliptext != NULL) { size_t size = GlobalSize(hClipboardData) + 1; /* this is intended for simple small text copies * such as an ip address, etc: do chop the size * here, otherwise we may experience Z_Malloc() * failures and all other not-oh-so-fun stuff. */ size = q_min(MAX_CLIPBOARDTXT, size); data = (char *) Z_Malloc(size); q_strlcpy (data, cliptext, size); GlobalUnlock (hClipboardData); } } CloseClipboard (); } return data; } void PL_ErrorDialog(const char *errorMsg) { MessageBox (NULL, errorMsg, "Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/menu.c����������������������������������������������������������������������0000644�0000000�0000000�00000156440�13147214764�014654� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "bgmusic.h" void (*vid_menucmdfn)(void); //johnfitz void (*vid_menudrawfn)(void); void (*vid_menukeyfn)(int key); enum m_state_e m_state; void M_Menu_Main_f (void); void M_Menu_SinglePlayer_f (void); void M_Menu_Load_f (void); void M_Menu_Save_f (void); void M_Menu_MultiPlayer_f (void); void M_Menu_Setup_f (void); void M_Menu_Net_f (void); void M_Menu_LanConfig_f (void); void M_Menu_GameOptions_f (void); void M_Menu_Search_f (void); void M_Menu_ServerList_f (void); void M_Menu_Options_f (void); void M_Menu_Keys_f (void); void M_Menu_Video_f (void); void M_Menu_Help_f (void); void M_Menu_Quit_f (void); void M_Main_Draw (void); void M_SinglePlayer_Draw (void); void M_Load_Draw (void); void M_Save_Draw (void); void M_MultiPlayer_Draw (void); void M_Setup_Draw (void); void M_Net_Draw (void); void M_LanConfig_Draw (void); void M_GameOptions_Draw (void); void M_Search_Draw (void); void M_ServerList_Draw (void); void M_Options_Draw (void); void M_Keys_Draw (void); void M_Video_Draw (void); void M_Help_Draw (void); void M_Quit_Draw (void); void M_Main_Key (int key); void M_SinglePlayer_Key (int key); void M_Load_Key (int key); void M_Save_Key (int key); void M_MultiPlayer_Key (int key); void M_Setup_Key (int key); void M_Net_Key (int key); void M_LanConfig_Key (int key); void M_GameOptions_Key (int key); void M_Search_Key (int key); void M_ServerList_Key (int key); void M_Options_Key (int key); void M_Keys_Key (int key); void M_Video_Key (int key); void M_Help_Key (int key); void M_Quit_Key (int key); qboolean m_entersound; // play after drawing a frame, so caching // won't disrupt the sound qboolean m_recursiveDraw; enum m_state_e m_return_state; qboolean m_return_onerror; char m_return_reason [32]; #define StartingGame (m_multiplayer_cursor == 1) #define JoiningGame (m_multiplayer_cursor == 0) #define IPXConfig (m_net_cursor == 0) #define TCPIPConfig (m_net_cursor == 1) void M_ConfigureNetSubsystem(void); /* ================ M_DrawCharacter Draws one solid graphics character ================ */ void M_DrawCharacter (int cx, int line, int num) { Draw_Character (cx, line, num); } void M_Print (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, (*str)+128); str++; cx += 8; } } void M_PrintWhite (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, *str); str++; cx += 8; } } void M_DrawTransPic (int x, int y, qpic_t *pic) { Draw_Pic (x, y, pic); //johnfitz -- simplified becuase centering is handled elsewhere } void M_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x, y, pic); //johnfitz -- simplified becuase centering is handled elsewhere } void M_DrawTransPicTranslate (int x, int y, qpic_t *pic, int top, int bottom) //johnfitz -- more parameters { Draw_TransPicTranslate (x, y, pic, top, bottom); //johnfitz -- simplified becuase centering is handled elsewhere } void M_DrawTextBox (int x, int y, int width, int lines) { qpic_t *p; int cx, cy; int n; // draw left side cx = x; cy = y; p = Draw_CachePic ("gfx/box_tl.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_ml.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bl.lmp"); M_DrawTransPic (cx, cy+8, p); // draw middle cx += 8; while (width > 0) { cy = y; p = Draw_CachePic ("gfx/box_tm.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mm.lmp"); for (n = 0; n < lines; n++) { cy += 8; if (n == 1) p = Draw_CachePic ("gfx/box_mm2.lmp"); M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bm.lmp"); M_DrawTransPic (cx, cy+8, p); width -= 2; cx += 16; } // draw right side cy = y; p = Draw_CachePic ("gfx/box_tr.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mr.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_br.lmp"); M_DrawTransPic (cx, cy+8, p); } //============================================================================= int m_save_demonum; /* ================ M_ToggleMenu_f ================ */ void M_ToggleMenu_f (void) { m_entersound = true; if (key_dest == key_menu) { if (m_state != m_main) { M_Menu_Main_f (); return; } IN_Activate(); key_dest = key_game; m_state = m_none; return; } if (key_dest == key_console) { Con_ToggleConsole_f (); } else { M_Menu_Main_f (); } } //============================================================================= /* MAIN MENU */ int m_main_cursor; #define MAIN_ITEMS 5 void M_Menu_Main_f (void) { if (key_dest != key_menu) { m_save_demonum = cls.demonum; cls.demonum = -1; } IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_main; m_entersound = true; } void M_Main_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/ttl_main.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp") ); f = (int)(realtime * 10)%6; M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); } void M_Main_Key (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: IN_Activate(); key_dest = key_game; m_state = m_none; cls.demonum = m_save_demonum; if (!fitzmode) /* QuakeSpasm customization: */ break; if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected) CL_NextDemo (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_main_cursor >= MAIN_ITEMS) m_main_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_main_cursor < 0) m_main_cursor = MAIN_ITEMS - 1; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; switch (m_main_cursor) { case 0: M_Menu_SinglePlayer_f (); break; case 1: M_Menu_MultiPlayer_f (); break; case 2: M_Menu_Options_f (); break; case 3: M_Menu_Help_f (); break; case 4: M_Menu_Quit_f (); break; } } } //============================================================================= /* SINGLE PLAYER MENU */ int m_singleplayer_cursor; #define SINGLEPLAYER_ITEMS 3 void M_Menu_SinglePlayer_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_singleplayer; m_entersound = true; } void M_SinglePlayer_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/ttl_sgl.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") ); f = (int)(realtime * 10)%6; M_DrawTransPic (54, 32 + m_singleplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); } void M_SinglePlayer_Key (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS) m_singleplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_singleplayer_cursor < 0) m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; switch (m_singleplayer_cursor) { case 0: if (sv.active) if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n", 0.0f)) break; IN_Activate(); key_dest = key_game; if (sv.active) Cbuf_AddText ("disconnect\n"); Cbuf_AddText ("maxplayers 1\n"); Cbuf_AddText ("deathmatch 0\n"); //johnfitz Cbuf_AddText ("coop 0\n"); //johnfitz Cbuf_AddText ("map start\n"); break; case 1: M_Menu_Load_f (); break; case 2: M_Menu_Save_f (); break; } } } //============================================================================= /* LOAD/SAVE MENU */ int load_cursor; // 0 < load_cursor < MAX_SAVEGAMES #define MAX_SAVEGAMES 20 /* johnfitz -- increased from 12 */ char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1]; int loadable[MAX_SAVEGAMES]; void M_ScanSaves (void) { int i, j; char name[MAX_OSPATH]; FILE *f; int version; for (i = 0; i < MAX_SAVEGAMES; i++) { strcpy (m_filenames[i], "--- UNUSED SLOT ---"); loadable[i] = false; q_snprintf (name, sizeof(name), "%s/s%i.sav", com_gamedir, i); f = fopen (name, "r"); if (!f) continue; fscanf (f, "%i\n", &version); fscanf (f, "%79s\n", name); strncpy (m_filenames[i], name, sizeof(m_filenames[i])-1); // change _ back to space for (j = 0; j < SAVEGAME_COMMENT_LENGTH; j++) { if (m_filenames[i][j] == '_') m_filenames[i][j] = ' '; } loadable[i] = true; fclose (f); } } void M_Menu_Load_f (void) { m_entersound = true; m_state = m_load; IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; M_ScanSaves (); } void M_Menu_Save_f (void) { if (!sv.active) return; if (cl.intermission) return; if (svs.maxclients != 1) return; m_entersound = true; m_state = m_save; IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; M_ScanSaves (); } void M_Load_Draw (void) { int i; qpic_t *p; p = Draw_CachePic ("gfx/p_load.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); for (i = 0; i < MAX_SAVEGAMES; i++) M_Print (16, 32 + 8*i, m_filenames[i]); // line cursor M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1)); } void M_Save_Draw (void) { int i; qpic_t *p; p = Draw_CachePic ("gfx/p_save.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); for (i = 0; i < MAX_SAVEGAMES; i++) M_Print (16, 32 + 8*i, m_filenames[i]); // line cursor M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1)); } void M_Load_Key (int k) { switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_SinglePlayer_f (); break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); if (!loadable[load_cursor]) return; m_state = m_none; IN_Activate(); key_dest = key_game; // Host_Loadgame_f can't bring up the loading plaque because too much // stack space has been used, so do it now SCR_BeginLoadingPlaque (); // issue the load command Cbuf_AddText (va ("load s%i\n", load_cursor) ); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } void M_Save_Key (int k) { switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_SinglePlayer_f (); break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_state = m_none; IN_Activate(); key_dest = key_game; Cbuf_AddText (va("save s%i\n", load_cursor)); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } //============================================================================= /* MULTIPLAYER MENU */ int m_multiplayer_cursor; #define MULTIPLAYER_ITEMS 3 void M_Menu_MultiPlayer_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_multiplayer; m_entersound = true; } void M_MultiPlayer_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") ); f = (int)(realtime * 10)%6; M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); if (ipxAvailable || tcpipAvailable) return; M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available"); } void M_MultiPlayer_Key (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) m_multiplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_multiplayer_cursor < 0) m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; switch (m_multiplayer_cursor) { case 0: if (ipxAvailable || tcpipAvailable) M_Menu_Net_f (); break; case 1: if (ipxAvailable || tcpipAvailable) M_Menu_Net_f (); break; case 2: M_Menu_Setup_f (); break; } } } //============================================================================= /* SETUP MENU */ int setup_cursor = 4; int setup_cursor_table[] = {40, 56, 80, 104, 140}; char setup_hostname[16]; char setup_myname[16]; int setup_oldtop; int setup_oldbottom; int setup_top; int setup_bottom; #define NUM_SETUP_CMDS 5 void M_Menu_Setup_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_setup; m_entersound = true; Q_strcpy(setup_myname, cl_name.string); Q_strcpy(setup_hostname, hostname.string); setup_top = setup_oldtop = ((int)cl_color.value) >> 4; setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15; } void M_Setup_Draw (void) { qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_Print (64, 40, "Hostname"); M_DrawTextBox (160, 32, 16, 1); M_Print (168, 40, setup_hostname); M_Print (64, 56, "Your name"); M_DrawTextBox (160, 48, 16, 1); M_Print (168, 56, setup_myname); M_Print (64, 80, "Shirt color"); M_Print (64, 104, "Pants color"); M_DrawTextBox (64, 140-8, 14, 1); M_Print (72, 140, "Accept Changes"); p = Draw_CachePic ("gfx/bigbox.lmp"); M_DrawTransPic (160, 64, p); p = Draw_CachePic ("gfx/menuplyr.lmp"); M_DrawTransPicTranslate (172, 72, p, setup_top, setup_bottom); M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); if (setup_cursor == 0) M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); if (setup_cursor == 1) M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); } void M_Setup_Key (int k) { switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_MultiPlayer_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); setup_cursor--; if (setup_cursor < 0) setup_cursor = NUM_SETUP_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); setup_cursor++; if (setup_cursor >= NUM_SETUP_CMDS) setup_cursor = 0; break; case K_LEFTARROW: if (setup_cursor < 2) return; S_LocalSound ("misc/menu3.wav"); if (setup_cursor == 2) setup_top = setup_top - 1; if (setup_cursor == 3) setup_bottom = setup_bottom - 1; break; case K_RIGHTARROW: if (setup_cursor < 2) return; forward: S_LocalSound ("misc/menu3.wav"); if (setup_cursor == 2) setup_top = setup_top + 1; if (setup_cursor == 3) setup_bottom = setup_bottom + 1; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: if (setup_cursor == 0 || setup_cursor == 1) return; if (setup_cursor == 2 || setup_cursor == 3) goto forward; // setup_cursor == 4 (OK) if (Q_strcmp(cl_name.string, setup_myname) != 0) Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); if (Q_strcmp(hostname.string, setup_hostname) != 0) Cvar_Set("hostname", setup_hostname); if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom) Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) ); m_entersound = true; M_Menu_MultiPlayer_f (); break; case K_BACKSPACE: if (setup_cursor == 0) { if (strlen(setup_hostname)) setup_hostname[strlen(setup_hostname)-1] = 0; } if (setup_cursor == 1) { if (strlen(setup_myname)) setup_myname[strlen(setup_myname)-1] = 0; } break; } if (setup_top > 13) setup_top = 0; if (setup_top < 0) setup_top = 13; if (setup_bottom > 13) setup_bottom = 0; if (setup_bottom < 0) setup_bottom = 13; } void M_Setup_Char (int k) { int l; switch (setup_cursor) { case 0: l = strlen(setup_hostname); if (l < 15) { setup_hostname[l+1] = 0; setup_hostname[l] = k; } break; case 1: l = strlen(setup_myname); if (l < 15) { setup_myname[l+1] = 0; setup_myname[l] = k; } break; } } qboolean M_Setup_TextEntry (void) { return (setup_cursor == 0 || setup_cursor == 1); } //============================================================================= /* NET MENU */ int m_net_cursor; int m_net_items; const char *net_helpMessage [] = { /* .........1.........2.... */ " Novell network LANs ", " or Windows 95 DOS-box. ", " ", "(LAN=Local Area Network)", " Commonly used to play ", " over the Internet, but ", " also used on a Local ", " Area Network. " }; void M_Menu_Net_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_net; m_entersound = true; m_net_items = 2; if (m_net_cursor >= m_net_items) m_net_cursor = 0; m_net_cursor--; M_Net_Key (K_DOWNARROW); } void M_Net_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); f = 32; if (ipxAvailable) p = Draw_CachePic ("gfx/netmen3.lmp"); else p = Draw_CachePic ("gfx/dim_ipx.lmp"); M_DrawTransPic (72, f, p); f += 19; if (tcpipAvailable) p = Draw_CachePic ("gfx/netmen4.lmp"); else p = Draw_CachePic ("gfx/dim_tcp.lmp"); M_DrawTransPic (72, f, p); f = (320-26*8)/2; M_DrawTextBox (f, 96, 24, 4); f += 8; M_Print (f, 104, net_helpMessage[m_net_cursor*4+0]); M_Print (f, 112, net_helpMessage[m_net_cursor*4+1]); M_Print (f, 120, net_helpMessage[m_net_cursor*4+2]); M_Print (f, 128, net_helpMessage[m_net_cursor*4+3]); f = (int)(realtime * 10)%6; M_DrawTransPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); } void M_Net_Key (int k) { again: switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_MultiPlayer_f (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_net_cursor >= m_net_items) m_net_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_net_cursor < 0) m_net_cursor = m_net_items - 1; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; M_Menu_LanConfig_f (); break; } if (m_net_cursor == 0 && !ipxAvailable) goto again; if (m_net_cursor == 1 && !tcpipAvailable) goto again; } //============================================================================= /* OPTIONS MENU */ enum { OPT_CUSTOMIZE = 0, OPT_CONSOLE, // 1 OPT_DEFAULTS, // 2 OPT_SCALE, OPT_SCRSIZE, OPT_GAMMA, OPT_CONTRAST, OPT_MOUSESPEED, OPT_SBALPHA, OPT_SNDVOL, OPT_MUSICVOL, OPT_MUSICEXT, OPT_ALWAYRUN, OPT_INVMOUSE, OPT_ALWAYSMLOOK, OPT_LOOKSPRING, OPT_LOOKSTRAFE, //#ifdef _WIN32 // OPT_USEMOUSE, //#endif OPT_VIDEO, // This is the last before OPTIONS_ITEMS OPTIONS_ITEMS }; enum { ALWAYSRUN_OFF = 0, ALWAYSRUN_VANILLA, ALWAYSRUN_QUAKESPASM, ALWAYSRUN_ITEMS }; #define SLIDER_RANGE 10 int options_cursor; void M_Menu_Options_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_options; m_entersound = true; } void M_AdjustSliders (int dir) { int curr_alwaysrun, target_alwaysrun; float f, l; S_LocalSound ("misc/menu3.wav"); switch (options_cursor) { case OPT_SCALE: // console and menu scale l = ((vid.width + 31) / 32) / 10.0; f = scr_conscale.value + dir * .1; if (f < 1) f = 1; else if(f > l) f = l; Cvar_SetValue ("scr_conscale", f); Cvar_SetValue ("scr_menuscale", f); Cvar_SetValue ("scr_sbarscale", f); break; case OPT_SCRSIZE: // screen size f = scr_viewsize.value + dir * 10; if (f > 120) f = 120; else if(f < 30) f = 30; Cvar_SetValue ("viewsize", f); break; case OPT_GAMMA: // gamma f = vid_gamma.value - dir * 0.05; if (f < 0.5) f = 0.5; else if (f > 1) f = 1; Cvar_SetValue ("gamma", f); break; case OPT_CONTRAST: // contrast f = vid_contrast.value + dir * 0.1; if (f < 1) f = 1; else if (f > 2) f = 2; Cvar_SetValue ("contrast", f); break; case OPT_MOUSESPEED: // mouse speed f = sensitivity.value + dir * 0.5; if (f > 11) f = 11; else if (f < 1) f = 1; Cvar_SetValue ("sensitivity", f); break; case OPT_SBALPHA: // statusbar alpha f = scr_sbaralpha.value - dir * 0.05; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("scr_sbaralpha", f); break; case OPT_MUSICVOL: // music volume f = bgmvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("bgmvolume", f); break; case OPT_MUSICEXT: // enable external music vs cdaudio Cvar_Set ("bgm_extmusic", bgm_extmusic.value ? "0" : "1"); break; case OPT_SNDVOL: // sfx volume f = sfxvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("volume", f); break; case OPT_ALWAYRUN: // always run if (cl_alwaysrun.value) curr_alwaysrun = ALWAYSRUN_QUAKESPASM; else if (cl_forwardspeed.value > 200) curr_alwaysrun = ALWAYSRUN_VANILLA; else curr_alwaysrun = ALWAYSRUN_OFF; target_alwaysrun = (ALWAYSRUN_ITEMS + curr_alwaysrun + dir) % ALWAYSRUN_ITEMS; if (target_alwaysrun == ALWAYSRUN_VANILLA) { Cvar_SetValue ("cl_alwaysrun", 0); Cvar_SetValue ("cl_forwardspeed", 400); Cvar_SetValue ("cl_backspeed", 400); } else if (target_alwaysrun == ALWAYSRUN_QUAKESPASM) { Cvar_SetValue ("cl_alwaysrun", 1); Cvar_SetValue ("cl_forwardspeed", 200); Cvar_SetValue ("cl_backspeed", 200); } else // ALWAYSRUN_OFF { Cvar_SetValue ("cl_alwaysrun", 0); Cvar_SetValue ("cl_forwardspeed", 200); Cvar_SetValue ("cl_backspeed", 200); } break; case OPT_INVMOUSE: // invert mouse Cvar_SetValue ("m_pitch", -m_pitch.value); break; case OPT_ALWAYSMLOOK: if (in_mlook.state & 1) Cbuf_AddText("-mlook"); else Cbuf_AddText("+mlook"); break; case OPT_LOOKSPRING: // lookspring Cvar_Set ("lookspring", lookspring.value ? "0" : "1"); break; case OPT_LOOKSTRAFE: // lookstrafe Cvar_Set ("lookstrafe", lookstrafe.value ? "0" : "1"); break; } } void M_DrawSlider (int x, int y, float range) { int i; if (range < 0) range = 0; if (range > 1) range = 1; M_DrawCharacter (x-8, y, 128); for (i = 0; i < SLIDER_RANGE; i++) M_DrawCharacter (x + i*8, y, 129); M_DrawCharacter (x+i*8, y, 130); M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131); } void M_DrawCheckbox (int x, int y, int on) { #if 0 if (on) M_DrawCharacter (x, y, 131); else M_DrawCharacter (x, y, 129); #endif if (on) M_Print (x, y, "on"); else M_Print (x, y, "off"); } void M_Options_Draw (void) { float r, l; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_option.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); // Draw the items in the order of the enum defined above: // OPT_CUSTOMIZE: M_Print (16, 32, " Controls"); // OPT_CONSOLE: M_Print (16, 32 + 8*OPT_CONSOLE, " Goto console"); // OPT_DEFAULTS: M_Print (16, 32 + 8*OPT_DEFAULTS, " Reset config"); // OPT_SCALE: M_Print (16, 32 + 8*OPT_SCALE, " Scale"); l = (vid.width / 320.0) - 1; r = l > 0 ? (scr_conscale.value - 1) / l : 0; M_DrawSlider (220, 32 + 8*OPT_SCALE, r); // OPT_SCRSIZE: M_Print (16, 32 + 8*OPT_SCRSIZE, " Screen size"); r = (scr_viewsize.value - 30) / (120 - 30); M_DrawSlider (220, 32 + 8*OPT_SCRSIZE, r); // OPT_GAMMA: M_Print (16, 32 + 8*OPT_GAMMA, " Brightness"); r = (1.0 - vid_gamma.value) / 0.5; M_DrawSlider (220, 32 + 8*OPT_GAMMA, r); // OPT_CONTRAST: M_Print (16, 32 + 8*OPT_CONTRAST, " Contrast"); r = vid_contrast.value - 1.0; M_DrawSlider (220, 32 + 8*OPT_CONTRAST, r); // OPT_MOUSESPEED: M_Print (16, 32 + 8*OPT_MOUSESPEED, " Mouse Speed"); r = (sensitivity.value - 1)/10; M_DrawSlider (220, 32 + 8*OPT_MOUSESPEED, r); // OPT_SBALPHA: M_Print (16, 32 + 8*OPT_SBALPHA, " Statusbar alpha"); r = (1.0 - scr_sbaralpha.value) ; // scr_sbaralpha range is 1.0 to 0.0 M_DrawSlider (220, 32 + 8*OPT_SBALPHA, r); // OPT_SNDVOL: M_Print (16, 32 + 8*OPT_SNDVOL, " Sound Volume"); r = sfxvolume.value; M_DrawSlider (220, 32 + 8*OPT_SNDVOL, r); // OPT_MUSICVOL: M_Print (16, 32 + 8*OPT_MUSICVOL, " Music Volume"); r = bgmvolume.value; M_DrawSlider (220, 32 + 8*OPT_MUSICVOL, r); // OPT_MUSICEXT: M_Print (16, 32 + 8*OPT_MUSICEXT, " External Music"); M_DrawCheckbox (220, 32 + 8*OPT_MUSICEXT, bgm_extmusic.value); // OPT_ALWAYRUN: M_Print (16, 32 + 8*OPT_ALWAYRUN, " Always Run"); if (cl_alwaysrun.value) M_Print (220, 32 + 8*OPT_ALWAYRUN, "quakespasm"); else if (cl_forwardspeed.value > 200.0) M_Print (220, 32 + 8*OPT_ALWAYRUN, "vanilla"); else M_Print (220, 32 + 8*OPT_ALWAYRUN, "off"); // OPT_INVMOUSE: M_Print (16, 32 + 8*OPT_INVMOUSE, " Invert Mouse"); M_DrawCheckbox (220, 32 + 8*OPT_INVMOUSE, m_pitch.value < 0); // OPT_ALWAYSMLOOK: M_Print (16, 32 + 8*OPT_ALWAYSMLOOK, " Mouse Look"); M_DrawCheckbox (220, 32 + 8*OPT_ALWAYSMLOOK, in_mlook.state & 1); // OPT_LOOKSPRING: M_Print (16, 32 + 8*OPT_LOOKSPRING, " Lookspring"); M_DrawCheckbox (220, 32 + 8*OPT_LOOKSPRING, lookspring.value); // OPT_LOOKSTRAFE: M_Print (16, 32 + 8*OPT_LOOKSTRAFE, " Lookstrafe"); M_DrawCheckbox (220, 32 + 8*OPT_LOOKSTRAFE, lookstrafe.value); // OPT_VIDEO: if (vid_menudrawfn) M_Print (16, 32 + 8*OPT_VIDEO, " Video Options"); // cursor M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); } void M_Options_Key (int k) { switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_Main_f (); break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; switch (options_cursor) { case OPT_CUSTOMIZE: M_Menu_Keys_f (); break; case OPT_CONSOLE: m_state = m_none; Con_ToggleConsole_f (); break; case OPT_DEFAULTS: if (SCR_ModalMessage("This will reset all controls\n" "and stored cvars. Continue? (y/n)\n", 15.0f)) { Cbuf_AddText ("resetcfg\n"); Cbuf_AddText ("exec default.cfg\n"); } break; case OPT_VIDEO: M_Menu_Video_f (); break; default: M_AdjustSliders (1); break; } return; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); options_cursor--; if (options_cursor < 0) options_cursor = OPTIONS_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); options_cursor++; if (options_cursor >= OPTIONS_ITEMS) options_cursor = 0; break; case K_LEFTARROW: M_AdjustSliders (-1); break; case K_RIGHTARROW: M_AdjustSliders (1); break; } if (options_cursor == OPTIONS_ITEMS - 1 && vid_menudrawfn == NULL) { if (k == K_UPARROW) options_cursor = OPTIONS_ITEMS - 2; else options_cursor = 0; } } //============================================================================= /* KEYS MENU */ const char *bindnames[][2] = { {"+attack", "attack"}, {"impulse 10", "next weapon"}, {"impulse 12", "prev weapon"}, {"+jump", "jump / swim up"}, {"+forward", "walk forward"}, {"+back", "backpedal"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+speed", "run"}, {"+moveleft", "step left"}, {"+moveright", "step right"}, {"+strafe", "sidestep"}, {"+lookup", "look up"}, {"+lookdown", "look down"}, {"centerview", "center view"}, {"+mlook", "mouse look"}, {"+klook", "keyboard look"}, {"+moveup", "swim up"}, {"+movedown", "swim down"} }; #define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) static int keys_cursor; static qboolean bind_grab; void M_Menu_Keys_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_keys; m_entersound = true; } void M_FindKeysForCommand (const char *command, int *threekeys) { int count; int j; int l; char *b; threekeys[0] = threekeys[1] = threekeys[2] = -1; l = strlen(command); count = 0; for (j = 0; j < MAX_KEYS; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) { threekeys[count] = j; count++; if (count == 3) break; } } } void M_UnbindCommand (const char *command) { int j; int l; char *b; l = strlen(command); for (j = 0; j < MAX_KEYS; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) Key_SetBinding (j, NULL); } } extern qpic_t *pic_up, *pic_down; void M_Keys_Draw (void) { int i, x, y; int keys[3]; const char *name; qpic_t *p; p = Draw_CachePic ("gfx/ttl_cstm.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); if (bind_grab) M_Print (12, 32, "Press a key or button for this action"); else M_Print (18, 32, "Enter to change, backspace to clear"); // search for known bindings for (i = 0; i < (int)NUMCOMMANDS; i++) { y = 48 + 8*i; M_Print (16, y, bindnames[i][1]); M_FindKeysForCommand (bindnames[i][0], keys); if (keys[0] == -1) { M_Print (140, y, "???"); } else { name = Key_KeynumToString (keys[0]); M_Print (140, y, name); x = strlen(name) * 8; if (keys[1] != -1) { name = Key_KeynumToString (keys[1]); M_Print (140 + x + 8, y, "or"); M_Print (140 + x + 32, y, name); x = x + 32 + strlen(name) * 8; if (keys[2] != -1) { M_Print (140 + x + 8, y, "or"); M_Print (140 + x + 32, y, Key_KeynumToString (keys[2])); } } } } if (bind_grab) M_DrawCharacter (130, 48 + keys_cursor*8, '='); else M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1)); } void M_Keys_Key (int k) { char cmd[80]; int keys[3]; if (bind_grab) { // defining a key S_LocalSound ("misc/menu1.wav"); if ((k != K_ESCAPE) && (k != '`')) { sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]); Cbuf_InsertText (cmd); } bind_grab = false; IN_Deactivate(modestate == MS_WINDOWED); // deactivate because we're returning to the menu return; } switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_Options_f (); break; case K_LEFTARROW: case K_UPARROW: S_LocalSound ("misc/menu1.wav"); keys_cursor--; if (keys_cursor < 0) keys_cursor = NUMCOMMANDS-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); keys_cursor++; if (keys_cursor >= (int)NUMCOMMANDS) keys_cursor = 0; break; case K_ENTER: // go into bind mode case K_KP_ENTER: case K_ABUTTON: M_FindKeysForCommand (bindnames[keys_cursor][0], keys); S_LocalSound ("misc/menu2.wav"); if (keys[2] != -1) M_UnbindCommand (bindnames[keys_cursor][0]); bind_grab = true; IN_Activate(); // activate to allow mouse key binding break; case K_BACKSPACE: // delete bindings case K_DEL: S_LocalSound ("misc/menu2.wav"); M_UnbindCommand (bindnames[keys_cursor][0]); break; } } //============================================================================= /* VIDEO MENU */ void M_Menu_Video_f (void) { (*vid_menucmdfn) (); //johnfitz } void M_Video_Draw (void) { (*vid_menudrawfn) (); } void M_Video_Key (int key) { (*vid_menukeyfn) (key); } //============================================================================= /* HELP MENU */ int help_page; #define NUM_HELP_PAGES 6 void M_Menu_Help_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_help; m_entersound = true; help_page = 0; } void M_Help_Draw (void) { M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) ); } void M_Help_Key (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: M_Menu_Main_f (); break; case K_UPARROW: case K_RIGHTARROW: m_entersound = true; if (++help_page >= NUM_HELP_PAGES) help_page = 0; break; case K_DOWNARROW: case K_LEFTARROW: m_entersound = true; if (--help_page < 0) help_page = NUM_HELP_PAGES-1; break; } } //============================================================================= /* QUIT MENU */ int msgNumber; enum m_state_e m_quit_prevstate; qboolean wasInMenus; void M_Menu_Quit_f (void) { if (m_state == m_quit) return; wasInMenus = (key_dest == key_menu); IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_quit_prevstate = m_state; m_state = m_quit; m_entersound = true; msgNumber = rand()&7; } void M_Quit_Key (int key) { if (key == K_ESCAPE) { if (wasInMenus) { m_state = m_quit_prevstate; m_entersound = true; } else { IN_Activate(); key_dest = key_game; m_state = m_none; } } } void M_Quit_Char (int key) { switch (key) { case 'n': case 'N': if (wasInMenus) { m_state = m_quit_prevstate; m_entersound = true; } else { IN_Activate(); key_dest = key_game; m_state = m_none; } break; case 'y': case 'Y': IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_console; Host_Quit_f (); break; default: break; } } qboolean M_Quit_TextEntry (void) { return true; } void M_Quit_Draw (void) //johnfitz -- modified for new quit message { char msg1[40]; char msg2[] = "by Ozkan, Ericw & Stevenaaus"; /* msg2/msg3 are mostly [40] */ char msg3[] = "Press y to quit"; int boxlen; if (wasInMenus) { m_state = m_quit_prevstate; m_recursiveDraw = true; M_Draw (); m_state = m_quit; } sprintf(msg1, "QuakeSpasm " QUAKESPASM_VER_STRING); //okay, this is kind of fucked up. M_DrawTextBox will always act as if //width is even. Also, the width and lines values are for the interior of the box, //but the x and y values include the border. boxlen = q_max(strlen(msg1), q_max((sizeof(msg2)-1),(sizeof(msg3)-1))) + 1; if (boxlen & 1) boxlen++; M_DrawTextBox (160-4*(boxlen+2), 76, boxlen, 4); //now do the text M_Print (160-4*strlen(msg1), 88, msg1); M_Print (160-4*(sizeof(msg2)-1), 96, msg2); M_PrintWhite (160-4*(sizeof(msg3)-1), 104, msg3); } //============================================================================= /* LAN CONFIG MENU */ int lanConfig_cursor = -1; int lanConfig_cursor_table [] = {72, 92, 124}; #define NUM_LANCONFIG_CMDS 3 int lanConfig_port; char lanConfig_portname[6]; char lanConfig_joinname[22]; void M_Menu_LanConfig_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_lanconfig; m_entersound = true; if (lanConfig_cursor == -1) { if (JoiningGame && TCPIPConfig) lanConfig_cursor = 2; else lanConfig_cursor = 1; } if (StartingGame && lanConfig_cursor == 2) lanConfig_cursor = 1; lanConfig_port = DEFAULTnet_hostport; sprintf(lanConfig_portname, "%u", lanConfig_port); m_return_onerror = false; m_return_reason[0] = 0; } void M_LanConfig_Draw (void) { qpic_t *p; int basex; const char *startJoin; const char *protocol; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); basex = (320-p->width)/2; M_DrawPic (basex, 4, p); if (StartingGame) startJoin = "New Game"; else startJoin = "Join Game"; if (IPXConfig) protocol = "IPX"; else protocol = "TCP/IP"; M_Print (basex, 32, va ("%s - %s", startJoin, protocol)); basex += 8; M_Print (basex, 52, "Address:"); if (IPXConfig) M_Print (basex+9*8, 52, my_ipx_address); else M_Print (basex+9*8, 52, my_tcpip_address); M_Print (basex, lanConfig_cursor_table[0], "Port"); M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1); M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname); if (JoiningGame) { M_Print (basex, lanConfig_cursor_table[1], "Search for local games..."); M_Print (basex, 108, "Join game at:"); M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1); M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname); } else { M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1); M_Print (basex+8, lanConfig_cursor_table[1], "OK"); } M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1)); if (lanConfig_cursor == 0) M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1)); if (lanConfig_cursor == 2) M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (basex, 148, m_return_reason); } void M_LanConfig_Key (int key) { int l; switch (key) { case K_ESCAPE: case K_BBUTTON: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); lanConfig_cursor--; if (lanConfig_cursor < 0) lanConfig_cursor = NUM_LANCONFIG_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); lanConfig_cursor++; if (lanConfig_cursor >= NUM_LANCONFIG_CMDS) lanConfig_cursor = 0; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: if (lanConfig_cursor == 0) break; m_entersound = true; M_ConfigureNetSubsystem (); if (lanConfig_cursor == 1) { if (StartingGame) { M_Menu_GameOptions_f (); break; } M_Menu_Search_f(); break; } if (lanConfig_cursor == 2) { m_return_state = m_state; m_return_onerror = true; IN_Activate(); key_dest = key_game; m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) ); break; } break; case K_BACKSPACE: if (lanConfig_cursor == 0) { if (strlen(lanConfig_portname)) lanConfig_portname[strlen(lanConfig_portname)-1] = 0; } if (lanConfig_cursor == 2) { if (strlen(lanConfig_joinname)) lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0; } break; } if (StartingGame && lanConfig_cursor == 2) { if (key == K_UPARROW) lanConfig_cursor = 1; else lanConfig_cursor = 0; } l = Q_atoi(lanConfig_portname); if (l > 65535) l = lanConfig_port; else lanConfig_port = l; sprintf(lanConfig_portname, "%u", lanConfig_port); } void M_LanConfig_Char (int key) { int l; switch (lanConfig_cursor) { case 0: if (key < '0' || key > '9') return; l = strlen(lanConfig_portname); if (l < 5) { lanConfig_portname[l+1] = 0; lanConfig_portname[l] = key; } break; case 2: l = strlen(lanConfig_joinname); if (l < 21) { lanConfig_joinname[l+1] = 0; lanConfig_joinname[l] = key; } break; } } qboolean M_LanConfig_TextEntry (void) { return (lanConfig_cursor == 0 || lanConfig_cursor == 2); } //============================================================================= /* GAME OPTIONS MENU */ typedef struct { const char *name; const char *description; } level_t; level_t levels[] = { {"start", "Entrance"}, // 0 {"e1m1", "Slipgate Complex"}, // 1 {"e1m2", "Castle of the Damned"}, {"e1m3", "The Necropolis"}, {"e1m4", "The Grisly Grotto"}, {"e1m5", "Gloom Keep"}, {"e1m6", "The Door To Chthon"}, {"e1m7", "The House of Chthon"}, {"e1m8", "Ziggurat Vertigo"}, {"e2m1", "The Installation"}, // 9 {"e2m2", "Ogre Citadel"}, {"e2m3", "Crypt of Decay"}, {"e2m4", "The Ebon Fortress"}, {"e2m5", "The Wizard's Manse"}, {"e2m6", "The Dismal Oubliette"}, {"e2m7", "Underearth"}, {"e3m1", "Termination Central"}, // 16 {"e3m2", "The Vaults of Zin"}, {"e3m3", "The Tomb of Terror"}, {"e3m4", "Satan's Dark Delight"}, {"e3m5", "Wind Tunnels"}, {"e3m6", "Chambers of Torment"}, {"e3m7", "The Haunted Halls"}, {"e4m1", "The Sewage System"}, // 23 {"e4m2", "The Tower of Despair"}, {"e4m3", "The Elder God Shrine"}, {"e4m4", "The Palace of Hate"}, {"e4m5", "Hell's Atrium"}, {"e4m6", "The Pain Maze"}, {"e4m7", "Azure Agony"}, {"e4m8", "The Nameless City"}, {"end", "Shub-Niggurath's Pit"}, // 31 {"dm1", "Place of Two Deaths"}, // 32 {"dm2", "Claustrophobopolis"}, {"dm3", "The Abandoned Base"}, {"dm4", "The Bad Place"}, {"dm5", "The Cistern"}, {"dm6", "The Dark Zone"} }; //MED 01/06/97 added hipnotic levels level_t hipnoticlevels[] = { {"start", "Command HQ"}, // 0 {"hip1m1", "The Pumping Station"}, // 1 {"hip1m2", "Storage Facility"}, {"hip1m3", "The Lost Mine"}, {"hip1m4", "Research Facility"}, {"hip1m5", "Military Complex"}, {"hip2m1", "Ancient Realms"}, // 6 {"hip2m2", "The Black Cathedral"}, {"hip2m3", "The Catacombs"}, {"hip2m4", "The Crypt"}, {"hip2m5", "Mortum's Keep"}, {"hip2m6", "The Gremlin's Domain"}, {"hip3m1", "Tur Torment"}, // 12 {"hip3m2", "Pandemonium"}, {"hip3m3", "Limbo"}, {"hip3m4", "The Gauntlet"}, {"hipend", "Armagon's Lair"}, // 16 {"hipdm1", "The Edge of Oblivion"} // 17 }; //PGM 01/07/97 added rogue levels //PGM 03/02/97 added dmatch level level_t roguelevels[] = { {"start", "Split Decision"}, {"r1m1", "Deviant's Domain"}, {"r1m2", "Dread Portal"}, {"r1m3", "Judgement Call"}, {"r1m4", "Cave of Death"}, {"r1m5", "Towers of Wrath"}, {"r1m6", "Temple of Pain"}, {"r1m7", "Tomb of the Overlord"}, {"r2m1", "Tempus Fugit"}, {"r2m2", "Elemental Fury I"}, {"r2m3", "Elemental Fury II"}, {"r2m4", "Curse of Osiris"}, {"r2m5", "Wizard's Keep"}, {"r2m6", "Blood Sacrifice"}, {"r2m7", "Last Bastion"}, {"r2m8", "Source of Evil"}, {"ctf1", "Division of Change"} }; typedef struct { const char *description; int firstLevel; int levels; } episode_t; episode_t episodes[] = { {"Welcome to Quake", 0, 1}, {"Doomed Dimension", 1, 8}, {"Realm of Black Magic", 9, 7}, {"Netherworld", 16, 7}, {"The Elder World", 23, 8}, {"Final Level", 31, 1}, {"Deathmatch Arena", 32, 6} }; //MED 01/06/97 added hipnotic episodes episode_t hipnoticepisodes[] = { {"Scourge of Armagon", 0, 1}, {"Fortress of the Dead", 1, 5}, {"Dominion of Darkness", 6, 6}, {"The Rift", 12, 4}, {"Final Level", 16, 1}, {"Deathmatch Arena", 17, 1} }; //PGM 01/07/97 added rogue episodes //PGM 03/02/97 added dmatch episode episode_t rogueepisodes[] = { {"Introduction", 0, 1}, {"Hell's Fortress", 1, 7}, {"Corridors of Time", 8, 8}, {"Deathmatch Arena", 16, 1} }; int startepisode; int startlevel; int maxplayers; qboolean m_serverInfoMessage = false; double m_serverInfoMessageTime; void M_Menu_GameOptions_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_gameoptions; m_entersound = true; if (maxplayers == 0) maxplayers = svs.maxclients; if (maxplayers < 2) maxplayers = svs.maxclientslimit; } int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120}; #define NUM_GAMEOPTIONS 9 int gameoptions_cursor; void M_GameOptions_Draw (void) { qpic_t *p; int x; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTextBox (152, 32, 10, 1); M_Print (160, 40, "begin game"); M_Print (0, 56, " Max players"); M_Print (160, 56, va("%i", maxplayers) ); M_Print (0, 64, " Game Type"); if (coop.value) M_Print (160, 64, "Cooperative"); else M_Print (160, 64, "Deathmatch"); M_Print (0, 72, " Teamplay"); if (rogue) { const char *msg; switch((int)teamplay.value) { case 1: msg = "No Friendly Fire"; break; case 2: msg = "Friendly Fire"; break; case 3: msg = "Tag"; break; case 4: msg = "Capture the Flag"; break; case 5: msg = "One Flag CTF"; break; case 6: msg = "Three Team CTF"; break; default: msg = "Off"; break; } M_Print (160, 72, msg); } else { const char *msg; switch((int)teamplay.value) { case 1: msg = "No Friendly Fire"; break; case 2: msg = "Friendly Fire"; break; default: msg = "Off"; break; } M_Print (160, 72, msg); } M_Print (0, 80, " Skill"); if (skill.value == 0) M_Print (160, 80, "Easy difficulty"); else if (skill.value == 1) M_Print (160, 80, "Normal difficulty"); else if (skill.value == 2) M_Print (160, 80, "Hard difficulty"); else M_Print (160, 80, "Nightmare difficulty"); M_Print (0, 88, " Frag Limit"); if (fraglimit.value == 0) M_Print (160, 88, "none"); else M_Print (160, 88, va("%i frags", (int)fraglimit.value)); M_Print (0, 96, " Time Limit"); if (timelimit.value == 0) M_Print (160, 96, "none"); else M_Print (160, 96, va("%i minutes", (int)timelimit.value)); M_Print (0, 112, " Episode"); // MED 01/06/97 added hipnotic episodes if (hipnotic) M_Print (160, 112, hipnoticepisodes[startepisode].description); // PGM 01/07/97 added rogue episodes else if (rogue) M_Print (160, 112, rogueepisodes[startepisode].description); else M_Print (160, 112, episodes[startepisode].description); M_Print (0, 120, " Level"); // MED 01/06/97 added hipnotic episodes if (hipnotic) { M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name); } // PGM 01/07/97 added rogue episodes else if (rogue) { M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name); } else { M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name); } // line cursor M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1)); if (m_serverInfoMessage) { if ((realtime - m_serverInfoMessageTime) < 5.0) { x = (320-26*8)/2; M_DrawTextBox (x, 138, 24, 4); x += 8; M_Print (x, 146, " More than 4 players "); M_Print (x, 154, " requires using command "); M_Print (x, 162, "line parameters; please "); M_Print (x, 170, " see techinfo.txt. "); } else { m_serverInfoMessage = false; } } } void M_NetStart_Change (int dir) { int count; float f; switch (gameoptions_cursor) { case 1: maxplayers += dir; if (maxplayers > svs.maxclientslimit) { maxplayers = svs.maxclientslimit; m_serverInfoMessage = true; m_serverInfoMessageTime = realtime; } if (maxplayers < 2) maxplayers = 2; break; case 2: Cvar_Set ("coop", coop.value ? "0" : "1"); break; case 3: count = (rogue) ? 6 : 2; f = teamplay.value + dir; if (f > count) f = 0; else if (f < 0) f = count; Cvar_SetValue ("teamplay", f); break; case 4: f = skill.value + dir; if (f > 3) f = 0; else if (f < 0) f = 3; Cvar_SetValue ("skill", f); break; case 5: f = fraglimit.value + dir * 10; if (f > 100) f = 0; else if (f < 0) f = 100; Cvar_SetValue ("fraglimit", f); break; case 6: f = timelimit.value + dir * 5; if (f > 60) f = 0; else if (f < 0) f = 60; Cvar_SetValue ("timelimit", f); break; case 7: startepisode += dir; //MED 01/06/97 added hipnotic count if (hipnotic) count = 6; //PGM 01/07/97 added rogue count //PGM 03/02/97 added 1 for dmatch episode else if (rogue) count = 4; else if (registered.value) count = 7; else count = 2; if (startepisode < 0) startepisode = count - 1; if (startepisode >= count) startepisode = 0; startlevel = 0; break; case 8: startlevel += dir; //MED 01/06/97 added hipnotic episodes if (hipnotic) count = hipnoticepisodes[startepisode].levels; //PGM 01/06/97 added hipnotic episodes else if (rogue) count = rogueepisodes[startepisode].levels; else count = episodes[startepisode].levels; if (startlevel < 0) startlevel = count - 1; if (startlevel >= count) startlevel = 0; break; } } void M_GameOptions_Key (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); gameoptions_cursor--; if (gameoptions_cursor < 0) gameoptions_cursor = NUM_GAMEOPTIONS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); gameoptions_cursor++; if (gameoptions_cursor >= NUM_GAMEOPTIONS) gameoptions_cursor = 0; break; case K_LEFTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("misc/menu3.wav"); M_NetStart_Change (-1); break; case K_RIGHTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("misc/menu3.wav"); M_NetStart_Change (1); break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); if (gameoptions_cursor == 0) { if (sv.active) Cbuf_AddText ("disconnect\n"); Cbuf_AddText ("listen 0\n"); // so host_netport will be re-examined Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) ); SCR_BeginLoadingPlaque (); if (hipnotic) Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) ); else if (rogue) Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) ); else Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) ); return; } M_NetStart_Change (1); break; } } //============================================================================= /* SEARCH MENU */ qboolean searchComplete = false; double searchCompleteTime; void M_Menu_Search_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_search; m_entersound = false; slistSilent = true; slistLocal = false; searchComplete = false; NET_Slist_f(); } void M_Search_Draw (void) { qpic_t *p; int x; p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); x = (320/2) - ((12*8)/2) + 4; M_DrawTextBox (x-8, 32, 12, 1); M_Print (x, 40, "Searching..."); if(slistInProgress) { NET_Poll(); return; } if (! searchComplete) { searchComplete = true; searchCompleteTime = realtime; } if (hostCacheCount) { M_Menu_ServerList_f (); return; } M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found"); if ((realtime - searchCompleteTime) < 3.0) return; M_Menu_LanConfig_f (); } void M_Search_Key (int key) { } //============================================================================= /* SLIST MENU */ int slist_cursor; qboolean slist_sorted; void M_Menu_ServerList_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_slist; m_entersound = true; slist_cursor = 0; m_return_onerror = false; m_return_reason[0] = 0; slist_sorted = false; } void M_ServerList_Draw (void) { int n; qpic_t *p; if (!slist_sorted) { slist_sorted = true; NET_SlistSort (); } p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); for (n = 0; n < hostCacheCount; n++) M_Print (16, 32 + 8*n, NET_SlistPrintServer (n)); M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (16, 148, m_return_reason); } void M_ServerList_Key (int k) { switch (k) { case K_ESCAPE: case K_BBUTTON: M_Menu_LanConfig_f (); break; case K_SPACE: M_Menu_Search_f (); break; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); slist_cursor--; if (slist_cursor < 0) slist_cursor = hostCacheCount - 1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); slist_cursor++; if (slist_cursor >= hostCacheCount) slist_cursor = 0; break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); m_return_state = m_state; m_return_onerror = true; slist_sorted = false; IN_Activate(); key_dest = key_game; m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", NET_SlistPrintServerName(slist_cursor)) ); break; default: break; } } //============================================================================= /* Menu Subsystem */ void M_Init (void) { Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); Cmd_AddCommand ("menu_main", M_Menu_Main_f); Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f); Cmd_AddCommand ("menu_load", M_Menu_Load_f); Cmd_AddCommand ("menu_save", M_Menu_Save_f); Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f); Cmd_AddCommand ("menu_setup", M_Menu_Setup_f); Cmd_AddCommand ("menu_options", M_Menu_Options_f); Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("menu_video", M_Menu_Video_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); } void M_Draw (void) { if (m_state == m_none || key_dest != key_menu) return; if (!m_recursiveDraw) { if (scr_con_current) { Draw_ConsoleBackground (); S_ExtraUpdate (); } Draw_FadeScreen (); //johnfitz -- fade even if console fills screen } else { m_recursiveDraw = false; } GL_SetCanvas (CANVAS_MENU); //johnfitz switch (m_state) { case m_none: break; case m_main: M_Main_Draw (); break; case m_singleplayer: M_SinglePlayer_Draw (); break; case m_load: M_Load_Draw (); break; case m_save: M_Save_Draw (); break; case m_multiplayer: M_MultiPlayer_Draw (); break; case m_setup: M_Setup_Draw (); break; case m_net: M_Net_Draw (); break; case m_options: M_Options_Draw (); break; case m_keys: M_Keys_Draw (); break; case m_video: M_Video_Draw (); break; case m_help: M_Help_Draw (); break; case m_quit: if (!fitzmode) { /* QuakeSpasm customization: */ /* Quit now! S.A. */ key_dest = key_console; Host_Quit_f (); } M_Quit_Draw (); break; case m_lanconfig: M_LanConfig_Draw (); break; case m_gameoptions: M_GameOptions_Draw (); break; case m_search: M_Search_Draw (); break; case m_slist: M_ServerList_Draw (); break; } if (m_entersound) { S_LocalSound ("misc/menu2.wav"); m_entersound = false; } S_ExtraUpdate (); } void M_Keydown (int key) { switch (m_state) { case m_none: return; case m_main: M_Main_Key (key); return; case m_singleplayer: M_SinglePlayer_Key (key); return; case m_load: M_Load_Key (key); return; case m_save: M_Save_Key (key); return; case m_multiplayer: M_MultiPlayer_Key (key); return; case m_setup: M_Setup_Key (key); return; case m_net: M_Net_Key (key); return; case m_options: M_Options_Key (key); return; case m_keys: M_Keys_Key (key); return; case m_video: M_Video_Key (key); return; case m_help: M_Help_Key (key); return; case m_quit: M_Quit_Key (key); return; case m_lanconfig: M_LanConfig_Key (key); return; case m_gameoptions: M_GameOptions_Key (key); return; case m_search: M_Search_Key (key); break; case m_slist: M_ServerList_Key (key); return; } } void M_Charinput (int key) { switch (m_state) { case m_setup: M_Setup_Char (key); return; case m_quit: M_Quit_Char (key); return; case m_lanconfig: M_LanConfig_Char (key); return; default: return; } } qboolean M_TextEntry (void) { switch (m_state) { case m_setup: return M_Setup_TextEntry (); case m_quit: return M_Quit_TextEntry (); case m_lanconfig: return M_LanConfig_TextEntry (); default: return false; } } void M_ConfigureNetSubsystem(void) { // enable/disable net systems to match desired config Cbuf_AddText ("stopdemo\n"); if (IPXConfig || TCPIPConfig) net_hostport = lanConfig_port; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_warp.c�������������������������������������������������������������������0000644�0000000�0000000�00000014565�13070016024�015324� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //gl_warp.c -- warping animation support #include "quakedef.h" extern cvar_t r_drawflat; cvar_t r_oldwater = {"r_oldwater", "0", CVAR_ARCHIVE}; cvar_t r_waterquality = {"r_waterquality", "8", CVAR_NONE}; cvar_t r_waterwarp = {"r_waterwarp", "1", CVAR_NONE}; int gl_warpimagesize; float load_subdivide_size; //johnfitz -- remember what subdivide_size value was when this map was loaded float turbsin[] = { #include "gl_warp_sin.h" }; #define WARPCALC(s,t) ((s + turbsin[(int)((t*2)+(cl.time*(128.0/M_PI))) & 255]) * (1.0/64)) //johnfitz -- correct warp #define WARPCALC2(s,t) ((s + turbsin[(int)((t*0.125+cl.time)*(128.0/M_PI)) & 255]) * (1.0/64)) //johnfitz -- old warp //============================================================================== // // OLD-STYLE WATER // //============================================================================== extern qmodel_t *loadmodel; msurface_t *warpface; cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", CVAR_ARCHIVE}; void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) { int i, j; float *v; mins[0] = mins[1] = mins[2] = 999999999; maxs[0] = maxs[1] = maxs[2] = -999999999; v = verts; for (i=0 ; i<numverts ; i++) for (j=0 ; j<3 ; j++, v++) { if (*v < mins[j]) mins[j] = *v; if (*v > maxs[j]) maxs[j] = *v; } } void SubdividePolygon (int numverts, float *verts) { int i, j, k; vec3_t mins, maxs; float m; float *v; vec3_t front[64], back[64]; int f, b; float dist[64]; float frac; glpoly_t *poly; float s, t; if (numverts > 60) Sys_Error ("numverts = %i", numverts); BoundPoly (numverts, verts, mins, maxs); for (i=0 ; i<3 ; i++) { m = (mins[i] + maxs[i]) * 0.5; m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5); if (maxs[i] - m < 8) continue; if (m - mins[i] < 8) continue; // cut it v = verts + i; for (j=0 ; j<numverts ; j++, v+= 3) dist[j] = *v - m; // wrap cases dist[j] = dist[0]; v-=i; VectorCopy (verts, v); f = b = 0; v = verts; for (j=0 ; j<numverts ; j++, v+= 3) { if (dist[j] >= 0) { VectorCopy (v, front[f]); f++; } if (dist[j] <= 0) { VectorCopy (v, back[b]); b++; } if (dist[j] == 0 || dist[j+1] == 0) continue; if ( (dist[j] > 0) != (dist[j+1] > 0) ) { // clip point frac = dist[j] / (dist[j] - dist[j+1]); for (k=0 ; k<3 ; k++) front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); f++; b++; } } SubdividePolygon (f, front[0]); SubdividePolygon (b, back[0]); return; } poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); poly->next = warpface->polys->next; warpface->polys->next = poly; poly->numverts = numverts; for (i=0 ; i<numverts ; i++, verts+= 3) { VectorCopy (verts, poly->verts[i]); s = DotProduct (verts, warpface->texinfo->vecs[0]); t = DotProduct (verts, warpface->texinfo->vecs[1]); poly->verts[i][3] = s; poly->verts[i][4] = t; } } /* ================ GL_SubdivideSurface ================ */ void GL_SubdivideSurface (msurface_t *fa) { vec3_t verts[64]; int i; warpface = fa; //the first poly in the chain is the undivided poly for newwater rendering. //grab the verts from that. for (i=0; i<fa->polys->numverts; i++) VectorCopy (fa->polys->verts[i], verts[i]); SubdividePolygon (fa->polys->numverts, verts[0]); } /* ================ DrawWaterPoly -- johnfitz ================ */ void DrawWaterPoly (glpoly_t *p) { float *v; int i; if (load_subdivide_size > 48) { glBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (WARPCALC2(v[3],v[4]), WARPCALC2(v[4],v[3])); glVertex3fv (v); } glEnd (); } else { glBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (WARPCALC(v[3],v[4]), WARPCALC(v[4],v[3])); glVertex3fv (v); } glEnd (); } } //============================================================================== // // RENDER-TO-FRAMEBUFFER WATER // //============================================================================== /* ============= R_UpdateWarpTextures -- johnfitz -- each frame, update warping textures ============= */ void R_UpdateWarpTextures (void) { texture_t *tx; int i; float x, y, x2, warptess; if (r_oldwater.value || cl.paused || r_drawflat_cheatsafe || r_lightmap_cheatsafe) return; warptess = 128.0/CLAMP (3.0, floor(r_waterquality.value), 64.0); for (i=0; i<cl.worldmodel->numtextures; i++) { if (!(tx = cl.worldmodel->textures[i])) continue; if (!tx->update_warp) continue; //render warp GL_SetCanvas (CANVAS_WARPIMAGE); GL_Bind (tx->gltexture); for (x=0.0; x<128.0; x=x2) { x2 = x + warptess; glBegin (GL_TRIANGLE_STRIP); for (y=0.0; y<128.01; y+=warptess) // .01 for rounding errors { glTexCoord2f (WARPCALC(x,y), WARPCALC(y,x)); glVertex2f (x,y); glTexCoord2f (WARPCALC(x2,y), WARPCALC(y,x2)); glVertex2f (x2,y); } glEnd(); } //copy to texture GL_Bind (tx->warpimage); glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, glx, gly+glheight-gl_warpimagesize, gl_warpimagesize, gl_warpimagesize); tx->update_warp = false; } // ericw -- workaround for osx 10.6 driver bug when using FSAA. R_Clear only clears the warpimage part of the screen. GL_SetCanvas(CANVAS_DEFAULT); //if warp render went down into sbar territory, we need to be sure to refresh it next frame if (gl_warpimagesize + sb_lines > glheight) Sbar_Changed (); //if viewsize is less than 100, we need to redraw the frame around the viewport scr_tileclear_updates = 0; } �������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mikmod.c����������������������������������������������������������������0000644�0000000�0000000�00000013135�12752002276�016017� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * tracker music (module file) decoding support using libmikmod * Copyright (C) 2013 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MIKMOD) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mikmod.h" #include <mikmod.h> #if ((LIBMIKMOD_VERSION+0) < 0x030105) #error libmikmod version is way too old and unusable. #endif #if (LIBMIKMOD_VERSION < 0x030107) /* ancient libmikmod */ #define S_MIKMOD_initlib(c) MikMod_Init() #else #define S_MIKMOD_initlib(c) MikMod_Init(c) #endif #ifndef DMODE_NOISEREDUCTION #define DMODE_NOISEREDUCTION 0x1000 /* Low pass filtering */ #endif #ifndef DMODE_SIMDMIXER #define DMODE_SIMDMIXER 0x0800 /* enable SIMD mixing */ #endif typedef struct _mik_priv { /* struct MREADER in libmikmod <= 3.2.0-beta2 * doesn't have iobase members. adding them here * so that if we compile against 3.2.0-beta2, we * can still run OK against 3.2.0b3 and newer. */ struct MREADER reader; long iobase, prev_iobase; fshandle_t *fh; MODULE *module; } mik_priv_t; static int MIK_Seek (MREADER *r, long ofs, int whence) { return FS_fseek(((mik_priv_t *)r)->fh, ofs, whence); } static long MIK_Tell (MREADER *r) { return FS_ftell(((mik_priv_t *)r)->fh); } static BOOL MIK_Read (MREADER *r, void *ptr, size_t siz) { return !!FS_fread(ptr, siz, 1, ((mik_priv_t *)r)->fh); } static int MIK_Get (MREADER *r) { return FS_fgetc(((mik_priv_t *)r)->fh); } static BOOL MIK_Eof (MREADER *r) { return FS_feof(((mik_priv_t *)r)->fh); } static qboolean S_MIKMOD_CodecInitialize (void) { if (mikmod_codec.initialized) return true; /* set mode flags to only we like: */ md_mode = 0; if ((shm->samplebits / 8) == 2) md_mode |= DMODE_16BITS; if (shm->channels == 2) md_mode |= DMODE_STEREO; md_mode |= DMODE_SOFT_MUSIC; /* this is a software-only mixer */ /* md_mixfreq is UWORD, so something like 96000 isn't OK */ md_mixfreq = (shm->speed < 65536)? shm->speed : 48000; /* keeping md_device as 0 which is default (auto-detect: we * only register drv_nos, and it will be the only one found.) * md_pansep (stereo channels separation) default 128 is OK. * no reverbation (md_reverb 0 (up to 15)) is OK. * md_musicvolume and md_sndfxvolume defaults are 128: OK. */ /* just tone down overall volume md_volume from 128 to 96? */ md_volume = 96; MikMod_RegisterDriver(&drv_nos); /* only need the "nosound" driver, none else */ MikMod_RegisterAllLoaders(); if (S_MIKMOD_initlib(NULL)) { Con_DPrintf("Could not initialize MikMod: %s\n", MikMod_strerror(MikMod_errno)); return false; } /* this can't get set with drv_nos, but whatever, be safe: */ md_mode &= ~DMODE_SIMDMIXER; /* SIMD mixer is buggy when combined with HQMIXER */ mikmod_codec.initialized = true; return true; } static void S_MIKMOD_CodecShutdown (void) { if (mikmod_codec.initialized) { mikmod_codec.initialized = false; MikMod_Exit(); } } static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream) { mik_priv_t *priv; stream->priv = Z_Malloc(sizeof(mik_priv_t)); priv = (mik_priv_t *) stream->priv; priv->reader.Seek = MIK_Seek; priv->reader.Tell = MIK_Tell; priv->reader.Read = MIK_Read; priv->reader.Get = MIK_Get; priv->reader.Eof = MIK_Eof; priv->fh = &stream->fh; priv->module = Player_LoadGeneric((MREADER *)stream->priv, 64, 0); if (!priv->module) { Con_DPrintf("Could not load module: %s\n", MikMod_strerror(MikMod_errno)); Z_Free(stream->priv); return false; } /* keep default values of fadeout (0: don't fade out volume during when last * position of the module is being played), extspd (1: do process Protracker * extended speed effect), panflag (1: do process panning effects), wrap (0: * don't wrap to restart position when module is finished) are OK with us as * set internally by libmikmod::Player_Init(). */ /* just change the loop setting to 0, i.e. don't process in-module loops: */ priv->module->loop = 0; Player_Start(priv->module); stream->info.rate = md_mixfreq; stream->info.bits = (md_mode & DMODE_16BITS)? 16: 8; stream->info.width = stream->info.bits / 8; stream->info.channels = (md_mode & DMODE_STEREO)? 2 : 1; /* Con_DPrintf("Playing %s (%d chn)\n", priv->module->songname, priv->module->numchn);*/ return true; } static int S_MIKMOD_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { if (!Player_Active()) return 0; return (int) VC_WriteBytes((SBYTE *)buffer, bytes); } static void S_MIKMOD_CodecCloseStream (snd_stream_t *stream) { Player_Stop(); Player_Free(((mik_priv_t *)stream->priv)->module); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_MIKMOD_CodecRewindStream (snd_stream_t *stream) { Player_SetPosition (0); return 0; } snd_codec_t mikmod_codec = { CODECTYPE_MOD, false, "s3m", S_MIKMOD_CodecInitialize, S_MIKMOD_CodecShutdown, S_MIKMOD_CodecOpenStream, S_MIKMOD_CodecReadStream, S_MIKMOD_CodecRewindStream, S_MIKMOD_CodecCloseStream, NULL }; #endif /* USE_CODEC_MIKMOD */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/vid.h�����������������������������������������������������������������������0000644�0000000�0000000�00000004747�13123310012�014452� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __VID_DEFS_H #define __VID_DEFS_H // vid.h -- video driver defs #define VID_CBITS 6 #define VID_GRADES (1 << VID_CBITS) #define GAMMA_MAX 3.0 // moved here for global use -- kristian typedef enum { MS_UNINIT, MS_WINDOWED, MS_FULLSCREEN } modestate_t; extern modestate_t modestate; // a pixel can be one, two, or four bytes typedef byte pixel_t; typedef struct vrect_s { int x, y, width, height; struct vrect_s *pnext; } vrect_t; typedef struct { pixel_t *buffer; // invisible buffer pixel_t *colormap; // 256 * VID_GRADES size unsigned short *colormap16; // 256 * VID_GRADES size int fullbright; // index of first fullbright color int rowbytes; // may be > width if displayed in a window int width; int height; float aspect; // width / height -- < 0 is taller than wide int numpages; int recalc_refdef; // if true, recalc vid-based stuff pixel_t *conbuffer; int conrowbytes; int conwidth; int conheight; int maxwarpwidth; int maxwarpheight; pixel_t *direct; // direct drawing to framebuffer, if not NULL } viddef_t; extern viddef_t vid; // global video state extern void (*vid_menudrawfn)(void); extern void (*vid_menukeyfn)(int key); extern void (*vid_menucmdfn)(void); //johnfitz void VID_Init (void); //johnfitz -- removed palette from argument list void VID_Shutdown (void); // Called at shutdown void VID_Update (vrect_t *rects); // flushes the given rectangles from the view buffer to the screen void VID_SyncCvars (void); void VID_Toggle (void); void *VID_GetWindow (void); qboolean VID_HasMouseOrInputFocus (void); qboolean VID_IsMinimized (void); void VID_Lock (void); #endif /* __VID_DEFS_H */ �������������������������quakespasm-0.93.0/Quake/gl_texmgr.c�����������������������������������������������������������������0000644�0000000�0000000�00000111153�13153014003�015645� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //gl_texmgr.c -- fitzquake's texture manager. manages opengl texture images #include "quakedef.h" const int gl_solid_format = 3; const int gl_alpha_format = 4; static cvar_t gl_texturemode = {"gl_texturemode", "", CVAR_ARCHIVE}; static cvar_t gl_texture_anisotropy = {"gl_texture_anisotropy", "1", CVAR_ARCHIVE}; static cvar_t gl_max_size = {"gl_max_size", "0", CVAR_NONE}; static cvar_t gl_picmip = {"gl_picmip", "0", CVAR_NONE}; static GLint gl_hardware_maxsize; #define MAX_GLTEXTURES 2048 static int numgltextures; static gltexture_t *active_gltextures, *free_gltextures; gltexture_t *notexture, *nulltexture; unsigned int d_8to24table[256]; unsigned int d_8to24table_fbright[256]; unsigned int d_8to24table_fbright_fence[256]; unsigned int d_8to24table_nobright[256]; unsigned int d_8to24table_nobright_fence[256]; unsigned int d_8to24table_conchars[256]; unsigned int d_8to24table_shirt[256]; unsigned int d_8to24table_pants[256]; /* ================================================================================ COMMANDS ================================================================================ */ typedef struct { int magfilter; int minfilter; const char *name; } glmode_t; static glmode_t glmodes[] = { {GL_NEAREST, GL_NEAREST, "GL_NEAREST"}, {GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, "GL_NEAREST_MIPMAP_NEAREST"}, {GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR, "GL_NEAREST_MIPMAP_LINEAR"}, {GL_LINEAR, GL_LINEAR, "GL_LINEAR"}, {GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, "GL_LINEAR_MIPMAP_NEAREST"}, {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, "GL_LINEAR_MIPMAP_LINEAR"}, }; #define NUM_GLMODES (int)(sizeof(glmodes)/sizeof(glmodes[0])) static int glmode_idx = NUM_GLMODES - 1; /* trilinear */ /* =============== TexMgr_DescribeTextureModes_f -- report available texturemodes =============== */ static void TexMgr_DescribeTextureModes_f (void) { int i; for (i = 0; i < NUM_GLMODES; i++) Con_SafePrintf (" %2i: %s\n", i + 1, glmodes[i].name); Con_Printf ("%i modes\n", i); } /* =============== TexMgr_SetFilterModes =============== */ static void TexMgr_SetFilterModes (gltexture_t *glt) { GL_Bind (glt); if (glt->flags & TEXPREF_NEAREST) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else if (glt->flags & TEXPREF_LINEAR) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else if (glt->flags & TEXPREF_MIPMAP) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].minfilter); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value); } else { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].magfilter); } } /* =============== TexMgr_TextureMode_f -- called when gl_texturemode changes =============== */ static void TexMgr_TextureMode_f (cvar_t *var) { gltexture_t *glt; int i; for (i = 0; i < NUM_GLMODES; i++) { if (!Q_strcmp (glmodes[i].name, gl_texturemode.string)) { if (glmode_idx != i) { glmode_idx = i; for (glt = active_gltextures; glt; glt = glt->next) TexMgr_SetFilterModes (glt); Sbar_Changed (); //sbar graphics need to be redrawn with new filter mode //FIXME: warpimages need to be redrawn, too. } return; } } for (i = 0; i < NUM_GLMODES; i++) { if (!q_strcasecmp (glmodes[i].name, gl_texturemode.string)) { Cvar_SetQuick (&gl_texturemode, glmodes[i].name); return; } } i = atoi(gl_texturemode.string); if (i >= 1 && i <= NUM_GLMODES) { Cvar_SetQuick (&gl_texturemode, glmodes[i-1].name); return; } Con_Printf ("\"%s\" is not a valid texturemode\n", gl_texturemode.string); Cvar_SetQuick (&gl_texturemode, glmodes[glmode_idx].name); } /* =============== TexMgr_Anisotropy_f -- called when gl_texture_anisotropy changes =============== */ static void TexMgr_Anisotropy_f (cvar_t *var) { if (gl_texture_anisotropy.value < 1) { Cvar_SetQuick (&gl_texture_anisotropy, "1"); } else if (gl_texture_anisotropy.value > gl_max_anisotropy) { Cvar_SetValueQuick (&gl_texture_anisotropy, gl_max_anisotropy); } else { gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) { /* TexMgr_SetFilterModes (glt);*/ if (glt->flags & TEXPREF_MIPMAP) { GL_Bind (glt); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].minfilter); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value); } } } } /* =============== TexMgr_Imagelist_f -- report loaded textures =============== */ static void TexMgr_Imagelist_f (void) { float mb; float texels = 0; gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) { Con_SafePrintf (" %4i x%4i %s\n", glt->width, glt->height, glt->name); if (glt->flags & TEXPREF_MIPMAP) texels += glt->width * glt->height * 4.0f / 3.0f; else texels += (glt->width * glt->height); } mb = texels * (Cvar_VariableValue("vid_bpp") / 8.0f) / 0x100000; Con_Printf ("%i textures %i pixels %1.1f megabytes\n", numgltextures, (int)texels, mb); } /* =============== TexMgr_Imagedump_f -- dump all current textures to TGA files =============== */ static void TexMgr_Imagedump_f (void) { char tganame[MAX_OSPATH], tempname[MAX_OSPATH], dirname[MAX_OSPATH]; gltexture_t *glt; byte *buffer; char *c; //create directory q_snprintf(dirname, sizeof(dirname), "%s/imagedump", com_gamedir); Sys_mkdir (dirname); //loop through textures for (glt = active_gltextures; glt; glt = glt->next) { q_strlcpy (tempname, glt->name, sizeof(tempname)); while ( (c = strchr(tempname, ':')) ) *c = '_'; while ( (c = strchr(tempname, '/')) ) *c = '_'; while ( (c = strchr(tempname, '*')) ) *c = '_'; q_snprintf(tganame, sizeof(tganame), "imagedump/%s.tga", tempname); GL_Bind (glt); if (glt->flags & TEXPREF_ALPHA) { buffer = (byte *) malloc(glt->width*glt->height*4); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); Image_WriteTGA (tganame, buffer, glt->width, glt->height, 32, true); } else { buffer = (byte *) malloc(glt->width*glt->height*3); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer); Image_WriteTGA (tganame, buffer, glt->width, glt->height, 24, true); } free (buffer); } Con_Printf ("dumped %i textures to %s\n", numgltextures, dirname); } /* =============== TexMgr_FrameUsage -- report texture memory usage for this frame =============== */ float TexMgr_FrameUsage (void) { float mb; float texels = 0; gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) { if (glt->visframe == r_framecount) { if (glt->flags & TEXPREF_MIPMAP) texels += glt->width * glt->height * 4.0f / 3.0f; else texels += (glt->width * glt->height); } } mb = texels * (Cvar_VariableValue("vid_bpp") / 8.0f) / 0x100000; return mb; } /* ================================================================================ TEXTURE MANAGER ================================================================================ */ /* ================ TexMgr_FindTexture ================ */ gltexture_t *TexMgr_FindTexture (qmodel_t *owner, const char *name) { gltexture_t *glt; if (name) { for (glt = active_gltextures; glt; glt = glt->next) { if (glt->owner == owner && !strcmp (glt->name, name)) return glt; } } return NULL; } /* ================ TexMgr_NewTexture ================ */ gltexture_t *TexMgr_NewTexture (void) { gltexture_t *glt; if (numgltextures == MAX_GLTEXTURES) Sys_Error("numgltextures == MAX_GLTEXTURES\n"); glt = free_gltextures; free_gltextures = glt->next; glt->next = active_gltextures; active_gltextures = glt; glGenTextures(1, &glt->texnum); numgltextures++; return glt; } static void GL_DeleteTexture (gltexture_t *texture); //ericw -- workaround for preventing TexMgr_FreeTexture during TexMgr_ReloadImages static qboolean in_reload_images; /* ================ TexMgr_FreeTexture ================ */ void TexMgr_FreeTexture (gltexture_t *kill) { gltexture_t *glt; if (in_reload_images) return; if (kill == NULL) { Con_Printf ("TexMgr_FreeTexture: NULL texture\n"); return; } if (active_gltextures == kill) { active_gltextures = kill->next; kill->next = free_gltextures; free_gltextures = kill; GL_DeleteTexture(kill); numgltextures--; return; } for (glt = active_gltextures; glt; glt = glt->next) { if (glt->next == kill) { glt->next = kill->next; kill->next = free_gltextures; free_gltextures = kill; GL_DeleteTexture(kill); numgltextures--; return; } } Con_Printf ("TexMgr_FreeTexture: not found\n"); } /* ================ TexMgr_FreeTextures compares each bit in "flags" to the one in glt->flags only if that bit is active in "mask" ================ */ void TexMgr_FreeTextures (unsigned int flags, unsigned int mask) { gltexture_t *glt, *next; for (glt = active_gltextures; glt; glt = next) { next = glt->next; if ((glt->flags & mask) == (flags & mask)) TexMgr_FreeTexture (glt); } } /* ================ TexMgr_FreeTexturesForOwner ================ */ void TexMgr_FreeTexturesForOwner (qmodel_t *owner) { gltexture_t *glt, *next; for (glt = active_gltextures; glt; glt = next) { next = glt->next; if (glt && glt->owner == owner) TexMgr_FreeTexture (glt); } } /* ================ TexMgr_DeleteTextureObjects ================ */ void TexMgr_DeleteTextureObjects (void) { gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) { GL_DeleteTexture (glt); } } /* ================================================================================ INIT ================================================================================ */ /* ================= TexMgr_LoadPalette -- johnfitz -- was VID_SetPalette, moved here, renamed, rewritten ================= */ void TexMgr_LoadPalette (void) { byte *pal, *src, *dst; int i, mark; FILE *f; COM_FOpenFile ("gfx/palette.lmp", &f, NULL); if (!f) Sys_Error ("Couldn't load gfx/palette.lmp"); mark = Hunk_LowMark (); pal = (byte *) Hunk_Alloc (768); fread (pal, 1, 768, f); fclose(f); //standard palette, 255 is transparent dst = (byte *)d_8to24table; src = pal; for (i = 0; i < 256; i++) { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = 255; } ((byte *) &d_8to24table[255]) [3] = 0; //fullbright palette, 0-223 are black (for additive blending) src = pal + 224*3; dst = (byte *) &d_8to24table_fbright[224]; for (i = 224; i < 256; i++) { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = 255; } for (i = 0; i < 224; i++) { dst = (byte *) &d_8to24table_fbright[i]; dst[3] = 255; dst[2] = dst[1] = dst[0] = 0; } //nobright palette, 224-255 are black (for additive blending) dst = (byte *)d_8to24table_nobright; src = pal; for (i = 0; i < 256; i++) { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = 255; } for (i = 224; i < 256; i++) { dst = (byte *) &d_8to24table_nobright[i]; dst[3] = 255; dst[2] = dst[1] = dst[0] = 0; } //fullbright palette, for fence textures memcpy(d_8to24table_fbright_fence, d_8to24table_fbright, 256*4); d_8to24table_fbright_fence[255] = 0; // Alpha of zero. //nobright palette, for fence textures memcpy(d_8to24table_nobright_fence, d_8to24table_nobright, 256*4); d_8to24table_nobright_fence[255] = 0; // Alpha of zero. //conchars palette, 0 and 255 are transparent memcpy(d_8to24table_conchars, d_8to24table, 256*4); ((byte *) &d_8to24table_conchars[0]) [3] = 0; Hunk_FreeToLowMark (mark); } /* ================ TexMgr_NewGame ================ */ void TexMgr_NewGame (void) { TexMgr_FreeTextures (0, TEXPREF_PERSIST); //deletes all textures where TEXPREF_PERSIST is unset TexMgr_LoadPalette (); } /* ============= TexMgr_RecalcWarpImageSize -- called during init, and after a vid_restart choose safe warpimage size and resize existing warpimage textures ============= */ void TexMgr_RecalcWarpImageSize (void) { // int oldsize = gl_warpimagesize; int mark; gltexture_t *glt; byte *dummy; // // find the new correct size // gl_warpimagesize = TexMgr_SafeTextureSize (512); while (gl_warpimagesize > vid.width) gl_warpimagesize >>= 1; while (gl_warpimagesize > vid.height) gl_warpimagesize >>= 1; // ericw -- removed early exit if (gl_warpimagesize == oldsize). // after vid_restart TexMgr_ReloadImage reloads textures // to tx->source_width/source_height, which might not match oldsize. // fixes: https://sourceforge.net/p/quakespasm/bugs/13/ // // resize the textures in opengl // mark = Hunk_LowMark(); dummy = (byte *) Hunk_Alloc (gl_warpimagesize*gl_warpimagesize*4); for (glt = active_gltextures; glt; glt = glt->next) { if (glt->flags & TEXPREF_WARPIMAGE) { GL_Bind (glt); glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, gl_warpimagesize, gl_warpimagesize, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummy); glt->width = glt->height = gl_warpimagesize; } } Hunk_FreeToLowMark (mark); } /* ================ TexMgr_Init must be called before any texture loading ================ */ void TexMgr_Init (void) { int i; static byte notexture_data[16] = {159,91,83,255,0,0,0,255,0,0,0,255,159,91,83,255}; //black and pink checker static byte nulltexture_data[16] = {127,191,255,255,0,0,0,255,0,0,0,255,127,191,255,255}; //black and blue checker extern texture_t *r_notexture_mip, *r_notexture_mip2; // init texture list free_gltextures = (gltexture_t *) Hunk_AllocName (MAX_GLTEXTURES * sizeof(gltexture_t), "gltextures"); active_gltextures = NULL; for (i = 0; i < MAX_GLTEXTURES - 1; i++) free_gltextures[i].next = &free_gltextures[i+1]; free_gltextures[i].next = NULL; numgltextures = 0; // palette TexMgr_LoadPalette (); Cvar_RegisterVariable (&gl_max_size); Cvar_RegisterVariable (&gl_picmip); Cvar_RegisterVariable (&gl_texture_anisotropy); Cvar_SetCallback (&gl_texture_anisotropy, &TexMgr_Anisotropy_f); gl_texturemode.string = glmodes[glmode_idx].name; Cvar_RegisterVariable (&gl_texturemode); Cvar_SetCallback (&gl_texturemode, &TexMgr_TextureMode_f); Cmd_AddCommand ("gl_describetexturemodes", &TexMgr_DescribeTextureModes_f); Cmd_AddCommand ("imagelist", &TexMgr_Imagelist_f); Cmd_AddCommand ("imagedump", &TexMgr_Imagedump_f); // poll max size from hardware glGetIntegerv (GL_MAX_TEXTURE_SIZE, &gl_hardware_maxsize); // load notexture images notexture = TexMgr_LoadImage (NULL, "notexture", 2, 2, SRC_RGBA, notexture_data, "", (src_offset_t)notexture_data, TEXPREF_NEAREST | TEXPREF_PERSIST | TEXPREF_NOPICMIP); nulltexture = TexMgr_LoadImage (NULL, "nulltexture", 2, 2, SRC_RGBA, nulltexture_data, "", (src_offset_t)nulltexture_data, TEXPREF_NEAREST | TEXPREF_PERSIST | TEXPREF_NOPICMIP); //have to assign these here becuase Mod_Init is called before TexMgr_Init r_notexture_mip->gltexture = r_notexture_mip2->gltexture = notexture; //set safe size for warpimages gl_warpimagesize = 0; TexMgr_RecalcWarpImageSize (); } /* ================================================================================ IMAGE LOADING ================================================================================ */ /* ================ TexMgr_Pad -- return smallest power of two greater than or equal to s ================ */ int TexMgr_Pad (int s) { int i; for (i = 1; i < s; i<<=1) ; return i; } /* =============== TexMgr_SafeTextureSize -- return a size with hardware and user prefs in mind =============== */ int TexMgr_SafeTextureSize (int s) { if (!gl_texture_NPOT) s = TexMgr_Pad(s); if ((int)gl_max_size.value > 0) s = q_min(TexMgr_Pad((int)gl_max_size.value), s); s = q_min(gl_hardware_maxsize, s); return s; } /* ================ TexMgr_PadConditional -- only pad if a texture of that size would be padded. (used for tex coords) ================ */ int TexMgr_PadConditional (int s) { if (s < TexMgr_SafeTextureSize(s)) return TexMgr_Pad(s); else return s; } /* ================ TexMgr_MipMapW ================ */ static unsigned *TexMgr_MipMapW (unsigned *data, int width, int height) { int i, size; byte *out, *in; out = in = (byte *)data; size = (width*height)>>1; for (i = 0; i < size; i++, out += 4, in += 8) { out[0] = (in[0] + in[4])>>1; out[1] = (in[1] + in[5])>>1; out[2] = (in[2] + in[6])>>1; out[3] = (in[3] + in[7])>>1; } return data; } /* ================ TexMgr_MipMapH ================ */ static unsigned *TexMgr_MipMapH (unsigned *data, int width, int height) { int i, j; byte *out, *in; out = in = (byte *)data; height>>=1; width<<=2; for (i = 0; i < height; i++, in += width) { for (j = 0; j < width; j += 4, out += 4, in += 4) { out[0] = (in[0] + in[width+0])>>1; out[1] = (in[1] + in[width+1])>>1; out[2] = (in[2] + in[width+2])>>1; out[3] = (in[3] + in[width+3])>>1; } } return data; } /* ================ TexMgr_ResampleTexture -- bilinear resample ================ */ static unsigned *TexMgr_ResampleTexture (unsigned *in, int inwidth, int inheight, qboolean alpha) { byte *nwpx, *nepx, *swpx, *sepx, *dest; unsigned xfrac, yfrac, x, y, modx, mody, imodx, imody, injump, outjump; unsigned *out; int i, j, outwidth, outheight; if (inwidth == TexMgr_Pad(inwidth) && inheight == TexMgr_Pad(inheight)) return in; outwidth = TexMgr_Pad(inwidth); outheight = TexMgr_Pad(inheight); out = (unsigned *) Hunk_Alloc(outwidth*outheight*4); xfrac = ((inwidth-1) << 16) / (outwidth-1); yfrac = ((inheight-1) << 16) / (outheight-1); y = outjump = 0; for (i = 0; i < outheight; i++) { mody = (y>>8) & 0xFF; imody = 256 - mody; injump = (y>>16) * inwidth; x = 0; for (j = 0; j < outwidth; j++) { modx = (x>>8) & 0xFF; imodx = 256 - modx; nwpx = (byte *)(in + (x>>16) + injump); nepx = nwpx + 4; swpx = nwpx + inwidth*4; sepx = swpx + 4; dest = (byte *)(out + outjump + j); dest[0] = (nwpx[0]*imodx*imody + nepx[0]*modx*imody + swpx[0]*imodx*mody + sepx[0]*modx*mody)>>16; dest[1] = (nwpx[1]*imodx*imody + nepx[1]*modx*imody + swpx[1]*imodx*mody + sepx[1]*modx*mody)>>16; dest[2] = (nwpx[2]*imodx*imody + nepx[2]*modx*imody + swpx[2]*imodx*mody + sepx[2]*modx*mody)>>16; if (alpha) dest[3] = (nwpx[3]*imodx*imody + nepx[3]*modx*imody + swpx[3]*imodx*mody + sepx[3]*modx*mody)>>16; else dest[3] = 255; x += xfrac; } outjump += outwidth; y += yfrac; } return out; } /* =============== TexMgr_AlphaEdgeFix eliminate pink edges on sprites, etc. operates in place on 32bit data =============== */ static void TexMgr_AlphaEdgeFix (byte *data, int width, int height) { int i, j, n = 0, b, c[3] = {0,0,0}, lastrow, thisrow, nextrow, lastpix, thispix, nextpix; byte *dest = data; for (i = 0; i < height; i++) { lastrow = width * 4 * ((i == 0) ? height-1 : i-1); thisrow = width * 4 * i; nextrow = width * 4 * ((i == height-1) ? 0 : i+1); for (j = 0; j < width; j++, dest += 4) { if (dest[3]) //not transparent continue; lastpix = 4 * ((j == 0) ? width-1 : j-1); thispix = 4 * j; nextpix = 4 * ((j == width-1) ? 0 : j+1); b = lastrow + lastpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = thisrow + lastpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = nextrow + lastpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = lastrow + thispix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = nextrow + thispix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = lastrow + nextpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = thisrow + nextpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} b = nextrow + nextpix; if (data[b+3]) {c[0] += data[b]; c[1] += data[b+1]; c[2] += data[b+2]; n++;} //average all non-transparent neighbors if (n) { dest[0] = (byte)(c[0]/n); dest[1] = (byte)(c[1]/n); dest[2] = (byte)(c[2]/n); n = c[0] = c[1] = c[2] = 0; } } } } /* =============== TexMgr_PadEdgeFixW -- special case of AlphaEdgeFix for textures that only need it because they were padded operates in place on 32bit data, and expects unpadded height and width values =============== */ static void TexMgr_PadEdgeFixW (byte *data, int width, int height) { byte *src, *dst; int i, padw, padh; padw = TexMgr_PadConditional(width); padh = TexMgr_PadConditional(height); //copy last full column to first empty column, leaving alpha byte at zero src = data + (width - 1) * 4; for (i = 0; i < padh; i++) { src[4] = src[0]; src[5] = src[1]; src[6] = src[2]; src += padw * 4; } //copy first full column to last empty column, leaving alpha byte at zero src = data; dst = data + (padw - 1) * 4; for (i = 0; i < padh; i++) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; src += padw * 4; dst += padw * 4; } } /* =============== TexMgr_PadEdgeFixH -- special case of AlphaEdgeFix for textures that only need it because they were padded operates in place on 32bit data, and expects unpadded height and width values =============== */ static void TexMgr_PadEdgeFixH (byte *data, int width, int height) { byte *src, *dst; int i, padw, padh; padw = TexMgr_PadConditional(width); padh = TexMgr_PadConditional(height); //copy last full row to first empty row, leaving alpha byte at zero dst = data + height * padw * 4; src = dst - padw * 4; for (i = 0; i < padw; i++) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; src += 4; dst += 4; } //copy first full row to last empty row, leaving alpha byte at zero dst = data + (padh - 1) * padw * 4; src = data; for (i = 0; i < padw; i++) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; src += 4; dst += 4; } } /* ================ TexMgr_8to32 ================ */ static unsigned *TexMgr_8to32 (byte *in, int pixels, unsigned int *usepal) { int i; unsigned *out, *data; out = data = (unsigned *) Hunk_Alloc(pixels*4); for (i = 0; i < pixels; i++) *out++ = usepal[*in++]; return data; } /* ================ TexMgr_PadImageW -- return image with width padded up to power-of-two dimentions ================ */ static byte *TexMgr_PadImageW (byte *in, int width, int height, byte padbyte) { int i, j, outwidth; byte *out, *data; if (width == TexMgr_Pad(width)) return in; outwidth = TexMgr_Pad(width); out = data = (byte *) Hunk_Alloc(outwidth*height); for (i = 0; i < height; i++) { for (j = 0; j < width; j++) *out++ = *in++; for ( ; j < outwidth; j++) *out++ = padbyte; } return data; } /* ================ TexMgr_PadImageH -- return image with height padded up to power-of-two dimentions ================ */ static byte *TexMgr_PadImageH (byte *in, int width, int height, byte padbyte) { int i, srcpix, dstpix; byte *data, *out; if (height == TexMgr_Pad(height)) return in; srcpix = width * height; dstpix = width * TexMgr_Pad(height); out = data = (byte *) Hunk_Alloc(dstpix); for (i = 0; i < srcpix; i++) *out++ = *in++; for ( ; i < dstpix; i++) *out++ = padbyte; return data; } /* ================ TexMgr_LoadImage32 -- handles 32bit source data ================ */ static void TexMgr_LoadImage32 (gltexture_t *glt, unsigned *data) { int internalformat, miplevel, mipwidth, mipheight, picmip; if (!gl_texture_NPOT) { // resample up data = TexMgr_ResampleTexture (data, glt->width, glt->height, glt->flags & TEXPREF_ALPHA); glt->width = TexMgr_Pad(glt->width); glt->height = TexMgr_Pad(glt->height); } // mipmap down picmip = (glt->flags & TEXPREF_NOPICMIP) ? 0 : q_max((int)gl_picmip.value, 0); mipwidth = TexMgr_SafeTextureSize (glt->width >> picmip); mipheight = TexMgr_SafeTextureSize (glt->height >> picmip); while ((int) glt->width > mipwidth) { TexMgr_MipMapW (data, glt->width, glt->height); glt->width >>= 1; if (glt->flags & TEXPREF_ALPHA) TexMgr_AlphaEdgeFix ((byte *)data, glt->width, glt->height); } while ((int) glt->height > mipheight) { TexMgr_MipMapH (data, glt->width, glt->height); glt->height >>= 1; if (glt->flags & TEXPREF_ALPHA) TexMgr_AlphaEdgeFix ((byte *)data, glt->width, glt->height); } // upload GL_Bind (glt); internalformat = (glt->flags & TEXPREF_ALPHA) ? gl_alpha_format : gl_solid_format; glTexImage2D (GL_TEXTURE_2D, 0, internalformat, glt->width, glt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // upload mipmaps if (glt->flags & TEXPREF_MIPMAP) { mipwidth = glt->width; mipheight = glt->height; for (miplevel=1; mipwidth > 1 || mipheight > 1; miplevel++) { if (mipwidth > 1) { TexMgr_MipMapW (data, mipwidth, mipheight); mipwidth >>= 1; } if (mipheight > 1) { TexMgr_MipMapH (data, mipwidth, mipheight); mipheight >>= 1; } glTexImage2D (GL_TEXTURE_2D, miplevel, internalformat, mipwidth, mipheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } } // set filter modes TexMgr_SetFilterModes (glt); } /* ================ TexMgr_LoadImage8 -- handles 8bit source data, then passes it to LoadImage32 ================ */ static void TexMgr_LoadImage8 (gltexture_t *glt, byte *data) { extern cvar_t gl_fullbrights; qboolean padw = false, padh = false; byte padbyte; unsigned int *usepal; int i; // HACK HACK HACK -- taken from tomazquake if (strstr(glt->name, "shot1sid") && glt->width == 32 && glt->height == 32 && CRC_Block(data, 1024) == 65393) { // This texture in b_shell1.bsp has some of the first 32 pixels painted white. // They are invisible in software, but look really ugly in GL. So we just copy // 32 pixels from the bottom to make it look nice. memcpy (data, data + 32*31, 32); } // detect false alpha cases if (glt->flags & TEXPREF_ALPHA && !(glt->flags & TEXPREF_CONCHARS)) { for (i = 0; i < (int) (glt->width * glt->height); i++) if (data[i] == 255) //transparent index break; if (i == (int) (glt->width * glt->height)) glt->flags -= TEXPREF_ALPHA; } // choose palette and padbyte if (glt->flags & TEXPREF_FULLBRIGHT) { if (glt->flags & TEXPREF_ALPHA) usepal = d_8to24table_fbright_fence; else usepal = d_8to24table_fbright; padbyte = 0; } else if (glt->flags & TEXPREF_NOBRIGHT && gl_fullbrights.value) { if (glt->flags & TEXPREF_ALPHA) usepal = d_8to24table_nobright_fence; else usepal = d_8to24table_nobright; padbyte = 0; } else if (glt->flags & TEXPREF_CONCHARS) { usepal = d_8to24table_conchars; padbyte = 0; } else { usepal = d_8to24table; padbyte = 255; } // pad each dimention, but only if it's not going to be downsampled later if (glt->flags & TEXPREF_PAD) { if ((int) glt->width < TexMgr_SafeTextureSize(glt->width)) { data = TexMgr_PadImageW (data, glt->width, glt->height, padbyte); glt->width = TexMgr_Pad(glt->width); padw = true; } if ((int) glt->height < TexMgr_SafeTextureSize(glt->height)) { data = TexMgr_PadImageH (data, glt->width, glt->height, padbyte); glt->height = TexMgr_Pad(glt->height); padh = true; } } // convert to 32bit data = (byte *)TexMgr_8to32(data, glt->width * glt->height, usepal); // fix edges if (glt->flags & TEXPREF_ALPHA) TexMgr_AlphaEdgeFix (data, glt->width, glt->height); else { if (padw) TexMgr_PadEdgeFixW (data, glt->source_width, glt->source_height); if (padh) TexMgr_PadEdgeFixH (data, glt->source_width, glt->source_height); } // upload it TexMgr_LoadImage32 (glt, (unsigned *)data); } /* ================ TexMgr_LoadLightmap -- handles lightmap data ================ */ static void TexMgr_LoadLightmap (gltexture_t *glt, byte *data) { // upload it GL_Bind (glt); glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes, glt->width, glt->height, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, data); // set filter modes TexMgr_SetFilterModes (glt); } /* ================ TexMgr_LoadImage -- the one entry point for loading all textures ================ */ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int height, enum srcformat format, byte *data, const char *source_file, src_offset_t source_offset, unsigned flags) { unsigned short crc; gltexture_t *glt; int mark; if (isDedicated) return NULL; // cache check switch (format) { case SRC_INDEXED: crc = CRC_Block(data, width * height); break; case SRC_LIGHTMAP: crc = CRC_Block(data, width * height * lightmap_bytes); break; case SRC_RGBA: crc = CRC_Block(data, width * height * 4); break; default: /* not reachable but avoids compiler warnings */ crc = 0; } if ((flags & TEXPREF_OVERWRITE) && (glt = TexMgr_FindTexture (owner, name))) { if (glt->source_crc == crc) return glt; } else glt = TexMgr_NewTexture (); // copy data glt->owner = owner; q_strlcpy (glt->name, name, sizeof(glt->name)); glt->width = width; glt->height = height; glt->flags = flags; glt->shirt = -1; glt->pants = -1; q_strlcpy (glt->source_file, source_file, sizeof(glt->source_file)); glt->source_offset = source_offset; glt->source_format = format; glt->source_width = width; glt->source_height = height; glt->source_crc = crc; //upload it mark = Hunk_LowMark(); switch (glt->source_format) { case SRC_INDEXED: TexMgr_LoadImage8 (glt, data); break; case SRC_LIGHTMAP: TexMgr_LoadLightmap (glt, data); break; case SRC_RGBA: TexMgr_LoadImage32 (glt, (unsigned *)data); break; } Hunk_FreeToLowMark(mark); return glt; } /* ================================================================================ COLORMAPPING AND TEXTURE RELOADING ================================================================================ */ /* ================ TexMgr_ReloadImage -- reloads a texture, and colormaps it if needed ================ */ void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants) { byte translation[256]; byte *src, *dst, *data = NULL, *translated; int mark, size, i; // // get source data // mark = Hunk_LowMark (); if (glt->source_file[0] && glt->source_offset) { //lump inside file long size; FILE *f; COM_FOpenFile(glt->source_file, &f, NULL); if (!f) goto invalid; fseek (f, glt->source_offset, SEEK_CUR); size = (long) (glt->source_width * glt->source_height); /* should be SRC_INDEXED, but no harm being paranoid: */ if (glt->source_format == SRC_RGBA) size *= 4; else if (glt->source_format == SRC_LIGHTMAP) size *= lightmap_bytes; data = (byte *) Hunk_Alloc (size); fread (data, 1, size, f); fclose (f); } else if (glt->source_file[0] && !glt->source_offset) data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height); //simple file else if (!glt->source_file[0] && glt->source_offset) data = (byte *) glt->source_offset; //image in memory if (!data) { invalid: Con_Printf ("TexMgr_ReloadImage: invalid source for %s\n", glt->name); Hunk_FreeToLowMark(mark); return; } glt->width = glt->source_width; glt->height = glt->source_height; // // apply shirt and pants colors // // if shirt and pants are -1,-1, use existing shirt and pants colors // if existing shirt and pants colors are -1,-1, don't bother colormapping if (shirt > -1 && pants > -1) { if (glt->source_format == SRC_INDEXED) { glt->shirt = shirt; glt->pants = pants; } else Con_Printf ("TexMgr_ReloadImage: can't colormap a non SRC_INDEXED texture: %s\n", glt->name); } if (glt->shirt > -1 && glt->pants > -1) { //create new translation table for (i = 0; i < 256; i++) translation[i] = i; shirt = glt->shirt * 16; if (shirt < 128) { for (i = 0; i < 16; i++) translation[TOP_RANGE+i] = shirt + i; } else { for (i = 0; i < 16; i++) translation[TOP_RANGE+i] = shirt+15-i; } pants = glt->pants * 16; if (pants < 128) { for (i = 0; i < 16; i++) translation[BOTTOM_RANGE+i] = pants + i; } else { for (i = 0; i < 16; i++) translation[BOTTOM_RANGE+i] = pants+15-i; } //translate texture size = glt->width * glt->height; dst = translated = (byte *) Hunk_Alloc (size); src = data; for (i = 0; i < size; i++) *dst++ = translation[*src++]; data = translated; } // // upload it // switch (glt->source_format) { case SRC_INDEXED: TexMgr_LoadImage8 (glt, data); break; case SRC_LIGHTMAP: TexMgr_LoadLightmap (glt, data); break; case SRC_RGBA: TexMgr_LoadImage32 (glt, (unsigned *)data); break; } Hunk_FreeToLowMark(mark); } /* ================ TexMgr_ReloadImages -- reloads all texture images. called only by vid_restart ================ */ void TexMgr_ReloadImages (void) { gltexture_t *glt; // ericw -- tricky bug: if the hunk is almost full, an allocation in TexMgr_ReloadImage // triggers cache items to be freed, which calls back into TexMgr to free the // texture. If this frees 'glt' in the loop below, the active_gltextures // list gets corrupted. // A test case is jam3_tronyn.bsp with -heapsize 65536, and do several mode // switches/fullscreen toggles // 2015-09-04 -- Cache_Flush workaround was causing issues (http://sourceforge.net/p/quakespasm/bugs/10/) // switching to a boolean flag. in_reload_images = true; for (glt = active_gltextures; glt; glt = glt->next) { glGenTextures(1, &glt->texnum); TexMgr_ReloadImage (glt, -1, -1); } in_reload_images = false; } /* ================ TexMgr_ReloadNobrightImages -- reloads all texture that were loaded with the nobright palette. called when gl_fullbrights changes ================ */ void TexMgr_ReloadNobrightImages (void) { gltexture_t *glt; for (glt = active_gltextures; glt; glt = glt->next) if (glt->flags & TEXPREF_NOBRIGHT) TexMgr_ReloadImage(glt, -1, -1); } /* ================================================================================ TEXTURE BINDING / TEXTURE UNIT SWITCHING ================================================================================ */ static GLuint currenttexture[3] = {GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE}; // to avoid unnecessary texture sets static GLenum currenttarget = GL_TEXTURE0_ARB; qboolean mtexenabled = false; /* ================ GL_SelectTexture -- johnfitz -- rewritten ================ */ void GL_SelectTexture (GLenum target) { if (target == currenttarget) return; GL_SelectTextureFunc(target); currenttarget = target; } /* ================ GL_DisableMultitexture -- selects texture unit 0 ================ */ void GL_DisableMultitexture(void) { if (mtexenabled) { glDisable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE0_ARB); mtexenabled = false; } } /* ================ GL_EnableMultitexture -- selects texture unit 1 ================ */ void GL_EnableMultitexture(void) { if (gl_mtexable) { GL_SelectTexture(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); mtexenabled = true; } } /* ================ GL_Bind -- johnfitz -- heavy revision ================ */ void GL_Bind (gltexture_t *texture) { if (!texture) texture = nulltexture; if (texture->texnum != currenttexture[currenttarget - GL_TEXTURE0_ARB]) { currenttexture[currenttarget - GL_TEXTURE0_ARB] = texture->texnum; glBindTexture (GL_TEXTURE_2D, texture->texnum); texture->visframe = r_framecount; } } /* ================ GL_DeleteTexture -- ericw Wrapper around glDeleteTextures that also clears the given texture number from our per-TMU cached texture binding table. ================ */ static void GL_DeleteTexture (gltexture_t *texture) { glDeleteTextures (1, &texture->texnum); if (texture->texnum == currenttexture[0]) currenttexture[0] = GL_UNUSED_TEXTURE; if (texture->texnum == currenttexture[1]) currenttexture[1] = GL_UNUSED_TEXTURE; if (texture->texnum == currenttexture[2]) currenttexture[2] = GL_UNUSED_TEXTURE; texture->texnum = 0; } /* ================ GL_ClearBindings -- ericw Invalidates cached bindings, so the next GL_Bind calls for each TMU will make real glBindTexture calls. Call this after changing the binding outside of GL_Bind. ================ */ void GL_ClearBindings(void) { int i; for (i = 0; i < 3; i++) { currenttexture[i] = GL_UNUSED_TEXTURE; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/main_sdl.c������������������������������������������������������������������0000644�0000000�0000000�00000011165�13203353462�015460� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif #include <stdio.h> #if defined(USE_SDL2) /* need at least SDL_2.0.0 */ #define SDL_MIN_X 2 #define SDL_MIN_Y 0 #define SDL_MIN_Z 0 #define SDL_REQUIREDVERSION (SDL_VERSIONNUM(SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z)) #define SDL_NEW_VERSION_REJECT (SDL_VERSIONNUM(3,0,0)) #else /* need at least SDL_1.2.10 */ #define SDL_MIN_X 1 #define SDL_MIN_Y 2 #define SDL_MIN_Z 10 #define SDL_REQUIREDVERSION (SDL_VERSIONNUM(SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z)) /* reject 1.3.0 and newer at runtime. */ #define SDL_NEW_VERSION_REJECT (SDL_VERSIONNUM(1,3,0)) #endif static void Sys_AtExit (void) { SDL_Quit(); } static void Sys_InitSDL (void) { #if defined(USE_SDL2) SDL_version v; SDL_version *sdl_version = &v; SDL_GetVersion(&v); #else const SDL_version *sdl_version = SDL_Linked_Version(); #endif Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); if (SDL_VERSIONNUM(sdl_version->major,sdl_version->minor,sdl_version->patch) < SDL_REQUIREDVERSION) { /*reject running under older SDL versions */ Sys_Error("You need at least v%d.%d.%d of SDL to run this game.", SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z); } if (SDL_VERSIONNUM(sdl_version->major,sdl_version->minor,sdl_version->patch) >= SDL_NEW_VERSION_REJECT) { /*reject running under newer (1.3.x) SDL */ Sys_Error("Your version of SDL library is incompatible with me.\n" "You need a library version in the line of %d.%d.%d\n", SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z); } if (SDL_Init(0) < 0) { Sys_Error("Couldn't init SDL: %s", SDL_GetError()); } atexit(Sys_AtExit); } #define DEFAULT_MEMORY (256 * 1024 * 1024) // ericw -- was 72MB (64-bit) / 64MB (32-bit) static quakeparms_t parms; // On OS X we call SDL_main from the launcher, but SDL2 doesn't redefine main // as SDL_main on OS X anymore, so we do it ourselves. #if defined(USE_SDL2) && defined(__APPLE__) #define main SDL_main #endif int main(int argc, char *argv[]) { int t; double time, oldtime, newtime; host_parms = &parms; parms.basedir = "."; parms.argc = argc; parms.argv = argv; COM_InitArgv(parms.argc, parms.argv); isDedicated = (COM_CheckParm("-dedicated") != 0); Sys_InitSDL (); Sys_Init(); parms.memsize = DEFAULT_MEMORY; if (COM_CheckParm("-heapsize")) { t = COM_CheckParm("-heapsize") + 1; if (t < com_argc) parms.memsize = Q_atoi(com_argv[t]) * 1024; } parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Not enough memory free; check disk space\n"); Sys_Printf("Quake %1.2f (c) id Software\n", VERSION); Sys_Printf("GLQuake %1.2f (c) id Software\n", GLQUAKE_VERSION); Sys_Printf("FitzQuake %1.2f (c) John Fitzgibbons\n", FITZQUAKE_VERSION); Sys_Printf("FitzQuake SDL port (c) SleepwalkR, Baker\n"); Sys_Printf("QuakeSpasm " QUAKESPASM_VER_STRING " (c) Ozkan Sezer, Eric Wasylishen & others\n"); Sys_Printf("Host_Init\n"); Host_Init(); oldtime = Sys_DoubleTime(); if (isDedicated) { while (1) { newtime = Sys_DoubleTime (); time = newtime - oldtime; while (time < sys_ticrate.value ) { SDL_Delay(1); newtime = Sys_DoubleTime (); time = newtime - oldtime; } Host_Frame (time); oldtime = newtime; } } else while (1) { /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { SDL_Delay(16); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; SDL_Delay(32); } else { scr_skipupdate = 0; } newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value && !cls.timedemo) SDL_Delay(1); oldtime = newtime; } return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_wins.h������������������������������������������������������������������0000644�0000000�0000000�00000003556�12407762022�015533� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __NET_WINSOCK_H #define __NET_WINSOCK_H sys_socket_t WINS_Init (void); void WINS_Shutdown (void); void WINS_Listen (qboolean state); sys_socket_t WINS_OpenSocket (int port); int WINS_CloseSocket (sys_socket_t socketid); int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t WINS_CheckNewConnections (void); int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *WINS_AddrToString (struct qsockaddr *addr); int WINS_StringToAddr (const char *string, struct qsockaddr *addr); int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name); int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr); int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int WINS_GetSocketPort (struct qsockaddr *addr); int WINS_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __NET_WINSOCK_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_xmp.c�������������������������������������������������������������������0000644�0000000�0000000�00000010605�13060355015�015335� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* tracker music (module file) decoding support using libxmp >= v4.2.0 * https://sourceforge.net/projects/xmp/ * https://github.com/cmatsuoka/libxmp.git * * Copyright (C) 2016 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_XMP) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_xmp.h" #if defined(_WIN32) && defined(XMP_NO_DLL) #define BUILDING_STATIC #endif #include <xmp.h> #if ((XMP_VERCODE+0) < 0x040200) #error libxmp version 4.2 or newer is required #endif static int S_XMP_StartPlay (snd_stream_t *stream) { int fmt = 0; if (stream->info.channels == 1) fmt |= XMP_FORMAT_MONO; if (stream->info.width == 1) fmt |= XMP_FORMAT_8BIT|XMP_FORMAT_UNSIGNED; return xmp_start_player((xmp_context)stream->priv, stream->info.rate, fmt); } static qboolean S_XMP_CodecInitialize (void) { return true; } static void S_XMP_CodecShutdown (void) { } static qboolean S_XMP_CodecOpenStream (snd_stream_t *stream) { /* need to load the whole file into memory and pass it to libxmp * using xmp_load_module_from_memory() which requires libxmp >= 4.2. * libxmp-4.0/4.1 only have xmp_load_module() which accepts a file * name which isn't good with files in containers like paks, etc. */ xmp_context c; byte *moddata; long len; int mark; c = xmp_create_context(); if (c == NULL) return false; len = FS_filelength (&stream->fh); mark = Hunk_LowMark(); moddata = (byte *) Hunk_Alloc(len); FS_fread(moddata, 1, len, &stream->fh); if (xmp_load_module_from_memory(c, moddata, len) != 0) { Con_DPrintf("Could not load module %s\n", stream->name); goto err1; } Hunk_FreeToLowMark(mark); /* free original file data */ stream->priv = c; if (shm->speed > XMP_MAX_SRATE) stream->info.rate = 44100; else if (shm->speed < XMP_MIN_SRATE) stream->info.rate = 11025; else stream->info.rate = shm->speed; stream->info.bits = shm->samplebits; stream->info.width = stream->info.bits / 8; stream->info.channels = shm->channels; if (S_XMP_StartPlay(stream) != 0) goto err2; /* percentual left/right channel separation, default is 70. */ if (stream->info.channels == 2) if (xmp_set_player(c, XMP_PLAYER_MIX, 100) != 0) goto err3; /* interpolation type, default is XMP_INTERP_LINEAR */ if (xmp_set_player(c, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE) != 0) goto err3; return true; err3: xmp_end_player(c); err2: xmp_release_module(c); err1: xmp_free_context(c); return false; } static int S_XMP_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int r; /* xmp_play_buffer() requires libxmp >= 4.1. it will write * native-endian pcm data to the buffer. if the data write * is partial, the rest of the buffer will be zero-filled. * the last param is the number that the current sequence of * the song will be looped at max. */ r = xmp_play_buffer((xmp_context)stream->priv, buffer, bytes, 1); if (r == 0) { return bytes; } if (r == -XMP_END) { Con_DPrintf("XMP EOF\n"); return 0; } return -1; } static void S_XMP_CodecCloseStream (snd_stream_t *stream) { xmp_context c = (xmp_context)stream->priv; xmp_end_player(c); xmp_release_module(c); xmp_free_context(c); S_CodecUtilClose(&stream); } static int S_XMP_CodecRewindStream (snd_stream_t *stream) { int ret; ret = S_XMP_StartPlay(stream); if (ret < 0) return ret; /*ret = xmp_set_position((xmp_context)stream->priv, 0);*/ ret = xmp_seek_time((xmp_context)stream->priv, 0); if (ret < 0) return ret; return 0; } snd_codec_t xmp_codec = { CODECTYPE_MOD, true, /* always available. */ "s3m", S_XMP_CodecInitialize, S_XMP_CodecShutdown, S_XMP_CodecOpenStream, S_XMP_CodecReadStream, S_XMP_CodecRewindStream, S_XMP_CodecCloseStream, NULL }; #endif /* USE_CODEC_XMP */ ���������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_udp.c�������������������������������������������������������������������0000644�0000000�0000000�00000027237�13077775036�015355� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static sys_socket_t net_broadcastsocket = 0; static struct sockaddr_in broadcastaddr; static in_addr_t myAddr; #include "net_udp.h" //============================================================================= sys_socket_t UDP_Init (void) { int err; char *colon; char buff[MAXHOSTNAMELEN]; struct hostent *local; struct qsockaddr addr; if (COM_CheckParm ("-noudp")) return INVALID_SOCKET; // determine my name & address myAddr = htonl(INADDR_LOOPBACK); if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("UDP_Init: gethostname failed (%s)\n", socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; // ericw -- if our hostname ends in ".local" (a macOS thing), // don't bother calling gethostbyname(), because it blocks for a few seconds // and then fails (on my system anyway.) if (strstr(buff, ".local") == (buff + strlen(buff) - 6)) { Con_SafePrintf("UDP_Init: skipping gethostbyname for %s\n", buff); goto skip_gethostbyname; } local = gethostbyname(buff); if (local == NULL) { Con_SafePrintf("UDP_Init: gethostbyname failed (%s)\n", hstrerror(h_errno)); } else if (local->h_addrtype != AF_INET) { Con_SafePrintf("UDP_Init: address from gethostbyname not IPv4\n"); } else { myAddr = *(in_addr_t *)local->h_addr_list[0]; } } skip_gethostbyname: if ((net_controlsocket = UDP_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("UDP_Init: Unable to open control socket, UDP disabled\n"); return INVALID_SOCKET; } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST; broadcastaddr.sin_port = htons((unsigned short)net_hostport); UDP_GetSocketAddr (net_controlsocket, &addr); strcpy(my_tcpip_address, UDP_AddrToString (&addr)); colon = strrchr (my_tcpip_address, ':'); if (colon) *colon = 0; Con_SafePrintf("UDP Initialized\n"); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void UDP_Shutdown (void) { UDP_Listen (false); UDP_CloseSocket (net_controlsocket); } //============================================================================= void UDP_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("UDP_Listen: Unable to open accept socket"); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; UDP_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t UDP_OpenSocket (int port) { sys_socket_t newsocket; struct sockaddr_in address; int _true = 1; int err; if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_in)); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) return newsocket; ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err)); UDP_CloseSocket (newsocket); return INVALID_SOCKET; } //============================================================================= int UDP_CloseSocket (sys_socket_t socketid) { if (socketid == net_broadcastsocket) net_broadcastsocket = 0; return closesocket (socketid); } //============================================================================= /* ============ PartialIPAddress this lets you type only as much of the net address as required, using the local network components to fill in the rest ============ */ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr) { char buff[256]; char *b; int addr, mask, num, port, run; buff[0] = '.'; b = buff; strcpy(buff+1, in); if (buff[1] == '.') b++; addr = 0; mask = -1; while (*b == '.') { b++; num = 0; run = 0; while (!( *b < '0' || *b > '9')) { num = num*10 + *b++ - '0'; if (++run > 3) return -1; } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) return -1; if (num < 0 || num > 255) return -1; mask <<= 8; addr = (addr<<8) + num; } if (*b++ == ':') port = atoi(b); else port = net_hostport; hostaddr->qsa_family = AF_INET; ((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port); ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); return 0; } //============================================================================= int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t UDP_CheckNewConnections (void) { int available; struct sockaddr_in from; socklen_t fromlen; char buff[1]; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (ioctl (net_acceptsocket, FIONREAD, &available) == -1) { int err = SOCKETERRNO; Sys_Error ("UDP: ioctlsocket (FIONREAD) failed (%s)", socketerror(err)); } if (available) return net_acceptsocket; // quietly absorb empty packets recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from, &fromlen); return INVALID_SOCKET; } //============================================================================= int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); int ret; ret = recvfrom (socketid, buf, len, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafePrintf ("UDP_Read, recvfrom: %s\n", socketerror(err)); } return ret; } //============================================================================= static int UDP_MakeSocketBroadcastCapable (sys_socket_t socketid) { int i = 1; // make this socket broadcast capable if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR) { int err = SOCKETERRNO; Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err)); return -1; } net_broadcastsocket = socketid; return 0; } //============================================================================= int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len) { int ret; if (socketid != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets"); ret = UDP_MakeSocketBroadcastCapable (socketid); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return UDP_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sendto (socketid, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafePrintf ("UDP_Write, sendto: %s\n", socketerror(err)); } return ret; } //============================================================================= const char *UDP_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); q_snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); return buffer; } //============================================================================= int UDP_StringToAddr (const char *string, struct qsockaddr *addr) { int ha1, ha2, ha3, ha4, hp, ipaddr; sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); return 0; } //============================================================================= int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); in_addr_t a; memset(addr, 0, sizeof(struct qsockaddr)); if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0) return -1; a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; if (a == 0 || a == htonl(INADDR_LOOPBACK)) ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; return 0; } //============================================================================= int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name) { struct hostent *hostentry; hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); if (hostentry) { strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); return 0; } strcpy (name, UDP_AddrToString (addr)); return 0; } //============================================================================= int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr) { struct hostent *hostentry; if (name[0] >= '0' && name[0] <= '9') return PartialIPAddress (name, addr); hostentry = gethostbyname (name); if (!hostentry) return -1; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(in_addr_t *)hostentry->h_addr_list[0]; return 0; } //============================================================================= int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) return -1; if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) return 1; return 0; } //============================================================================= int UDP_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_in *)addr)->sin_port); } int UDP_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); return 0; } //============================================================================= �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_win.c�������������������������������������������������������������������0000644�0000000�0000000�00000005033�12407762022�015333� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" #include "net_loop.h" net_driver_t net_drivers[] = { { "Loopback", false, Loop_Init, Loop_Listen, Loop_SearchForHosts, Loop_Connect, Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown }, { "Datagram", false, Datagram_Init, Datagram_Listen, Datagram_SearchForHosts, Datagram_Connect, Datagram_CheckNewConnections, Datagram_GetMessage, Datagram_SendMessage, Datagram_SendUnreliableMessage, Datagram_CanSendMessage, Datagram_CanSendUnreliableMessage, Datagram_Close, Datagram_Shutdown } }; const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0])); #include "net_wins.h" #include "net_wipx.h" net_landriver_t net_landrivers[] = { { "Winsock TCPIP", false, 0, WINS_Init, WINS_Shutdown, WINS_Listen, WINS_OpenSocket, WINS_CloseSocket, WINS_Connect, WINS_CheckNewConnections, WINS_Read, WINS_Write, WINS_Broadcast, WINS_AddrToString, WINS_StringToAddr, WINS_GetSocketAddr, WINS_GetNameFromAddr, WINS_GetAddrFromName, WINS_AddrCompare, WINS_GetSocketPort, WINS_SetSocketPort }, { "Winsock IPX", false, 0, WIPX_Init, WIPX_Shutdown, WIPX_Listen, WIPX_OpenSocket, WIPX_CloseSocket, WIPX_Connect, WIPX_CheckNewConnections, WIPX_Read, WIPX_Write, WIPX_Broadcast, WIPX_AddrToString, WIPX_StringToAddr, WIPX_GetSocketAddr, WIPX_GetNameFromAddr, WIPX_GetAddrFromName, WIPX_AddrCompare, WIPX_GetSocketPort, WIPX_SetSocketPort } }; const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0])); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_umx.h�������������������������������������������������������������������0000644�0000000�0000000�00000000302�12220541170�015334� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Unreal UMX format support */ #if !defined(_SND_UMX_H_) #define _SND_UMX_H_ #if defined(USE_CODEC_UMX) extern snd_codec_t umx_codec; #endif /* USE_CODEC_UMX */ #endif /* ! _SND_UMX_H_ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_texmgr.h�����������������������������������������������������������������0000644�0000000�0000000�00000010375�12633216330�015667� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _GL_TEXMAN_H #define _GL_TEXMAN_H //gl_texmgr.h -- fitzquake's texture manager. manages opengl texture images #define TEXPREF_NONE 0x0000 #define TEXPREF_MIPMAP 0x0001 // generate mipmaps // TEXPREF_NEAREST and TEXPREF_LINEAR aren't supposed to be ORed with TEX_MIPMAP #define TEXPREF_LINEAR 0x0002 // force linear #define TEXPREF_NEAREST 0x0004 // force nearest #define TEXPREF_ALPHA 0x0008 // allow alpha #define TEXPREF_PAD 0x0010 // allow padding #define TEXPREF_PERSIST 0x0020 // never free #define TEXPREF_OVERWRITE 0x0040 // overwrite existing same-name texture #define TEXPREF_NOPICMIP 0x0080 // always load full-sized #define TEXPREF_FULLBRIGHT 0x0100 // use fullbright mask palette #define TEXPREF_NOBRIGHT 0x0200 // use nobright mask palette #define TEXPREF_CONCHARS 0x0400 // use conchars palette #define TEXPREF_WARPIMAGE 0x0800 // resize this texture when warpimagesize changes enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA}; typedef uintptr_t src_offset_t; typedef struct gltexture_s { //managed by texture manager GLuint texnum; struct gltexture_s *next; qmodel_t *owner; //managed by image loading char name[64]; unsigned int width; //size of image as it exists in opengl unsigned int height; //size of image as it exists in opengl unsigned int flags; char source_file[MAX_QPATH]; //relative filepath to data source, or "" if source is in memory src_offset_t source_offset; //byte offset into file, or memory address enum srcformat source_format; //format of pixel data (indexed, lightmap, or rgba) unsigned int source_width; //size of image in source data unsigned int source_height; //size of image in source data unsigned short source_crc; //generated by source data before modifications char shirt; //0-13 shirt color, or -1 if never colormapped char pants; //0-13 pants color, or -1 if never colormapped //used for rendering int visframe; //matches r_framecount if texture was bound this frame } gltexture_t; extern gltexture_t *notexture; extern gltexture_t *nulltexture; extern unsigned int d_8to24table[256]; extern unsigned int d_8to24table_fbright[256]; extern unsigned int d_8to24table_nobright[256]; extern unsigned int d_8to24table_conchars[256]; extern unsigned int d_8to24table_shirt[256]; extern unsigned int d_8to24table_pants[256]; // TEXTURE MANAGER float TexMgr_FrameUsage (void); gltexture_t *TexMgr_FindTexture (qmodel_t *owner, const char *name); gltexture_t *TexMgr_NewTexture (void); void TexMgr_FreeTexture (gltexture_t *kill); void TexMgr_FreeTextures (unsigned int flags, unsigned int mask); void TexMgr_FreeTexturesForOwner (qmodel_t *owner); void TexMgr_NewGame (void); void TexMgr_Init (void); void TexMgr_DeleteTextureObjects (void); // IMAGE LOADING gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int height, enum srcformat format, byte *data, const char *source_file, src_offset_t source_offset, unsigned flags); void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants); void TexMgr_ReloadImages (void); void TexMgr_ReloadNobrightImages (void); int TexMgr_Pad(int s); int TexMgr_SafeTextureSize (int s); int TexMgr_PadConditional (int s); // TEXTURE BINDING & TEXTURE UNIT SWITCHING void GL_SelectTexture (GLenum target); void GL_DisableMultitexture (void); //selects texture unit 0 void GL_EnableMultitexture (void); //selects texture unit 1 void GL_Bind (gltexture_t *texture); void GL_ClearBindings (void); #endif /* _GL_TEXMAN_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_dma.c�������������������������������������������������������������������0000644�0000000�0000000�00000054637�12636166045�015322� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2011 O. Sezer <sezero@users.sourceforge.net> Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_dma.c -- main control for any streaming sound output device #include "quakedef.h" #include "snd_codec.h" #include "bgmusic.h" static void S_Play (void); static void S_PlayVol (void); static void S_SoundList (void); static void S_Update_ (void); void S_StopAllSounds (qboolean clear); static void S_StopAllSoundsC (void); // ======================================================================= // Internal sound data & structures // ======================================================================= channel_t snd_channels[MAX_CHANNELS]; int total_channels; static int snd_blocked = 0; static qboolean snd_initialized = false; static dma_t sn; volatile dma_t *shm = NULL; vec3_t listener_origin; vec3_t listener_forward; vec3_t listener_right; vec3_t listener_up; #define sound_nominal_clip_dist 1000.0 int soundtime; // sample PAIRS int paintedtime; // sample PAIRS int s_rawend; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; #define MAX_SFX 1024 static sfx_t *known_sfx = NULL; // hunk allocated [MAX_SFX] static int num_sfx; static sfx_t *ambient_sfx[NUM_AMBIENTS]; static qboolean sound_started = false; cvar_t bgmvolume = {"bgmvolume", "1", CVAR_ARCHIVE}; cvar_t sfxvolume = {"volume", "0.7", CVAR_ARCHIVE}; cvar_t precache = {"precache", "1", CVAR_NONE}; cvar_t loadas8bit = {"loadas8bit", "0", CVAR_NONE}; cvar_t sndspeed = {"sndspeed", "11025", CVAR_NONE}; cvar_t snd_mixspeed = {"snd_mixspeed", "44100", CVAR_NONE}; #if defined(_WIN32) #define SND_FILTERQUALITY_DEFAULT "5" #else #define SND_FILTERQUALITY_DEFAULT "1" #endif cvar_t snd_filterquality = {"snd_filterquality", SND_FILTERQUALITY_DEFAULT, CVAR_NONE}; static cvar_t nosound = {"nosound", "0", CVAR_NONE}; static cvar_t ambient_level = {"ambient_level", "0.3", CVAR_NONE}; static cvar_t ambient_fade = {"ambient_fade", "100", CVAR_NONE}; static cvar_t snd_noextraupdate = {"snd_noextraupdate", "0", CVAR_NONE}; static cvar_t snd_show = {"snd_show", "0", CVAR_NONE}; static cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", CVAR_ARCHIVE}; static void S_SoundInfo_f (void) { if (!sound_started || !shm) { Con_Printf ("sound system not started\n"); return; } Con_Printf("%d bit, %s, %d Hz\n", shm->samplebits, (shm->channels == 2) ? "stereo" : "mono", shm->speed); Con_Printf("%5d samples\n", shm->samples); Con_Printf("%5d samplepos\n", shm->samplepos); Con_Printf("%5d submission_chunk\n", shm->submission_chunk); Con_Printf("%5d total_channels\n", total_channels); Con_Printf("%p dma buffer\n", shm->buffer); } static void SND_Callback_sfxvolume (cvar_t *var) { SND_InitScaletable (); } static void SND_Callback_snd_filterquality (cvar_t *var) { if (snd_filterquality.value < 1 || snd_filterquality.value > 5) { Con_Printf ("snd_filterquality must be between 1 and 5\n"); Cvar_SetQuick (&snd_filterquality, SND_FILTERQUALITY_DEFAULT); } } /* ================ S_Startup ================ */ void S_Startup (void) { if (!snd_initialized) return; sound_started = SNDDMA_Init(&sn); if (!sound_started) { Con_Printf("Failed initializing sound\n"); } else { Con_Printf("Audio: %d bit, %s, %d Hz\n", shm->samplebits, (shm->channels == 2) ? "stereo" : "mono", shm->speed); } } /* ================ S_Init ================ */ void S_Init (void) { int i; if (snd_initialized) { Con_Printf("Sound is already initialized\n"); return; } Cvar_RegisterVariable(&nosound); Cvar_RegisterVariable(&sfxvolume); Cvar_RegisterVariable(&precache); Cvar_RegisterVariable(&loadas8bit); Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&ambient_level); Cvar_RegisterVariable(&ambient_fade); Cvar_RegisterVariable(&snd_noextraupdate); Cvar_RegisterVariable(&snd_show); Cvar_RegisterVariable(&_snd_mixahead); Cvar_RegisterVariable(&sndspeed); Cvar_RegisterVariable(&snd_mixspeed); Cvar_RegisterVariable(&snd_filterquality); if (safemode || COM_CheckParm("-nosound")) return; Con_Printf("\nSound Initialization\n"); Cmd_AddCommand("play", S_Play); Cmd_AddCommand("playvol", S_PlayVol); Cmd_AddCommand("stopsound", S_StopAllSoundsC); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); i = COM_CheckParm("-sndspeed"); if (i && i < com_argc-1) { Cvar_SetQuick (&sndspeed, com_argv[i+1]); } i = COM_CheckParm("-mixspeed"); if (i && i < com_argc-1) { Cvar_SetQuick (&snd_mixspeed, com_argv[i+1]); } if (host_parms->memsize < 0x800000) { Cvar_SetQuick (&loadas8bit, "1"); Con_Printf ("loading all sounds as 8bit\n"); } Cvar_SetCallback(&sfxvolume, SND_Callback_sfxvolume); Cvar_SetCallback(&snd_filterquality, &SND_Callback_snd_filterquality); SND_InitScaletable (); known_sfx = (sfx_t *) Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); num_sfx = 0; snd_initialized = true; S_Startup (); if (sound_started == 0) return; // provides a tick sound until washed clean // if (shm->buffer) // shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); S_CodecInit (); S_StopAllSounds (true); } // ======================================================================= // Shutdown sound engine // ======================================================================= void S_Shutdown (void) { if (!sound_started) return; sound_started = 0; snd_blocked = 0; S_CodecShutdown(); SNDDMA_Shutdown(); shm = NULL; } // ======================================================================= // Load a sound // ======================================================================= /* ================== S_FindName ================== */ static sfx_t *S_FindName (const char *name) { int i; sfx_t *sfx; if (!name) Sys_Error ("S_FindName: NULL"); if (Q_strlen(name) >= MAX_QPATH) Sys_Error ("Sound name too long: %s", name); // see if already loaded for (i = 0; i < num_sfx; i++) { if (!Q_strcmp(known_sfx[i].name, name)) { return &known_sfx[i]; } } if (num_sfx == MAX_SFX) Sys_Error ("S_FindName: out of sfx_t"); sfx = &known_sfx[i]; q_strlcpy (sfx->name, name, sizeof(sfx->name)); num_sfx++; return sfx; } /* ================== S_TouchSound ================== */ void S_TouchSound (const char *name) { sfx_t *sfx; if (!sound_started) return; sfx = S_FindName (name); Cache_Check (&sfx->cache); } /* ================== S_PrecacheSound ================== */ sfx_t *S_PrecacheSound (const char *name) { sfx_t *sfx; if (!sound_started || nosound.value) return NULL; sfx = S_FindName (name); // cache it in if (precache.value) S_LoadSound (sfx); return sfx; } //============================================================================= /* ================= SND_PickChannel picks a channel based on priorities, empty slots, number of channels ================= */ channel_t *SND_PickChannel (int entnum, int entchannel) { int ch_idx; int first_to_die; int life_left; // Check for replacement sound, or find the best one to replace first_to_die = -1; life_left = 0x7fffffff; for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; ch_idx++) { if (entchannel != 0 // channel 0 never overrides && snd_channels[ch_idx].entnum == entnum && (snd_channels[ch_idx].entchannel == entchannel || entchannel == -1) ) { // always override sound from same entity first_to_die = ch_idx; break; } // don't let monster sounds override player sounds if (snd_channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && snd_channels[ch_idx].sfx) continue; if (snd_channels[ch_idx].end - paintedtime < life_left) { life_left = snd_channels[ch_idx].end - paintedtime; first_to_die = ch_idx; } } if (first_to_die == -1) return NULL; if (snd_channels[first_to_die].sfx) snd_channels[first_to_die].sfx = NULL; return &snd_channels[first_to_die]; } /* ================= SND_Spatialize spatializes a channel ================= */ void SND_Spatialize (channel_t *ch) { vec_t dot; vec_t dist; vec_t lscale, rscale, scale; vec3_t source_vec; // anything coming from the view entity will always be full volume if (ch->entnum == cl.viewentity) { ch->leftvol = ch->master_vol; ch->rightvol = ch->master_vol; return; } // calculate stereo seperation and distance attenuation VectorSubtract(ch->origin, listener_origin, source_vec); dist = VectorNormalize(source_vec) * ch->dist_mult; dot = DotProduct(listener_right, source_vec); if (shm->channels == 1) { rscale = 1.0; lscale = 1.0; } else { rscale = 1.0 + dot; lscale = 1.0 - dot; } // add in distance effect scale = (1.0 - dist) * rscale; ch->rightvol = (int) (ch->master_vol * scale); if (ch->rightvol < 0) ch->rightvol = 0; scale = (1.0 - dist) * lscale; ch->leftvol = (int) (ch->master_vol * scale); if (ch->leftvol < 0) ch->leftvol = 0; } // ======================================================================= // Start a sound effect // ======================================================================= void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) { channel_t *target_chan, *check; sfxcache_t *sc; int ch_idx; int skip; if (!sound_started) return; if (!sfx) return; if (nosound.value) return; // pick a channel to play on target_chan = SND_PickChannel(entnum, entchannel); if (!target_chan) return; // spatialize memset (target_chan, 0, sizeof(*target_chan)); VectorCopy(origin, target_chan->origin); target_chan->dist_mult = attenuation / sound_nominal_clip_dist; target_chan->master_vol = (int) (fvol * 255); target_chan->entnum = entnum; target_chan->entchannel = entchannel; SND_Spatialize(target_chan); if (!target_chan->leftvol && !target_chan->rightvol) return; // not audible at all // new channel sc = S_LoadSound (sfx); if (!sc) { target_chan->sfx = NULL; return; // couldn't load the sound's data } target_chan->sfx = sfx; target_chan->pos = 0.0; target_chan->end = paintedtime + sc->length; // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder check = &snd_channels[NUM_AMBIENTS]; for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; ch_idx++, check++) { if (check == target_chan) continue; if (check->sfx == sfx && !check->pos) { /* skip = rand () % (int)(0.1 * shm->speed); if (skip >= target_chan->end) skip = target_chan->end - 1; */ /* LordHavoc: fixed skip calculations */ skip = 0.1 * shm->speed; /* 0.1 * sc->speed */ if (skip > sc->length) skip = sc->length; if (skip > 0) skip = rand() % skip; target_chan->pos += skip; target_chan->end -= skip; break; } } } void S_StopSound (int entnum, int entchannel) { int i; for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) { if (snd_channels[i].entnum == entnum && snd_channels[i].entchannel == entchannel) { snd_channels[i].end = 0; snd_channels[i].sfx = NULL; return; } } } void S_StopAllSounds (qboolean clear) { int i; if (!sound_started) return; total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics for (i = 0; i < MAX_CHANNELS; i++) { if (snd_channels[i].sfx) snd_channels[i].sfx = NULL; } memset(snd_channels, 0, MAX_CHANNELS * sizeof(channel_t)); if (clear) S_ClearBuffer (); } static void S_StopAllSoundsC (void) { S_StopAllSounds (true); } void S_ClearBuffer (void) { int clear; if (!sound_started || !shm) return; SNDDMA_LockBuffer (); if (! shm->buffer) return; s_rawend = 0; if (shm->samplebits == 8 && !shm->signed8) clear = 0x80; else clear = 0; memset(shm->buffer, clear, shm->samples * shm->samplebits / 8); SNDDMA_Submit (); } /* ================= S_StaticSound ================= */ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) { channel_t *ss; sfxcache_t *sc; if (!sfx) return; if (total_channels == MAX_CHANNELS) { Con_Printf ("total_channels == MAX_CHANNELS\n"); return; } ss = &snd_channels[total_channels]; total_channels++; sc = S_LoadSound (sfx); if (!sc) return; if (sc->loopstart == -1) { Con_Printf ("Sound %s not looped\n", sfx->name); return; } ss->sfx = sfx; VectorCopy (origin, ss->origin); ss->master_vol = (int)vol; ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; ss->end = paintedtime + sc->length; SND_Spatialize (ss); } //============================================================================= /* =================== S_UpdateAmbientSounds =================== */ static void S_UpdateAmbientSounds (void) { mleaf_t *l; int vol, ambient_channel; channel_t *chan; // no ambients when disconnected if (cls.state != ca_connected) return; // calc ambient sound levels if (!cl.worldmodel) return; l = Mod_PointInLeaf (listener_origin, cl.worldmodel); if (!l || !ambient_level.value) { for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) snd_channels[ambient_channel].sfx = NULL; return; } for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { chan = &snd_channels[ambient_channel]; chan->sfx = ambient_sfx[ambient_channel]; vol = (int) (ambient_level.value * l->ambient_sound_level[ambient_channel]); if (vol < 8) vol = 0; // don't adjust volume too fast if (chan->master_vol < vol) { chan->master_vol += (int) (host_frametime * ambient_fade.value); if (chan->master_vol > vol) chan->master_vol = vol; } else if (chan->master_vol > vol) { chan->master_vol -= (int) (host_frametime * ambient_fade.value); if (chan->master_vol < vol) chan->master_vol = vol; } chan->leftvol = chan->rightvol = chan->master_vol; } } /* =================== S_RawSamples (from QuakeII) Streaming music support. Byte swapping of data must be handled by the codec. Expects data in signed 16 bit, or unsigned 8 bit format. =================== */ void S_RawSamples (int samples, int rate, int width, int channels, byte *data, float volume) { int i; int src, dst; float scale; int intVolume; if (s_rawend < paintedtime) s_rawend = paintedtime; scale = (float) rate / shm->speed; intVolume = (int) (256 * volume); if (channels == 2 && width == 2) { for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples [dst].left = ((short *) data)[src * 2] * intVolume; s_rawsamples [dst].right = ((short *) data)[src * 2 + 1] * intVolume; } } else if (channels == 1 && width == 2) { for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples [dst].left = ((short *) data)[src] * intVolume; s_rawsamples [dst].right = ((short *) data)[src] * intVolume; } } else if (channels == 2 && width == 1) { intVolume *= 256; for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; // s_rawsamples [dst].left = ((signed char *) data)[src * 2] * intVolume; // s_rawsamples [dst].right = ((signed char *) data)[src * 2 + 1] * intVolume; s_rawsamples [dst].left = (((byte *) data)[src * 2] - 128) * intVolume; s_rawsamples [dst].right = (((byte *) data)[src * 2 + 1] - 128) * intVolume; } } else if (channels == 1 && width == 1) { intVolume *= 256; for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; // s_rawsamples [dst].left = ((signed char *) data)[src] * intVolume; // s_rawsamples [dst].right = ((signed char *) data)[src] * intVolume; s_rawsamples [dst].left = (((byte *) data)[src] - 128) * intVolume; s_rawsamples [dst].right = (((byte *) data)[src] - 128) * intVolume; } } } /* ============ S_Update Called once each time through the main loop ============ */ void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { int i, j; int total; channel_t *ch; channel_t *combine; if (!sound_started || (snd_blocked > 0)) return; VectorCopy(origin, listener_origin); VectorCopy(forward, listener_forward); VectorCopy(right, listener_right); VectorCopy(up, listener_up); // update general area ambient sound sources S_UpdateAmbientSounds (); combine = NULL; // update spatialization for static and dynamic sounds ch = snd_channels + NUM_AMBIENTS; for (i = NUM_AMBIENTS; i < total_channels; i++, ch++) { if (!ch->sfx) continue; SND_Spatialize(ch); // respatialize channel if (!ch->leftvol && !ch->rightvol) continue; // try to combine static sounds with a previous channel of the same // sound effect so we don't mix five torches every frame if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) { // see if it can just use the last one if (combine && combine->sfx == ch->sfx) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; continue; } // search for one combine = snd_channels + MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; j < i; j++, combine++) { if (combine->sfx == ch->sfx) break; } if (j == total_channels) { combine = NULL; } else { if (combine != ch) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; } continue; } } } // // debugging output // if (snd_show.value) { total = 0; ch = snd_channels; for (i = 0; i < total_channels; i++, ch++) { if (ch->sfx && (ch->leftvol || ch->rightvol) ) { // Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); total++; } } Con_Printf ("----(%i)----\n", total); } // add raw data from streamed samples // BGM_Update(); // moved to the main loop just before S_Update () // mix some sound S_Update_(); } static void GetSoundtime (void) { int samplepos; static int buffers; static int oldsamplepos; int fullsamples; fullsamples = shm->samples / shm->channels; // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. samplepos = SNDDMA_GetDMAPos(); if (samplepos < oldsamplepos) { buffers++; // buffer wrapped if (paintedtime > 0x40000000) { // time to chop things off to avoid 32 bit limits buffers = 0; paintedtime = fullsamples; S_StopAllSounds (true); } } oldsamplepos = samplepos; soundtime = buffers*fullsamples + samplepos/shm->channels; } void S_ExtraUpdate (void) { if (snd_noextraupdate.value) return; // don't pollute timings S_Update_(); } static void S_Update_ (void) { unsigned int endtime; int samps; if (!sound_started || (snd_blocked > 0)) return; SNDDMA_LockBuffer (); if (! shm->buffer) return; // Updates DMA time GetSoundtime(); // check to make sure that we haven't overshot if (paintedtime < soundtime) { // Con_Printf ("S_Update_ : overflow\n"); paintedtime = soundtime; } // mix ahead of current position endtime = soundtime + (unsigned int)(_snd_mixahead.value * shm->speed); samps = shm->samples >> (shm->channels - 1); endtime = q_min(endtime, (unsigned int)(soundtime + samps)); S_PaintChannels (endtime); SNDDMA_Submit (); } void S_BlockSound (void) { /* FIXME: do we really need the blocking at the * driver level? */ if (sound_started && snd_blocked == 0) /* ++snd_blocked == 1 */ { snd_blocked = 1; S_ClearBuffer (); if (shm) SNDDMA_BlockSound(); } } void S_UnblockSound (void) { if (!sound_started || !snd_blocked) return; if (snd_blocked == 1) /* --snd_blocked == 0 */ { snd_blocked = 0; SNDDMA_UnblockSound(); S_ClearBuffer (); } } /* =============================================================================== console functions =============================================================================== */ static void S_Play (void) { static int hash = 345; int i; char name[256]; sfx_t *sfx; i = 1; while (i < Cmd_Argc()) { q_strlcpy(name, Cmd_Argv(i), sizeof(name)); if (!Q_strrchr(Cmd_Argv(i), '.')) { q_strlcat(name, ".wav", sizeof(name)); } sfx = S_PrecacheSound(name); S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0); i++; } } static void S_PlayVol (void) { static int hash = 543; int i; float vol; char name[256]; sfx_t *sfx; i = 1; while (i < Cmd_Argc()) { q_strlcpy(name, Cmd_Argv(i), sizeof(name)); if (!Q_strrchr(Cmd_Argv(i), '.')) { q_strlcat(name, ".wav", sizeof(name)); } sfx = S_PrecacheSound(name); vol = Q_atof(Cmd_Argv(i + 1)); S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0); i += 2; } } static void S_SoundList (void) { int i; sfx_t *sfx; sfxcache_t *sc; int size, total; total = 0; for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++) { sc = (sfxcache_t *) Cache_Check (&sfx->cache); if (!sc) continue; size = sc->length*sc->width*(sc->stereo + 1); total += size; if (sc->loopstart >= 0) Con_SafePrintf ("L"); //johnfitz -- was Con_Printf else Con_SafePrintf (" "); //johnfitz -- was Con_Printf Con_SafePrintf("(%2db) %6i : %s\n", sc->width*8, size, sfx->name); //johnfitz -- was Con_Printf } Con_Printf ("%i sounds, %i bytes\n", num_sfx, total); //johnfitz -- added count } void S_LocalSound (const char *name) { sfx_t *sfx; if (nosound.value) return; if (!sound_started) return; sfx = S_PrecacheSound (name); if (!sfx) { Con_Printf ("S_LocalSound: can't cache %s\n", name); return; } S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); } void S_ClearPrecache (void) { } void S_BeginPrecaching (void) { } void S_EndPrecaching (void) { } �������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_sys.h�������������������������������������������������������������������0000644�0000000�0000000�00000013352�12757031150�015363� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * net_sys.h -- common network system header. * - depends on arch_def.h * - may depend on q_stdinc.h * * Copyright (C) 2007-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_SYS_H__ #define __NET_SYS_H__ #include <sys/types.h> #include <errno.h> #include <stddef.h> #include <limits.h> #if defined(PLATFORM_BSD) || defined(PLATFORM_OSX) || \ defined(PLATFORM_AMIGA) /* bsdsocket.library */ || \ defined(__GNU__) /* GNU/Hurd */ || defined(__riscos__) /* struct sockaddr has unsigned char sa_len as the first member in BSD * variants and the family member is also an unsigned char instead of an * unsigned short. This should matter only when PLATFORM_UNIX is defined, * however, checking for the offset of sa_family in every platform that * provide a struct sockaddr doesn't hurt either (see down below for the * compile time asserts.) */ /* FIXME : GET RID OF THIS ABOMINATION !!! */ #define HAVE_SA_LEN 1 #define SA_FAM_OFFSET 1 #else #undef HAVE_SA_LEN #define SA_FAM_OFFSET 0 #endif /* BSD, sockaddr */ /* unix includes and compatibility macros */ #if defined(PLATFORM_UNIX) || defined(PLATFORM_RISCOS) #include <sys/param.h> #include <sys/ioctl.h> #if defined(__sun) || defined(sun) #include <sys/filio.h> #include <sys/sockio.h> #endif /* __sunos__ */ #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> typedef int sys_socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #if defined(__APPLE__) && defined(SO_NKE) && !defined(SO_NOADDRERR) /* ancient Mac OS X SDKs 10.2 and older are missing socklen_t */ typedef int socklen_t; /* defining as signed int to match the old api */ #endif /* ancient OSX SDKs */ #define SOCKETERRNO errno #define ioctlsocket ioctl #define closesocket close #define selectsocket select #define IOCTLARG_P(x) /* (char *) */ x #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED #define socketerror(x) strerror((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of unix stuff */ /* amiga includes and compatibility macros */ #if defined(PLATFORM_AMIGA) /* Amiga bsdsocket.library */ #include <sys/param.h> #include <sys/ioctl.h> #include <unistd.h> #include <proto/exec.h> #include <proto/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> typedef int sys_socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #if !(defined(__AROS__) || defined(__amigaos4__)) typedef LONG socklen_t; /* int32_t */ #endif #if !defined(__amigaos4__) #if (LONG_MAX <= 2147483647L) typedef unsigned long in_addr_t; /* u_int32_t */ #else typedef unsigned int in_addr_t; /* u_int32_t */ #endif #endif #define SOCKETERRNO Errno() #define ioctlsocket IoctlSocket #define closesocket CloseSocket #define selectsocket(_N,_R,_W,_E,_T) \ WaitSelect((_N),(_R),(_W),(_E),(_T),NULL) #define IOCTLARG_P(x) (char *) x #if defined(__amigaos4__) || defined(PLATFORM_AMIGAOS3) #define inet_ntoa(x) Inet_NtoA(x.s_addr) /* Inet_NtoA(*(ULONG*)&x) */ #define h_errno Errno() #endif #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED #define socketerror(x) strerror((x)) /* there is h_errno but no hstrerror() */ #define hstrerror(x) strerror((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of amiga bsdsocket.library stuff */ /* windows includes and compatibility macros */ #if defined(PLATFORM_WINDOWS) /* NOTE: winsock[2].h already includes windows.h */ #if !defined(_USE_WINSOCK2) #include <winsock.h> #else #include <winsock2.h> #include <ws2tcpip.h> #endif /* there is no in_addr_t on windows: define it as the type of the S_addr of in_addr structure */ typedef u_long in_addr_t; /* uint32_t */ /* on windows, socklen_t is to be a winsock2 thing */ #if !defined(IP_MSFILTER_SIZE) typedef int socklen_t; #endif /* socklen_t type */ typedef SOCKET sys_socket_t; #define selectsocket select #define IOCTLARG_P(x) /* (u_long *) */ x #define SOCKETERRNO WSAGetLastError() #define NET_EWOULDBLOCK WSAEWOULDBLOCK #define NET_ECONNREFUSED WSAECONNREFUSED /* must #include "wsaerror.h" for this : */ #define socketerror(x) __WSAE_StrError((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of windows stuff */ /* macros which may still be missing */ #if !defined(INADDR_NONE) #define INADDR_NONE ((in_addr_t) 0xffffffff) #endif /* INADDR_NONE */ #if !defined(INADDR_LOOPBACK) #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) /* 127.0.0.1 */ #endif /* INADDR_LOOPBACK */ #if !defined(MAXHOSTNAMELEN) /* SUSv2 guarantees that `Host names are limited to 255 bytes'. POSIX 1003.1-2001 guarantees that `Host names (not including the terminating NUL) are limited to HOST_NAME_MAX bytes'. */ #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAMELEN */ #endif /* __NET_SYS_H__ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/Makefile��������������������������������������������������������������������0000644�0000000�0000000�00000014040�13142575645�015174� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# GNU Makefile for QuakeSpasm unix targets. # You need the SDL library fully installed. # "make DEBUG=1" to build a debug client. # "make SDL_CONFIG=/path/to/sdl-config" for unusual SDL installations. # "make DO_USERDIRS=1" to enable user directories support # Enable/Disable user directories support DO_USERDIRS=0 ### Enable/Disable SDL2 USE_SDL2=0 ### Enable/Disable codecs for streaming music support USE_CODEC_WAVE=1 USE_CODEC_FLAC=0 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 USE_CODEC_OPUS=0 # either mikmod or xmp USE_CODEC_MIKMOD=0 USE_CODEC_XMP=0 USE_CODEC_UMX=0 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # --------------------------- # Helper functions # --------------------------- check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) # --------------------------- HOST_OS := $(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]') DEBUG ?= 0 # --------------------------- # build variables # --------------------------- CC ?= gcc LINKER = $(CC) STRIP ?= strip #CPUFLAGS= -mtune=i686 #CPUFLAGS= -march=pentium4 #CPUFLAGS= -mtune=k8 #CPUFLAGS= -march=atom CPUFLAGS= LDFLAGS = DFLAGS ?= CFLAGS ?= -Wall -Wno-trigraphs CFLAGS += $(CPUFLAGS) ifneq ($(DEBUG),0) DFLAGS += -DDEBUG CFLAGS += -g do_strip= else DFLAGS += -DNDEBUG CFLAGS += -O2 CFLAGS += $(call check_gcc,-fweb,) CFLAGS += $(call check_gcc,-frename-registers,) cmd_strip=$(STRIP) $(1) define do_strip $(call cmd_strip,$(1)); endef endif ifeq ($(DO_USERDIRS),1) CFLAGS += -DDO_USERDIRS=1 endif ### X11BASE only gets used if its in an unusual place X11DIRS := /usr/X11R7 /usr/local/X11R7 /usr/X11R6 /usr/local/X11R6 X11BASE_GUESS := $(shell \ if [ -e /usr/include/X11/Xlib.h ] && \ [ -e /usr/lib/libX11.a ]; then exit 0; fi; \ if [ -e /usr/local/include/X11/Xlib.h ] && \ [ -e /usr/local/lib/libX11.a ]; then exit 0; fi; \ for DIR in $(X11DIRS); do \ if [ -e $$DIR/include/X11/Xlib.h ] && \ [ -e $$DIR/lib/libX11.a ]; then echo $$DIR; break; fi; \ done ) X11BASE ?= $(X11BASE_GUESS) ifneq ($(X11BASE),) LDFLAGS+= -L$(X11BASE)/lib CFLAGS += -I$(X11BASE)/include endif ifeq ($(USE_SDL2),1) CFLAGS += -DUSE_SDL2 endif ifeq ($(USE_SDL2),1) SDL_CONFIG ?= sdl2-config else SDL_CONFIG ?= sdl-config endif SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags) SDL_LIBS := $(shell $(SDL_CONFIG) --libs) ifeq ($(HOST_OS),sunos) NET_LIBS :=-lsocket -lnsl -lresolv else NET_LIBS := endif ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3.o lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123.o lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif CODECLIBS := ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_FLAC),1) CFLAGS+= -DUSE_CODEC_FLAC CODECLIBS+= -lFLAC endif ifeq ($(USE_CODEC_OPUS),1) # opus and opusfile put their *.h under <includedir>/opus, # but they include the headers without the opus directory # prefix and rely on pkg-config. ewww... CFLAGS+= -DUSE_CODEC_OPUS CFLAGS+= $(shell pkg-config --cflags opusfile) CODECLIBS+= $(shell pkg-config --libs opusfile) endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODECLIBS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),1) CFLAGS+= -DUSE_CODEC_MP3 CODECLIBS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),1) CFLAGS+= -DUSE_CODEC_MIKMOD CODECLIBS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),1) CFLAGS+= -DUSE_CODEC_XMP CODECLIBS+= -lxmp endif ifeq ($(USE_CODEC_UMX),1) CFLAGS+= -DUSE_CODEC_UMX endif COMMON_LIBS:= -lm -lGL LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODECLIBS) # --------------------------- # targets # --------------------------- .PHONY: clean debug release DEFAULT_TARGET := quakespasm # --------------------------- # rules # --------------------------- %.o: %.c $(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $< # ---------------------------------------------------------------------------- # objects # ---------------------------------------------------------------------------- MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj) \ snd_mikmod.o \ snd_xmp.o \ snd_umx.o COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o SYSOBJ_CDA := cd_sdl.o SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := pl_linux.o sys_sdl_unix.o SYSOBJ_MAIN:= main_sdl.o SYSOBJ_RES := GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_fog.o \ gl_rmisc.o \ r_part.o \ r_world.o \ gl_screen.o \ gl_sky.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ image.o \ gl_texmgr.o \ gl_mesh.o \ r_sprite.o \ r_alias.o \ r_brush.o \ gl_model.o OBJS := strlcat.o \ strlcpy.o \ $(GLOBJS) \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_tent.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ common.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ world.o \ zone.o \ $(SYSOBJ_SYS) $(SYSOBJ_MAIN) $(SYSOBJ_RES) # ------------------------ # Linux build rules # ------------------------ quakespasm: $(OBJS) $(LINKER) $(OBJS) $(LDFLAGS) $(LIBS) $(SDL_LIBS) -o $@ $(call do_strip,$@) image.o: lodepng.c lodepng.h stb_image_write.h release: quakespasm debug: $(error Use "make DEBUG=1") clean: rm -f $(shell find . \( -name '*~' -o -name '#*#' -o -name '*.o' -o -name '*.res' -o -name $(DEFAULT_TARGET) \) -print) install: quakespasm cp quakespasm /usr/local/games/quake ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pr_comp.h�������������������������������������������������������������������0000644�0000000�0000000�00000006160�12407762022�015336� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __PR_COMP_H #define __PR_COMP_H // this file is shared by quake and qcc typedef int func_t; typedef int string_t; typedef enum { ev_bad = -1, ev_void = 0, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer } etype_t; #define OFS_NULL 0 #define OFS_RETURN 1 #define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors #define OFS_PARM1 7 #define OFS_PARM2 10 #define OFS_PARM3 13 #define OFS_PARM4 16 #define OFS_PARM5 19 #define OFS_PARM6 22 #define OFS_PARM7 25 #define RESERVED_OFS 28 enum { OP_DONE, OP_MUL_F, OP_MUL_V, OP_MUL_FV, OP_MUL_VF, OP_DIV_F, OP_ADD_F, OP_ADD_V, OP_SUB_F, OP_SUB_V, OP_EQ_F, OP_EQ_V, OP_EQ_S, OP_EQ_E, OP_EQ_FNC, OP_NE_F, OP_NE_V, OP_NE_S, OP_NE_E, OP_NE_FNC, OP_LE, OP_GE, OP_LT, OP_GT, OP_LOAD_F, OP_LOAD_V, OP_LOAD_S, OP_LOAD_ENT, OP_LOAD_FLD, OP_LOAD_FNC, OP_ADDRESS, OP_STORE_F, OP_STORE_V, OP_STORE_S, OP_STORE_ENT, OP_STORE_FLD, OP_STORE_FNC, OP_STOREP_F, OP_STOREP_V, OP_STOREP_S, OP_STOREP_ENT, OP_STOREP_FLD, OP_STOREP_FNC, OP_RETURN, OP_NOT_F, OP_NOT_V, OP_NOT_S, OP_NOT_ENT, OP_NOT_FNC, OP_IF, OP_IFNOT, OP_CALL0, OP_CALL1, OP_CALL2, OP_CALL3, OP_CALL4, OP_CALL5, OP_CALL6, OP_CALL7, OP_CALL8, OP_STATE, OP_GOTO, OP_AND, OP_OR, OP_BITAND, OP_BITOR }; typedef struct statement_s { unsigned short op; short a, b, c; } dstatement_t; typedef struct { unsigned short type; // if DEF_SAVEGLOBAL bit is set // the variable needs to be saved in savegames unsigned short ofs; int s_name; } ddef_t; #define DEF_SAVEGLOBAL (1<<15) #define MAX_PARMS 8 typedef struct { int first_statement; // negative numbers are builtins int parm_start; int locals; // total ints of parms + locals int profile; // runtime int s_name; int s_file; // source file defined in int numparms; byte parm_size[MAX_PARMS]; } dfunction_t; #define PROG_VERSION 6 typedef struct { int version; int crc; // check of header file int ofs_statements; int numstatements; // statement 0 is an error int ofs_globaldefs; int numglobaldefs; int ofs_fielddefs; int numfielddefs; int ofs_functions; int numfunctions; // function 0 is an empty int ofs_strings; int numstrings; // first string is a null string int ofs_globals; int numglobals; int entityfields; } dprograms_t; #endif /* __PR_COMP_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/view.c����������������������������������������������������������������������0000644�0000000�0000000�00000055133�13164764750�014663� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // view.c -- player eye positioning #include "quakedef.h" /* The view is allowed to move slightly from it's true position for bobbing, but if it exceeds 8 pixels linear distance (spherical, not box), the list of entities sent from the server may not include everything in the pvs, especially when crossing a water boudnary. */ cvar_t scr_ofsx = {"scr_ofsx","0", CVAR_NONE}; cvar_t scr_ofsy = {"scr_ofsy","0", CVAR_NONE}; cvar_t scr_ofsz = {"scr_ofsz","0", CVAR_NONE}; cvar_t cl_rollspeed = {"cl_rollspeed", "200", CVAR_NONE}; cvar_t cl_rollangle = {"cl_rollangle", "2.0", CVAR_NONE}; cvar_t cl_bob = {"cl_bob","0.02", CVAR_NONE}; cvar_t cl_bobcycle = {"cl_bobcycle","0.6", CVAR_NONE}; cvar_t cl_bobup = {"cl_bobup","0.5", CVAR_NONE}; cvar_t v_kicktime = {"v_kicktime", "0.5", CVAR_NONE}; cvar_t v_kickroll = {"v_kickroll", "0.6", CVAR_NONE}; cvar_t v_kickpitch = {"v_kickpitch", "0.6", CVAR_NONE}; cvar_t v_gunkick = {"v_gunkick", "1", CVAR_NONE}; //johnfitz cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", CVAR_NONE}; cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", CVAR_NONE}; cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", CVAR_NONE}; cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", CVAR_NONE}; cvar_t v_iroll_level = {"v_iroll_level", "0.1", CVAR_NONE}; cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", CVAR_NONE}; cvar_t v_idlescale = {"v_idlescale", "0", CVAR_NONE}; cvar_t crosshair = {"crosshair", "0", CVAR_ARCHIVE}; cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100", CVAR_NONE}; cvar_t gl_cshiftpercent_contents = {"gl_cshiftpercent_contents", "100", CVAR_NONE}; // QuakeSpasm cvar_t gl_cshiftpercent_damage = {"gl_cshiftpercent_damage", "100", CVAR_NONE}; // QuakeSpasm cvar_t gl_cshiftpercent_bonus = {"gl_cshiftpercent_bonus", "100", CVAR_NONE}; // QuakeSpasm cvar_t gl_cshiftpercent_powerup = {"gl_cshiftpercent_powerup", "100", CVAR_NONE}; // QuakeSpasm cvar_t r_viewmodel_quake = {"r_viewmodel_quake", "0", CVAR_ARCHIVE}; float v_dmg_time, v_dmg_roll, v_dmg_pitch; extern int in_forward, in_forward2, in_back; vec3_t v_punchangles[2]; //johnfitz -- copied from cl.punchangle. 0 is current, 1 is previous value. never the same unless map just loaded /* =============== V_CalcRoll Used by view and sv_user =============== */ float V_CalcRoll (vec3_t angles, vec3_t velocity) { vec3_t forward, right, up; float sign; float side; float value; AngleVectors (angles, forward, right, up); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; // if (cl.inwater) // value *= 6; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } /* =============== V_CalcBob =============== */ float V_CalcBob (void) { float bob; float cycle; cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value; cycle /= cl_bobcycle.value; if (cycle < cl_bobup.value) cycle = M_PI * cycle / cl_bobup.value; else cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); // bob is proportional to velocity in the xy plane // (don't count Z, or jumping messes it up) bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value; //Con_Printf ("speed: %5.1f\n", VectorLength(cl.velocity)); bob = bob*0.3 + bob*0.7*sin(cycle); if (bob > 4) bob = 4; else if (bob < -7) bob = -7; return bob; } //============================================================================= cvar_t v_centermove = {"v_centermove", "0.15", CVAR_NONE}; cvar_t v_centerspeed = {"v_centerspeed","500", CVAR_NONE}; void V_StartPitchDrift (void) { #if 1 if (cl.laststop == cl.time) { return; // something else is keeping it from drifting } #endif if (cl.nodrift || !cl.pitchvel) { cl.pitchvel = v_centerspeed.value; cl.nodrift = false; cl.driftmove = 0; } } void V_StopPitchDrift (void) { cl.laststop = cl.time; cl.nodrift = true; cl.pitchvel = 0; } /* =============== V_DriftPitch Moves the client pitch angle towards cl.idealpitch sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. Drifting is enabled when the center view key is hit, mlook is released and lookspring is non 0, or when =============== */ void V_DriftPitch (void) { float delta, move; if (noclip_anglehack || !cl.onground || cls.demoplayback ) //FIXME: noclip_anglehack is set on the server, so in a nonlocal game this won't work. { cl.driftmove = 0; cl.pitchvel = 0; return; } // don't count small mouse motion if (cl.nodrift) { if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value) cl.driftmove = 0; else cl.driftmove += host_frametime; if ( cl.driftmove > v_centermove.value) { if (lookspring.value) V_StartPitchDrift (); } return; } delta = cl.idealpitch - cl.viewangles[PITCH]; if (!delta) { cl.pitchvel = 0; return; } move = host_frametime * cl.pitchvel; cl.pitchvel += host_frametime * v_centerspeed.value; //Con_Printf ("move: %f (%f)\n", move, host_frametime); if (delta > 0) { if (move > delta) { cl.pitchvel = 0; move = delta; } cl.viewangles[PITCH] += move; } else if (delta < 0) { if (move > -delta) { cl.pitchvel = 0; move = -delta; } cl.viewangles[PITCH] -= move; } } /* ============================================================================== VIEW BLENDING ============================================================================== */ cshift_t cshift_empty = { {130,80,50}, 0 }; cshift_t cshift_water = { {130,80,50}, 128 }; cshift_t cshift_slime = { {0,25,5}, 150 }; cshift_t cshift_lava = { {255,80,0}, 150 }; float v_blend[4]; // rgba 0.0 - 1.0 //johnfitz -- deleted BuildGammaTable(), V_CheckGamma(), gammatable[], and ramps[][] /* =============== V_ParseDamage =============== */ void V_ParseDamage (void) { int armor, blood; vec3_t from; int i; vec3_t forward, right, up; entity_t *ent; float side; float count; armor = MSG_ReadByte (); blood = MSG_ReadByte (); for (i=0 ; i<3 ; i++) from[i] = MSG_ReadCoord (cl.protocolflags); count = blood*0.5 + armor*0.5; if (count < 10) count = 10; cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) cl.cshifts[CSHIFT_DAMAGE].percent = 150; if (armor > blood) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; } else if (armor) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; } else { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; } // // calculate view angle kicks // ent = &cl_entities[cl.viewentity]; VectorSubtract (from, ent->origin, from); VectorNormalize (from); AngleVectors (ent->angles, forward, right, up); side = DotProduct (from, right); v_dmg_roll = count*side*v_kickroll.value; side = DotProduct (from, forward); v_dmg_pitch = count*side*v_kickpitch.value; v_dmg_time = v_kicktime.value; } /* ================== V_cshift_f ================== */ void V_cshift_f (void) { cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); cshift_empty.percent = atoi(Cmd_Argv(4)); } /* ================== V_BonusFlash_f When you run over an item, the server sends this command ================== */ void V_BonusFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; cl.cshifts[CSHIFT_BONUS].percent = 50; } /* ============= V_SetContentsColor Underwater, lava, etc each has a color shift ============= */ void V_SetContentsColor (int contents) { switch (contents) { case CONTENTS_EMPTY: case CONTENTS_SOLID: case CONTENTS_SKY: //johnfitz -- no blend in sky cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; break; case CONTENTS_LAVA: cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; break; case CONTENTS_SLIME: cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; break; default: cl.cshifts[CSHIFT_CONTENTS] = cshift_water; } } /* ============= V_CalcPowerupCshift ============= */ void V_CalcPowerupCshift (void) { if (cl.items & IT_QUAD) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else if (cl.items & IT_SUIT) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 20; } else if (cl.items & IT_INVISIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; cl.cshifts[CSHIFT_POWERUP].percent = 100; } else if (cl.items & IT_INVULNERABILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else cl.cshifts[CSHIFT_POWERUP].percent = 0; } /* ============= V_CalcBlend ============= */ void V_CalcBlend (void) { float r, g, b, a, a2; int j; cvar_t *cshiftpercent_cvars[NUM_CSHIFTS] = { &gl_cshiftpercent_contents, &gl_cshiftpercent_damage, &gl_cshiftpercent_bonus, &gl_cshiftpercent_powerup }; r = 0; g = 0; b = 0; a = 0; for (j=0 ; j<NUM_CSHIFTS ; j++) { if (!gl_cshiftpercent.value) continue; //johnfitz -- only apply leaf contents color shifts during intermission if (cl.intermission && j != CSHIFT_CONTENTS) continue; //johnfitz a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0; // QuakeSpasm -- also scale by the specific gl_cshiftpercent_* cvar a2 *= (cshiftpercent_cvars[j]->value / 100.0); // QuakeSpasm if (!a2) continue; a = a + a2*(1-a); a2 = a2/a; r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2; g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2; b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2; } v_blend[0] = r/255.0; v_blend[1] = g/255.0; v_blend[2] = b/255.0; v_blend[3] = a; if (v_blend[3] > 1) v_blend[3] = 1; if (v_blend[3] < 0) v_blend[3] = 0; } /* ============= V_UpdateBlend -- johnfitz -- V_UpdatePalette cleaned up and renamed ============= */ void V_UpdateBlend (void) { int i, j; qboolean blend_changed; V_CalcPowerupCshift (); blend_changed = false; for (i=0 ; i<NUM_CSHIFTS ; i++) { if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { blend_changed = true; cl.prev_cshifts[i].percent = cl.cshifts[i].percent; } for (j=0 ; j<3 ; j++) if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) { blend_changed = true; cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; } } // drop the damage value cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; // drop the bonus value cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; if (cl.cshifts[CSHIFT_BONUS].percent <= 0) cl.cshifts[CSHIFT_BONUS].percent = 0; if (blend_changed) V_CalcBlend (); } /* ============ V_PolyBlend -- johnfitz -- moved here from gl_rmain.c, and rewritten to use glOrtho ============ */ void V_PolyBlend (void) { if (!gl_polyblend.value || !v_blend[3]) return; GL_DisableMultitexture(); glDisable (GL_ALPHA_TEST); glDisable (GL_TEXTURE_2D); glDisable (GL_DEPTH_TEST); glEnable (GL_BLEND); glMatrixMode(GL_PROJECTION); glLoadIdentity (); glOrtho (0, 1, 1, 0, -99999, 99999); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glColor4fv (v_blend); glBegin (GL_QUADS); glVertex2f (0,0); glVertex2f (1, 0); glVertex2f (1, 1); glVertex2f (0, 1); glEnd (); glDisable (GL_BLEND); glEnable (GL_DEPTH_TEST); glEnable (GL_TEXTURE_2D); glEnable (GL_ALPHA_TEST); } /* ============================================================================== VIEW RENDERING ============================================================================== */ float angledelta (float a) { a = anglemod(a); if (a > 180) a -= 360; return a; } /* ================== CalcGunAngle ================== */ void CalcGunAngle (void) { float yaw, pitch, move; static float oldyaw = 0; static float oldpitch = 0; yaw = r_refdef.viewangles[YAW]; pitch = -r_refdef.viewangles[PITCH]; yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; if (yaw > 10) yaw = 10; if (yaw < -10) yaw = -10; pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; if (pitch > 10) pitch = 10; if (pitch < -10) pitch = -10; move = host_frametime*20; if (yaw > oldyaw) { if (oldyaw + move < yaw) yaw = oldyaw + move; } else { if (oldyaw - move > yaw) yaw = oldyaw - move; } if (pitch > oldpitch) { if (oldpitch + move < pitch) pitch = oldpitch + move; } else { if (oldpitch - move > pitch) pitch = oldpitch - move; } oldyaw = yaw; oldpitch = pitch; cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_BoundOffsets ============== */ void V_BoundOffsets (void) { entity_t *ent; ent = &cl_entities[cl.viewentity]; // absolutely bound refresh reletive to entity clipping hull // so the view can never be inside a solid wall if (r_refdef.vieworg[0] < ent->origin[0] - 14) r_refdef.vieworg[0] = ent->origin[0] - 14; else if (r_refdef.vieworg[0] > ent->origin[0] + 14) r_refdef.vieworg[0] = ent->origin[0] + 14; if (r_refdef.vieworg[1] < ent->origin[1] - 14) r_refdef.vieworg[1] = ent->origin[1] - 14; else if (r_refdef.vieworg[1] > ent->origin[1] + 14) r_refdef.vieworg[1] = ent->origin[1] + 14; if (r_refdef.vieworg[2] < ent->origin[2] - 22) r_refdef.vieworg[2] = ent->origin[2] - 22; else if (r_refdef.vieworg[2] > ent->origin[2] + 30) r_refdef.vieworg[2] = ent->origin[2] + 30; } /* ============== V_AddIdle Idle swaying ============== */ void V_AddIdle (void) { r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll (void) { float side; side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); r_refdef.viewangles[ROLL] += side; if (v_dmg_time > 0) { r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; v_dmg_time -= host_frametime; } if (cl.stats[STAT_HEALTH] <= 0) { r_refdef.viewangles[ROLL] = 80; // dead view angle return; } } /* ================== V_CalcIntermissionRefdef ================== */ void V_CalcIntermissionRefdef (void) { entity_t *ent, *view; float old; // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; VectorCopy (ent->origin, r_refdef.vieworg); VectorCopy (ent->angles, r_refdef.viewangles); view->model = NULL; // allways idle in intermission old = v_idlescale.value; v_idlescale.value = 1; V_AddIdle (); v_idlescale.value = old; } /* ================== V_CalcRefdef ================== */ void V_CalcRefdef (void) { entity_t *ent, *view; int i; vec3_t forward, right, up; vec3_t angles; float bob; static float oldz = 0; static vec3_t punch = {0,0,0}; //johnfitz -- v_gunkick float delta; //johnfitz -- v_gunkick V_DriftPitch (); // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; // transform the view offset by the model's matrix to get the offset from // model origin for the view ent->angles[YAW] = cl.viewangles[YAW]; // the model should face the view dir ent->angles[PITCH] = -cl.viewangles[PITCH]; // the model should face the view dir bob = V_CalcBob (); // refresh position VectorCopy (ent->origin, r_refdef.vieworg); r_refdef.vieworg[2] += cl.viewheight + bob; // never let it sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis r_refdef.vieworg[0] += 1.0/32; r_refdef.vieworg[1] += 1.0/32; r_refdef.vieworg[2] += 1.0/32; VectorCopy (cl.viewangles, r_refdef.viewangles); V_CalcViewRoll (); V_AddIdle (); // offsets angles[PITCH] = -ent->angles[PITCH]; // because entity pitches are actually backward angles[YAW] = ent->angles[YAW]; angles[ROLL] = ent->angles[ROLL]; AngleVectors (angles, forward, right, up); if (cl.maxclients <= 1) //johnfitz -- moved cheat-protection here from V_RenderView for (i=0 ; i<3 ; i++) r_refdef.vieworg[i] += scr_ofsx.value*forward[i] + scr_ofsy.value*right[i] + scr_ofsz.value*up[i]; V_BoundOffsets (); // set up gun position VectorCopy (cl.viewangles, view->angles); CalcGunAngle (); VectorCopy (ent->origin, view->origin); view->origin[2] += cl.viewheight; for (i=0 ; i<3 ; i++) view->origin[i] += forward[i]*bob*0.4; view->origin[2] += bob; //johnfitz -- removed all gun position fudging code (was used to keep gun from getting covered by sbar) //MarkV -- restored this with r_viewmodel_quake cvar if (r_viewmodel_quake.value) { if (scr_viewsize.value == 110) view->origin[2] += 1; else if (scr_viewsize.value == 100) view->origin[2] += 2; else if (scr_viewsize.value == 90) view->origin[2] += 1; else if (scr_viewsize.value == 80) view->origin[2] += 0.5; } view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; view->frame = cl.stats[STAT_WEAPONFRAME]; view->colormap = vid.colormap; //johnfitz -- v_gunkick if (v_gunkick.value == 1) //original quake kick VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles); if (v_gunkick.value == 2) //lerped kick { for (i=0; i<3; i++) if (punch[i] != v_punchangles[0][i]) { //speed determined by how far we need to lerp in 1/10th of a second delta = (v_punchangles[0][i]-v_punchangles[1][i]) * host_frametime * 10; if (delta > 0) punch[i] = q_min(punch[i]+delta, v_punchangles[0][i]); else if (delta < 0) punch[i] = q_max(punch[i]+delta, v_punchangles[0][i]); } VectorAdd (r_refdef.viewangles, punch, r_refdef.viewangles); } //johnfitz // smooth out stair step ups if (!noclip_anglehack && cl.onground && ent->origin[2] - oldz > 0) //johnfitz -- added exception for noclip //FIXME: noclip_anglehack is set on the server, so in a nonlocal game this won't work. { float steptime; steptime = cl.time - cl.oldtime; if (steptime < 0) //FIXME I_Error ("steptime < 0"); steptime = 0; oldz += steptime * 80; if (oldz > ent->origin[2]) oldz = ent->origin[2]; if (ent->origin[2] - oldz > 12) oldz = ent->origin[2] - 12; r_refdef.vieworg[2] += oldz - ent->origin[2]; view->origin[2] += oldz - ent->origin[2]; } else oldz = ent->origin[2]; if (chase_active.value) Chase_UpdateForDrawing (); //johnfitz } /* ================== V_RenderView The player's clipping box goes from (-16 -16 -24) to (16 16 32) from the entity origin, so any view position inside that will be valid ================== */ extern vrect_t scr_vrect; void V_RenderView (void) { if (con_forcedup) return; if (cl.intermission) V_CalcIntermissionRefdef (); else if (!cl.paused /* && (cl.maxclients > 1 || key_dest == key_game) */) V_CalcRefdef (); //johnfitz -- removed lcd code R_RenderView (); V_PolyBlend (); //johnfitz -- moved here from R_Renderview (); } /* ============================================================================== INIT ============================================================================== */ /* ============= V_Init ============= */ void V_Init (void) { Cmd_AddCommand ("v_cshift", V_cshift_f); Cmd_AddCommand ("bf", V_BonusFlash_f); Cmd_AddCommand ("centerview", V_StartPitchDrift); Cvar_RegisterVariable (&v_centermove); Cvar_RegisterVariable (&v_centerspeed); Cvar_RegisterVariable (&v_iyaw_cycle); Cvar_RegisterVariable (&v_iroll_cycle); Cvar_RegisterVariable (&v_ipitch_cycle); Cvar_RegisterVariable (&v_iyaw_level); Cvar_RegisterVariable (&v_iroll_level); Cvar_RegisterVariable (&v_ipitch_level); Cvar_RegisterVariable (&v_idlescale); Cvar_RegisterVariable (&crosshair); Cvar_RegisterVariable (&gl_cshiftpercent); Cvar_RegisterVariable (&gl_cshiftpercent_contents); // QuakeSpasm Cvar_RegisterVariable (&gl_cshiftpercent_damage); // QuakeSpasm Cvar_RegisterVariable (&gl_cshiftpercent_bonus); // QuakeSpasm Cvar_RegisterVariable (&gl_cshiftpercent_powerup); // QuakeSpasm Cvar_RegisterVariable (&scr_ofsx); Cvar_RegisterVariable (&scr_ofsy); Cvar_RegisterVariable (&scr_ofsz); Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); Cvar_RegisterVariable (&cl_bob); Cvar_RegisterVariable (&cl_bobcycle); Cvar_RegisterVariable (&cl_bobup); Cvar_RegisterVariable (&v_kicktime); Cvar_RegisterVariable (&v_kickroll); Cvar_RegisterVariable (&v_kickpitch); Cvar_RegisterVariable (&v_gunkick); //johnfitz Cvar_RegisterVariable (&r_viewmodel_quake); //MarkV } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_wave.h������������������������������������������������������������������0000644�0000000�0000000�00000000313�11511145623�015474� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* WAV streaming music support. */ #if !defined(_SND_WAVE_H_) #define _SND_WAVE_H_ #if defined(USE_CODEC_WAVE) extern snd_codec_t wav_codec; #endif /* USE_CODEC_WAVE */ #endif /* ! _SND_WAVE_H_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/build_cross_win64.sh��������������������������������������������������������0000755�0000000�0000000�00000000670�12475156650�017435� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Change this script to meet your needs and/or environment. TARGET=x86_64-w64-mingw32 PREFIX=/opt/cross_win64 PATH="$PREFIX/bin:$PATH" export PATH MAKE_CMD=make CC="$TARGET-gcc" AS="$TARGET-as" RANLIB="$TARGET-ranlib" AR="$TARGET-ar" WINDRES="$TARGET-windres" STRIP="$TARGET-strip" export PATH CC AS AR RANLIB WINDRES STRIP exec $MAKE_CMD CC=$CC AS=$AS RANLIB=$RANLIB AR=$AR WINDRES=$WINDRES STRIP=$STRIP -f Makefile.w64 $* ������������������������������������������������������������������������quakespasm-0.93.0/Quake/host_cmd.c������������������������������������������������������������������0000644�0000000�0000000�00000140162�13141155354�015473� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #ifndef _WIN32 #include <dirent.h> #endif extern cvar_t pausable; int current_skill; void Mod_Print (void); /* ================== Host_Quit_f ================== */ void Host_Quit_f (void) { if (key_dest != key_console && cls.state != ca_dedicated) { M_Menu_Quit_f (); return; } CL_Disconnect (); Host_ShutdownServer(false); Sys_Quit (); } //============================================================================== //johnfitz -- extramaps management //============================================================================== /* ================== FileList_Add ================== */ void FileList_Add (const char *name, filelist_item_t **list) { filelist_item_t *item,*cursor,*prev; // ignore duplicate for (item = *list; item; item = item->next) { if (!Q_strcmp (name, item->name)) return; } item = (filelist_item_t *) Z_Malloc(sizeof(filelist_item_t)); q_strlcpy (item->name, name, sizeof(item->name)); // insert each entry in alphabetical order if (*list == NULL || q_strcasecmp(item->name, (*list)->name) < 0) //insert at front { item->next = *list; *list = item; } else //insert later { prev = *list; cursor = (*list)->next; while (cursor && (q_strcasecmp(item->name, cursor->name) > 0)) { prev = cursor; cursor = cursor->next; } item->next = prev->next; prev->next = item; } } static void FileList_Clear (filelist_item_t **list) { filelist_item_t *blah; while (*list) { blah = (*list)->next; Z_Free(*list); *list = blah; } } filelist_item_t *extralevels; void ExtraMaps_Add (const char *name) { FileList_Add(name, &extralevels); } void ExtraMaps_Init (void) { #ifdef _WIN32 WIN32_FIND_DATA fdat; HANDLE fhnd; #else DIR *dir_p; struct dirent *dir_t; #endif char filestring[MAX_OSPATH]; char mapname[32]; char ignorepakdir[32]; searchpath_t *search; pack_t *pak; int i; // we don't want to list the maps in id1 pakfiles, // because these are not "add-on" levels q_snprintf (ignorepakdir, sizeof(ignorepakdir), "/%s/", GAMENAME); for (search = com_searchpaths; search; search = search->next) { if (*search->filename) //directory { #ifdef _WIN32 q_snprintf (filestring, sizeof(filestring), "%s/maps/*.bsp", search->filename); fhnd = FindFirstFile(filestring, &fdat); if (fhnd == INVALID_HANDLE_VALUE) continue; do { COM_StripExtension(fdat.cFileName, mapname, sizeof(mapname)); ExtraMaps_Add (mapname); } while (FindNextFile(fhnd, &fdat)); FindClose(fhnd); #else q_snprintf (filestring, sizeof(filestring), "%s/maps/", search->filename); dir_p = opendir(filestring); if (dir_p == NULL) continue; while ((dir_t = readdir(dir_p)) != NULL) { if (q_strcasecmp(COM_FileGetExtension(dir_t->d_name), "bsp") != 0) continue; COM_StripExtension(dir_t->d_name, mapname, sizeof(mapname)); ExtraMaps_Add (mapname); } closedir(dir_p); #endif } else //pakfile { if (!strstr(search->pack->filename, ignorepakdir)) { //don't list standard id maps for (i = 0, pak = search->pack; i < pak->numfiles; i++) { if (!strcmp(COM_FileGetExtension(pak->files[i].name), "bsp")) { if (pak->files[i].filelen > 32*1024) { // don't list files under 32k (ammo boxes etc) COM_StripExtension(pak->files[i].name + 5, mapname, sizeof(mapname)); ExtraMaps_Add (mapname); } } } } } } } static void ExtraMaps_Clear (void) { FileList_Clear(&extralevels); } void ExtraMaps_NewGame (void) { ExtraMaps_Clear (); ExtraMaps_Init (); } /* ================== Host_Maps_f ================== */ void Host_Maps_f (void) { int i; filelist_item_t *level; for (level = extralevels, i = 0; level; level = level->next, i++) Con_SafePrintf (" %s\n", level->name); if (i) Con_SafePrintf ("%i map(s)\n", i); else Con_SafePrintf ("no maps found\n"); } //============================================================================== //johnfitz -- modlist management //============================================================================== filelist_item_t *modlist; void Modlist_Add (const char *name) { FileList_Add(name, &modlist); } #ifdef _WIN32 void Modlist_Init (void) { WIN32_FIND_DATA fdat; HANDLE fhnd; DWORD attribs; char dir_string[MAX_OSPATH], mod_string[MAX_OSPATH]; q_snprintf (dir_string, sizeof(dir_string), "%s/*", com_basedir); fhnd = FindFirstFile(dir_string, &fdat); if (fhnd == INVALID_HANDLE_VALUE) return; do { if (!strcmp(fdat.cFileName, ".") || !strcmp(fdat.cFileName, "..")) continue; q_snprintf (mod_string, sizeof(mod_string), "%s/%s", com_basedir, fdat.cFileName); attribs = GetFileAttributes (mod_string); if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) { /* don't bother testing for pak files / progs.dat */ Modlist_Add(fdat.cFileName); } } while (FindNextFile(fhnd, &fdat)); FindClose(fhnd); } #else void Modlist_Init (void) { DIR *dir_p, *mod_dir_p; struct dirent *dir_t; char dir_string[MAX_OSPATH], mod_string[MAX_OSPATH]; q_snprintf (dir_string, sizeof(dir_string), "%s/", com_basedir); dir_p = opendir(dir_string); if (dir_p == NULL) return; while ((dir_t = readdir(dir_p)) != NULL) { if (!strcmp(dir_t->d_name, ".") || !strcmp(dir_t->d_name, "..")) continue; if (!q_strcasecmp (COM_FileGetExtension (dir_t->d_name), "app")) // skip .app bundles on macOS continue; q_snprintf(mod_string, sizeof(mod_string), "%s%s/", dir_string, dir_t->d_name); mod_dir_p = opendir(mod_string); if (mod_dir_p == NULL) continue; /* don't bother testing for pak files / progs.dat */ Modlist_Add(dir_t->d_name); closedir(mod_dir_p); } closedir(dir_p); } #endif //============================================================================== //ericw -- demo list management //============================================================================== filelist_item_t *demolist; static void DemoList_Clear (void) { FileList_Clear (&demolist); } void DemoList_Rebuild (void) { DemoList_Clear (); DemoList_Init (); } // TODO: Factor out to a general-purpose file searching function void DemoList_Init (void) { #ifdef _WIN32 WIN32_FIND_DATA fdat; HANDLE fhnd; #else DIR *dir_p; struct dirent *dir_t; #endif char filestring[MAX_OSPATH]; char demname[32]; char ignorepakdir[32]; searchpath_t *search; pack_t *pak; int i; // we don't want to list the demos in id1 pakfiles, // because these are not "add-on" demos q_snprintf (ignorepakdir, sizeof(ignorepakdir), "/%s/", GAMENAME); for (search = com_searchpaths; search; search = search->next) { if (*search->filename) //directory { #ifdef _WIN32 q_snprintf (filestring, sizeof(filestring), "%s/*.dem", search->filename); fhnd = FindFirstFile(filestring, &fdat); if (fhnd == INVALID_HANDLE_VALUE) continue; do { COM_StripExtension(fdat.cFileName, demname, sizeof(demname)); FileList_Add (demname, &demolist); } while (FindNextFile(fhnd, &fdat)); FindClose(fhnd); #else q_snprintf (filestring, sizeof(filestring), "%s/", search->filename); dir_p = opendir(filestring); if (dir_p == NULL) continue; while ((dir_t = readdir(dir_p)) != NULL) { if (q_strcasecmp(COM_FileGetExtension(dir_t->d_name), "dem") != 0) continue; COM_StripExtension(dir_t->d_name, demname, sizeof(demname)); FileList_Add (demname, &demolist); } closedir(dir_p); #endif } else //pakfile { if (!strstr(search->pack->filename, ignorepakdir)) { //don't list standard id demos for (i = 0, pak = search->pack; i < pak->numfiles; i++) { if (!strcmp(COM_FileGetExtension(pak->files[i].name), "dem")) { COM_StripExtension(pak->files[i].name, demname, sizeof(demname)); FileList_Add (demname, &demolist); } } } } } } /* ================== Host_Mods_f -- johnfitz list all potential mod directories (contain either a pak file or a progs.dat) ================== */ void Host_Mods_f (void) { int i; filelist_item_t *mod; for (mod = modlist, i=0; mod; mod = mod->next, i++) Con_SafePrintf (" %s\n", mod->name); if (i) Con_SafePrintf ("%i mod(s)\n", i); else Con_SafePrintf ("no mods found\n"); } //============================================================================== /* ============= Host_Mapname_f -- johnfitz ============= */ void Host_Mapname_f (void) { if (sv.active) { Con_Printf ("\"mapname\" is \"%s\"\n", sv.name); return; } if (cls.state == ca_connected) { Con_Printf ("\"mapname\" is \"%s\"\n", cl.mapname); return; } Con_Printf ("no map loaded\n"); } /* ================== Host_Status_f ================== */ void Host_Status_f (void) { void (*print_fn) (const char *fmt, ...) FUNCP_PRINTF(1,2); client_t *client; int seconds; int minutes; int hours = 0; int j; if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print_fn = Con_Printf; } else print_fn = SV_ClientPrintf; print_fn ("host: %s\n", Cvar_VariableString ("hostname")); print_fn ("version: %4.2f\n", VERSION); if (tcpipAvailable) print_fn ("tcp/ip: %s\n", my_tcpip_address); if (ipxAvailable) print_fn ("ipx: %s\n", my_ipx_address); print_fn ("map: %s\n", sv.name); print_fn ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients); for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; seconds = (int)(net_time - NET_QSocketGetTime(client->netconnection)); minutes = seconds / 60; if (minutes) { seconds -= (minutes * 60); hours = minutes / 60; if (hours) minutes -= (hours * 60); } else hours = 0; print_fn ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds); print_fn (" %s\n", NET_QSocketGetAddressString(client->netconnection)); } } /* ================== Host_God_f Sets client to godmode ================== */ void Host_God_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; //johnfitz -- allow user to explicitly set god mode to on or off switch (Cmd_Argc()) { case 1: sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; if (!((int)sv_player->v.flags & FL_GODMODE) ) SV_ClientPrintf ("godmode OFF\n"); else SV_ClientPrintf ("godmode ON\n"); break; case 2: if (Q_atof(Cmd_Argv(1))) { sv_player->v.flags = (int)sv_player->v.flags | FL_GODMODE; SV_ClientPrintf ("godmode ON\n"); } else { sv_player->v.flags = (int)sv_player->v.flags & ~FL_GODMODE; SV_ClientPrintf ("godmode OFF\n"); } break; default: Con_Printf("god [value] : toggle god mode. values: 0 = off, 1 = on\n"); break; } //johnfitz } /* ================== Host_Notarget_f ================== */ void Host_Notarget_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; //johnfitz -- allow user to explicitly set notarget to on or off switch (Cmd_Argc()) { case 1: sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; if (!((int)sv_player->v.flags & FL_NOTARGET) ) SV_ClientPrintf ("notarget OFF\n"); else SV_ClientPrintf ("notarget ON\n"); break; case 2: if (Q_atof(Cmd_Argv(1))) { sv_player->v.flags = (int)sv_player->v.flags | FL_NOTARGET; SV_ClientPrintf ("notarget ON\n"); } else { sv_player->v.flags = (int)sv_player->v.flags & ~FL_NOTARGET; SV_ClientPrintf ("notarget OFF\n"); } break; default: Con_Printf("notarget [value] : toggle notarget mode. values: 0 = off, 1 = on\n"); break; } //johnfitz } qboolean noclip_anglehack; /* ================== Host_Noclip_f ================== */ void Host_Noclip_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; //johnfitz -- allow user to explicitly set noclip to on or off switch (Cmd_Argc()) { case 1: if (sv_player->v.movetype != MOVETYPE_NOCLIP) { noclip_anglehack = true; sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf ("noclip ON\n"); } else { noclip_anglehack = false; sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("noclip OFF\n"); } break; case 2: if (Q_atof(Cmd_Argv(1))) { noclip_anglehack = true; sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf ("noclip ON\n"); } else { noclip_anglehack = false; sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("noclip OFF\n"); } break; default: Con_Printf("noclip [value] : toggle noclip mode. values: 0 = off, 1 = on\n"); break; } //johnfitz } /* ==================== Host_SetPos_f adapted from fteqw, originally by Alex Shadowalker ==================== */ void Host_SetPos_f(void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; if (Cmd_Argc() != 7 && Cmd_Argc() != 4) { SV_ClientPrintf("usage:\n"); SV_ClientPrintf(" setpos <x> <y> <z>\n"); SV_ClientPrintf(" setpos <x> <y> <z> <pitch> <yaw> <roll>\n"); SV_ClientPrintf("current values:\n"); SV_ClientPrintf(" %i %i %i %i %i %i\n", (int)sv_player->v.origin[0], (int)sv_player->v.origin[1], (int)sv_player->v.origin[2], (int)sv_player->v.v_angle[0], (int)sv_player->v.v_angle[1], (int)sv_player->v.v_angle[2]); return; } if (sv_player->v.movetype != MOVETYPE_NOCLIP) { noclip_anglehack = true; sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf ("noclip ON\n"); } //make sure they're not going to whizz away from it sv_player->v.velocity[0] = 0; sv_player->v.velocity[1] = 0; sv_player->v.velocity[2] = 0; sv_player->v.origin[0] = atof(Cmd_Argv(1)); sv_player->v.origin[1] = atof(Cmd_Argv(2)); sv_player->v.origin[2] = atof(Cmd_Argv(3)); if (Cmd_Argc() == 7) { sv_player->v.angles[0] = atof(Cmd_Argv(4)); sv_player->v.angles[1] = atof(Cmd_Argv(5)); sv_player->v.angles[2] = atof(Cmd_Argv(6)); sv_player->v.fixangle = 1; } SV_LinkEdict (sv_player, false); } /* ================== Host_Fly_f Sets client to flymode ================== */ void Host_Fly_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; //johnfitz -- allow user to explicitly set noclip to on or off switch (Cmd_Argc()) { case 1: if (sv_player->v.movetype != MOVETYPE_FLY) { sv_player->v.movetype = MOVETYPE_FLY; SV_ClientPrintf ("flymode ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("flymode OFF\n"); } break; case 2: if (Q_atof(Cmd_Argv(1))) { sv_player->v.movetype = MOVETYPE_FLY; SV_ClientPrintf ("flymode ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("flymode OFF\n"); } break; default: Con_Printf("fly [value] : toggle fly mode. values: 0 = off, 1 = on\n"); break; } //johnfitz } /* ================== Host_Ping_f ================== */ void Host_Ping_f (void) { int i, j; float total; client_t *client; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } SV_ClientPrintf ("Client ping times:\n"); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; total = 0; for (j = 0; j < NUM_PING_TIMES; j++) total+=client->ping_times[j]; total /= NUM_PING_TIMES; SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name); } } /* =============================================================================== SERVER TRANSITIONS =============================================================================== */ /* ====================== Host_Map_f handle a map <servername> command from the console. Active clients are kicked off. ====================== */ void Host_Map_f (void) { int i; char name[MAX_QPATH], *p; if (Cmd_Argc() < 2) //no map name given { if (cls.state == ca_dedicated) { if (sv.active) Con_Printf ("Current map: %s\n", sv.name); else Con_Printf ("Server not active\n"); } else if (cls.state == ca_connected) { Con_Printf ("Current map: %s ( %s )\n", cl.levelname, cl.mapname); } else { Con_Printf ("map <levelname>: start a new server\n"); } return; } if (cmd_source != src_command) return; cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); Host_ShutdownServer(false); if (cls.state != ca_dedicated) IN_Activate(); key_dest = key_game; // remove console or menu SCR_BeginLoadingPlaque (); svs.serverflags = 0; // haven't completed an episode yet q_strlcpy (name, Cmd_Argv(1), sizeof(name)); // remove (any) trailing ".bsp" from mapname -- S.A. p = strstr(name, ".bsp"); if (p && p[4] == '\0') *p = '\0'; SV_SpawnServer (name); if (!sv.active) return; if (cls.state != ca_dedicated) { memset (cls.spawnparms, 0, MAX_MAPSTRING); for (i = 2; i < Cmd_Argc(); i++) { q_strlcat (cls.spawnparms, Cmd_Argv(i), MAX_MAPSTRING); q_strlcat (cls.spawnparms, " ", MAX_MAPSTRING); } Cmd_ExecuteString ("connect local", src_command); } } /* ====================== Host_Randmap_f Loads a random map from the "maps" list. ====================== */ void Host_Randmap_f (void) { int i, randlevel, numlevels; filelist_item_t *level; if (cmd_source != src_command) return; for (level = extralevels, numlevels = 0; level; level = level->next) numlevels++; if (numlevels == 0) { Con_Printf ("no maps\n"); return; } randlevel = (rand() % numlevels); for (level = extralevels, i = 0; level; level = level->next, i++) { if (i == randlevel) { Con_Printf ("Starting map %s...\n", level->name); Cbuf_AddText (va("map %s\n", level->name)); return; } } } /* ================== Host_Changelevel_f Goes to a new map, taking all clients along ================== */ void Host_Changelevel_f (void) { char level[MAX_QPATH]; if (Cmd_Argc() != 2) { Con_Printf ("changelevel <levelname> : continue game on a new level\n"); return; } if (!sv.active || cls.demoplayback) { Con_Printf ("Only the server may changelevel\n"); return; } //johnfitz -- check for client having map before anything else q_snprintf (level, sizeof(level), "maps/%s.bsp", Cmd_Argv(1)); if (!COM_FileExists(level, NULL)) Host_Error ("cannot find map %s", level); //johnfitz if (cls.state != ca_dedicated) IN_Activate(); // -- S.A. key_dest = key_game; // remove console or menu SV_SaveSpawnparms (); q_strlcpy (level, Cmd_Argv(1), sizeof(level)); SV_SpawnServer (level); // also issue an error if spawn failed -- O.S. if (!sv.active) Host_Error ("cannot run map %s", level); } /* ================== Host_Restart_f Restarts the current server for a dead player ================== */ void Host_Restart_f (void) { char mapname[MAX_QPATH]; if (cls.demoplayback || !sv.active) return; if (cmd_source != src_command) return; q_strlcpy (mapname, sv.name, sizeof(mapname)); // mapname gets cleared in spawnserver SV_SpawnServer (mapname); if (!sv.active) Host_Error ("cannot restart map %s", mapname); } /* ================== Host_Reconnect_f This command causes the client to wait for the signon messages again. This is sent just before a server changes levels ================== */ void Host_Reconnect_f (void) { if (cls.demoplayback) // cross-map demo playback fix from Baker return; SCR_BeginLoadingPlaque (); cls.signon = 0; // need new connection messages } /* ===================== Host_Connect_f User command to connect to server ===================== */ void Host_Connect_f (void) { char name[MAX_QPATH]; cls.demonum = -1; // stop demo loop in case this fails if (cls.demoplayback) { CL_StopPlayback (); CL_Disconnect (); } q_strlcpy (name, Cmd_Argv(1), sizeof(name)); CL_EstablishConnection (name); Host_Reconnect_f (); } /* =============================================================================== LOAD / SAVE GAME =============================================================================== */ #define SAVEGAME_VERSION 5 /* =============== Host_SavegameComment Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current =============== */ void Host_SavegameComment (char *text) { int i; char kills[20]; for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) text[i] = ' '; memcpy (text, cl.levelname, q_min(strlen(cl.levelname),22)); //johnfitz -- only copy 22 chars. sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); memcpy (text+22, kills, strlen(kills)); // convert space to _ to make stdio happy for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) { if (text[i] == ' ') text[i] = '_'; } text[SAVEGAME_COMMENT_LENGTH] = '\0'; } /* =============== Host_Savegame_f =============== */ void Host_Savegame_f (void) { char name[MAX_OSPATH]; FILE *f; int i; char comment[SAVEGAME_COMMENT_LENGTH+1]; if (cmd_source != src_command) return; if (!sv.active) { Con_Printf ("Not playing a local game.\n"); return; } if (cl.intermission) { Con_Printf ("Can't save in intermission.\n"); return; } if (svs.maxclients != 1) { Con_Printf ("Can't save multiplayer games.\n"); return; } if (Cmd_Argc() != 2) { Con_Printf ("save <savename> : save a game\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } for (i=0 ; i<svs.maxclients ; i++) { if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) ) { Con_Printf ("Can't savegame with a dead player\n"); return; } } q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); COM_AddExtension (name, ".sav", sizeof(name)); Con_Printf ("Saving game to %s...\n", name); f = fopen (name, "w"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fprintf (f, "%i\n", SAVEGAME_VERSION); Host_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i = 0; i < NUM_SPAWN_PARMS; i++) fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n",sv.time); // write the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (sv.lightstyles[i]) fprintf (f, "%s\n", sv.lightstyles[i]); else fprintf (f,"m\n"); } ED_WriteGlobals (f); for (i = 0; i < sv.num_edicts; i++) { ED_Write (f, EDICT_NUM(i)); fflush (f); } fclose (f); Con_Printf ("done.\n"); } /* =============== Host_Loadgame_f =============== */ void Host_Loadgame_f (void) { static char *start; char name[MAX_OSPATH]; char mapname[MAX_QPATH]; float time, tfloat; const char *data; int i; edict_t *ent; int entnum; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load <savename> : load a game\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } cls.demonum = -1; // stop demo loop in case this fails q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); COM_AddExtension (name, ".sav", sizeof(name)); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_Printf ("Loading game from %s...\n", name); // avoid leaking if the previous Host_Loadgame_f failed with a Host_Error if (start != NULL) free (start); start = (char *) COM_LoadMallocFile_TextMode_OSPath(name, NULL); if (start == NULL) { Con_Printf ("ERROR: couldn't open.\n"); return; } data = start; data = COM_ParseIntNewline (data, &version); if (version != SAVEGAME_VERSION) { free (start); start = NULL; Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } data = COM_ParseStringNewline (data); for (i = 0; i < NUM_SPAWN_PARMS; i++) data = COM_ParseFloatNewline (data, &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values data = COM_ParseFloatNewline(data, &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_SetValue ("skill", (float)current_skill); data = COM_ParseStringNewline (data); q_strlcpy (mapname, com_token, sizeof(mapname)); data = COM_ParseFloatNewline (data, &time); CL_Disconnect_f (); SV_SpawnServer (mapname); if (!sv.active) { free (start); start = NULL; Con_Printf ("Couldn't load map\n"); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { data = COM_ParseStringNewline (data); sv.lightstyles[i] = (const char *)Hunk_Strdup (com_token, "lightstyles"); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (*data) { data = COM_Parse (data); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) { Sys_Error ("First token isn't a brace"); } if (entnum == -1) { // parse the global vars data = ED_ParseGlobals (data); } else { // parse an edict ent = EDICT_NUM(entnum); if (entnum < sv.num_edicts) { ent->free = false; memset (&ent->v, 0, progs->entityfields * 4); } else { memset (ent, 0, pr_edict_size); } data = ED_ParseEdict (data, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; free (start); start = NULL; for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } } //============================================================================ /* ====================== Host_Name_f ====================== */ void Host_Name_f (void) { char newName[32]; if (Cmd_Argc () == 1) { Con_Printf ("\"name\" is \"%s\"\n", cl_name.string); return; } if (Cmd_Argc () == 2) q_strlcpy(newName, Cmd_Argv(1), sizeof(newName)); else q_strlcpy(newName, Cmd_Args(), sizeof(newName)); newName[15] = 0; // client_t structure actually says name[32]. if (cmd_source == src_command) { if (Q_strcmp(cl_name.string, newName) == 0) return; Cvar_Set ("_cl_name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) { if (Q_strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); } Q_strcpy (host_client->name, newName); host_client->edict->v.netname = PR_SetEngineString(host_client->name); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); } void Host_Say(qboolean teamonly) { int j; client_t *client; client_t *save; const char *p; char text[MAXCMDLINE], *p2; qboolean quoted; qboolean fromServer = false; if (cmd_source == src_command) { if (cls.state != ca_dedicated) { Cmd_ForwardToServer (); return; } fromServer = true; teamonly = false; } if (Cmd_Argc () < 2) return; save = host_client; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } // turn on color set 1 if (!fromServer) q_snprintf (text, sizeof(text), "\001%s: %s", save->name, p); else q_snprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client || !client->active || !client->spawned) continue; if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team) continue; host_client = client; SV_ClientPrintf("%s", text); } host_client = save; if (cls.state == ca_dedicated) Sys_Printf("%s", &text[1]); } void Host_Say_f(void) { Host_Say(false); } void Host_Say_Team_f(void) { Host_Say(true); } void Host_Tell_f(void) { int j; client_t *client; client_t *save; const char *p; char text[MAXCMDLINE], *p2; qboolean quoted; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (Cmd_Argc () < 3) return; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } q_snprintf (text, sizeof(text), "%s: %s", host_client->name, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } save = host_client; for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active || !client->spawned) continue; if (q_strcasecmp(client->name, Cmd_Argv(1))) continue; host_client = client; SV_ClientPrintf("%s", text); break; } host_client = save; } /* ================== Host_Color_f ================== */ void Host_Color_f(void) { int top, bottom; int playercolor; if (Cmd_Argc() == 1) { Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f); Con_Printf ("color <0-13> [0-13]\n"); return; } if (Cmd_Argc() == 2) top = bottom = atoi(Cmd_Argv(1)); else { top = atoi(Cmd_Argv(1)); bottom = atoi(Cmd_Argv(2)); } top &= 15; if (top > 13) top = 13; bottom &= 15; if (bottom > 13) bottom = 13; playercolor = top*16 + bottom; if (cmd_source == src_command) { Cvar_SetValue ("_cl_color", playercolor); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } host_client->colors = playercolor; host_client->edict->v.team = bottom + 1; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, host_client->colors); } /* ================== Host_Kill_f ================== */ void Host_Kill_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (sv_player->v.health <= 0) { SV_ClientPrintf ("Can't suicide -- allready dead!\n"); return; } pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientKill); } /* ================== Host_Pause_f ================== */ void Host_Pause_f (void) { //ericw -- demo pause support (inspired by MarkV) if (cls.demoplayback) { cls.demopaused = !cls.demopaused; cl.paused = cls.demopaused; return; } if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (!pausable.value) SV_ClientPrintf ("Pause not allowed.\n"); else { sv.paused ^= 1; if (sv.paused) { SV_BroadcastPrintf ("%s paused the game\n", PR_GetString(sv_player->v.netname)); } else { SV_BroadcastPrintf ("%s unpaused the game\n",PR_GetString(sv_player->v.netname)); } // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } } //=========================================================================== /* ================== Host_PreSpawn_f ================== */ void Host_PreSpawn_f (void) { if (cmd_source == src_command) { Con_Printf ("prespawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("prespawn not valid -- allready spawned\n"); return; } SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 2); host_client->sendsignon = true; } /* ================== Host_Spawn_f ================== */ void Host_Spawn_f (void) { int i; client_t *client; edict_t *ent; if (cmd_source == src_command) { Con_Printf ("spawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("Spawn not valid -- allready spawned\n"); return; } // run the entrance script if (sv.loadgame) { // loaded games are fully inited allready // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client->edict; memset (&ent->v, 0, progs->entityfields * 4); ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = PR_SetEngineString(host_client->name); // copy spawn parms out of the client_t for (i=0 ; i< NUM_SPAWN_PARMS ; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientConnect); if ((Sys_DoubleTime() - NET_QSocketGetTime(host_client->netconnection)) <= sv.time) Sys_Printf ("%s entered the game\n", host_client->name); PR_ExecuteProgram (pr_global_struct->PutClientInServer); } // send all current names, colors, and frag counts SZ_Clear (&host_client->message); // send time of update MSG_WriteByte (&host_client->message, svc_time); MSG_WriteFloat (&host_client->message, sv.time); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { MSG_WriteByte (&host_client->message, svc_updatename); MSG_WriteByte (&host_client->message, i); MSG_WriteString (&host_client->message, client->name); MSG_WriteByte (&host_client->message, svc_updatefrags); MSG_WriteByte (&host_client->message, i); MSG_WriteShort (&host_client->message, client->old_frags); MSG_WriteByte (&host_client->message, svc_updatecolors); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->colors); } // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&host_client->message, svc_lightstyle); MSG_WriteByte (&host_client->message, (char)i); MSG_WriteString (&host_client->message, sv.lightstyles[i]); } // // send some stats // MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_SECRETS); MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_MONSTERS); MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->message, svc_setangle); for (i = 0; i < 2; i++) MSG_WriteAngle (&host_client->message, ent->v.angles[i], sv.protocolflags ); MSG_WriteAngle (&host_client->message, 0, sv.protocolflags ); SV_WriteClientdataToMessage (sv_player, &host_client->message); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 3); host_client->sendsignon = true; } /* ================== Host_Begin_f ================== */ void Host_Begin_f (void) { if (cmd_source == src_command) { Con_Printf ("begin is not valid from the console\n"); return; } host_client->spawned = true; } //=========================================================================== /* ================== Host_Kick_f Kicks a user off of the server ================== */ void Host_Kick_f (void) { const char *who; const char *message = NULL; client_t *save; int i; qboolean byNumber = false; if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } } else if (pr_global_struct->deathmatch) return; save = host_client; if (Cmd_Argc() > 2 && Q_strcmp(Cmd_Argv(1), "#") == 0) { i = Q_atof(Cmd_Argv(2)) - 1; if (i < 0 || i >= svs.maxclients) return; if (!svs.clients[i].active) return; host_client = &svs.clients[i]; byNumber = true; } else { for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; if (q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0) break; } } if (i < svs.maxclients) { if (cmd_source == src_command) if (cls.state == ca_dedicated) who = "Console"; else who = cl_name.string; else who = save->name; // can't kick yourself! if (host_client == save) return; if (Cmd_Argc() > 2) { message = COM_Parse(Cmd_Args()); if (byNumber) { message++; // skip the # while (*message == ' ') // skip white space message++; message += strlen(Cmd_Argv(2)); // skip the number } while (*message && *message == ' ') message++; } if (message) SV_ClientPrintf ("Kicked by %s: %s\n", who, message); else SV_ClientPrintf ("Kicked by %s\n", who); SV_DropClient (false); } host_client = save; } /* =============================================================================== DEBUGGING TOOLS =============================================================================== */ /* ================== Host_Give_f ================== */ void Host_Give_f (void) { const char *t; int v; eval_t *val; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch) return; t = Cmd_Argv(1); v = atoi (Cmd_Argv(2)); switch (t[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // MED 01/04/97 added hipnotic give stuff if (hipnotic) { if (t[0] == '6') { if (t[1] == 'a') sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN; else sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER; } else if (t[0] == '9') sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON; else if (t[0] == '0') sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR; else if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); } else { if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); } break; case 's': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_shells1"); if (val) val->_float = v; } sv_player->v.ammo_shells = v; break; case 'n': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_nails1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_nails = v; } } else { sv_player->v.ammo_nails = v; } break; case 'l': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_lava_nails"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_nails = v; } } break; case 'r': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_rockets1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_rockets = v; } } else { sv_player->v.ammo_rockets = v; } break; case 'm': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_multi_rockets"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_rockets = v; } } break; case 'h': sv_player->v.health = v; break; case 'c': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_cells1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_cells = v; } } else { sv_player->v.ammo_cells = v; } break; case 'p': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_plasma"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_cells = v; } } break; //johnfitz -- give armour case 'a': if (v > 150) { sv_player->v.armortype = 0.8; sv_player->v.armorvalue = v; sv_player->v.items = sv_player->v.items - ((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3; } else if (v > 100) { sv_player->v.armortype = 0.6; sv_player->v.armorvalue = v; sv_player->v.items = sv_player->v.items - ((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR2; } else if (v >= 0) { sv_player->v.armortype = 0.3; sv_player->v.armorvalue = v; sv_player->v.items = sv_player->v.items - ((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR1; } break; //johnfitz } //johnfitz -- update currentammo to match new ammo (so statusbar updates correctly) switch ((int)(sv_player->v.weapon)) { case IT_SHOTGUN: case IT_SUPER_SHOTGUN: sv_player->v.currentammo = sv_player->v.ammo_shells; break; case IT_NAILGUN: case IT_SUPER_NAILGUN: case RIT_LAVA_SUPER_NAILGUN: sv_player->v.currentammo = sv_player->v.ammo_nails; break; case IT_GRENADE_LAUNCHER: case IT_ROCKET_LAUNCHER: case RIT_MULTI_GRENADE: case RIT_MULTI_ROCKET: sv_player->v.currentammo = sv_player->v.ammo_rockets; break; case IT_LIGHTNING: case HIT_LASER_CANNON: case HIT_MJOLNIR: sv_player->v.currentammo = sv_player->v.ammo_cells; break; case RIT_LAVA_NAILGUN: //same as IT_AXE if (rogue) sv_player->v.currentammo = sv_player->v.ammo_nails; break; case RIT_PLASMA_GUN: //same as HIT_PROXIMITY_GUN if (rogue) sv_player->v.currentammo = sv_player->v.ammo_cells; if (hipnotic) sv_player->v.currentammo = sv_player->v.ammo_rockets; break; } //johnfitz } edict_t *FindViewthing (void) { int i; edict_t *e; for (i=0 ; i<sv.num_edicts ; i++) { e = EDICT_NUM(i); if ( !strcmp (PR_GetString(e->v.classname), "viewthing") ) return e; } Con_Printf ("No viewthing on map\n"); return NULL; } /* ================== Host_Viewmodel_f ================== */ void Host_Viewmodel_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = Mod_ForName (Cmd_Argv(1), false); if (!m) { Con_Printf ("Can't load %s\n", Cmd_Argv(1)); return; } e->v.frame = 0; cl.model_precache[(int)e->v.modelindex] = m; } /* ================== Host_Viewframe_f ================== */ void Host_Viewframe_f (void) { edict_t *e; int f; qmodel_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; f = atoi(Cmd_Argv(1)); if (f >= m->numframes) f = m->numframes - 1; e->v.frame = f; } void PrintFrameName (qmodel_t *m, int frame) { aliashdr_t *hdr; maliasframedesc_t *pframedesc; hdr = (aliashdr_t *)Mod_Extradata (m); if (!hdr) return; pframedesc = &hdr->frames[frame]; Con_Printf ("frame %i: %s\n", frame, pframedesc->name); } /* ================== Host_Viewnext_f ================== */ void Host_Viewnext_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame + 1; if (e->v.frame >= m->numframes) e->v.frame = m->numframes - 1; PrintFrameName (m, e->v.frame); } /* ================== Host_Viewprev_f ================== */ void Host_Viewprev_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame - 1; if (e->v.frame < 0) e->v.frame = 0; PrintFrameName (m, e->v.frame); } /* =============================================================================== DEMO LOOP CONTROL =============================================================================== */ /* ================== Host_Startdemos_f ================== */ void Host_Startdemos_f (void) { int i, c; if (cls.state == ca_dedicated) return; c = Cmd_Argc() - 1; if (c > MAX_DEMOS) { Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS); c = MAX_DEMOS; } Con_Printf ("%i demo(s) in loop\n", c); for (i = 1; i < c + 1; i++) q_strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])); if (!sv.active && cls.demonum != -1 && !cls.demoplayback) { cls.demonum = 0; if (!fitzmode) { /* QuakeSpasm customization: */ /* go straight to menu, no CL_NextDemo */ cls.demonum = -1; Cbuf_InsertText("menu_main\n"); return; } CL_NextDemo (); } else { cls.demonum = -1; } } /* ================== Host_Demos_f Return to looping demos ================== */ void Host_Demos_f (void) { if (cls.state == ca_dedicated) return; if (cls.demonum == -1) cls.demonum = 1; CL_Disconnect_f (); CL_NextDemo (); } /* ================== Host_Stopdemo_f Return to looping demos ================== */ void Host_Stopdemo_f (void) { if (cls.state == ca_dedicated) return; if (!cls.demoplayback) return; CL_StopPlayback (); CL_Disconnect (); } //============================================================================= /* ================== Host_InitCommands ================== */ void Host_InitCommands (void) { Cmd_AddCommand ("maps", Host_Maps_f); //johnfitz Cmd_AddCommand ("mods", Host_Mods_f); //johnfitz Cmd_AddCommand ("games", Host_Mods_f); // as an alias to "mods" -- S.A. / QuakeSpasm Cmd_AddCommand ("mapname", Host_Mapname_f); //johnfitz Cmd_AddCommand ("randmap", Host_Randmap_f); //ericw Cmd_AddCommand ("status", Host_Status_f); Cmd_AddCommand ("quit", Host_Quit_f); Cmd_AddCommand ("god", Host_God_f); Cmd_AddCommand ("notarget", Host_Notarget_f); Cmd_AddCommand ("fly", Host_Fly_f); Cmd_AddCommand ("map", Host_Map_f); Cmd_AddCommand ("restart", Host_Restart_f); Cmd_AddCommand ("changelevel", Host_Changelevel_f); Cmd_AddCommand ("connect", Host_Connect_f); Cmd_AddCommand ("reconnect", Host_Reconnect_f); Cmd_AddCommand ("name", Host_Name_f); Cmd_AddCommand ("noclip", Host_Noclip_f); Cmd_AddCommand ("setpos", Host_SetPos_f); //QuakeSpasm Cmd_AddCommand ("say", Host_Say_f); Cmd_AddCommand ("say_team", Host_Say_Team_f); Cmd_AddCommand ("tell", Host_Tell_f); Cmd_AddCommand ("color", Host_Color_f); Cmd_AddCommand ("kill", Host_Kill_f); Cmd_AddCommand ("pause", Host_Pause_f); Cmd_AddCommand ("spawn", Host_Spawn_f); Cmd_AddCommand ("begin", Host_Begin_f); Cmd_AddCommand ("prespawn", Host_PreSpawn_f); Cmd_AddCommand ("kick", Host_Kick_f); Cmd_AddCommand ("ping", Host_Ping_f); Cmd_AddCommand ("load", Host_Loadgame_f); Cmd_AddCommand ("save", Host_Savegame_f); Cmd_AddCommand ("give", Host_Give_f); Cmd_AddCommand ("startdemos", Host_Startdemos_f); Cmd_AddCommand ("demos", Host_Demos_f); Cmd_AddCommand ("stopdemo", Host_Stopdemo_f); Cmd_AddCommand ("viewmodel", Host_Viewmodel_f); Cmd_AddCommand ("viewframe", Host_Viewframe_f); Cmd_AddCommand ("viewnext", Host_Viewnext_f); Cmd_AddCommand ("viewprev", Host_Viewprev_f); Cmd_AddCommand ("mcache", Mod_Print); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/r_brush.c�������������������������������������������������������������������0000644�0000000�0000000�00000076710�13067122410�015340� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_brush.c: brush model rendering. renamed from r_surf.c #include "quakedef.h" extern cvar_t gl_fullbrights, r_drawflat, gl_overbright, r_oldwater; //johnfitz extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix int gl_lightmap_format; int lightmap_bytes; #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 gltexture_t *lightmap_textures[MAX_LIGHTMAPS]; //johnfitz -- changed to an array unsigned blocklights[BLOCK_WIDTH*BLOCK_HEIGHT*3]; //johnfitz -- was 18*18, added lit support (*3) and loosened surface extents maximum (BLOCK_WIDTH*BLOCK_HEIGHT) typedef struct glRect_s { unsigned char l,t,w,h; } glRect_t; glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; qboolean lightmap_modified[MAX_LIGHTMAPS]; glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; int last_lightmap_allocated; //ericw -- optimization: remember the index of the last lightmap AllocBlock stored a surf in // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; /* =============== R_TextureAnimation -- johnfitz -- added "frame" param to eliminate use of "currententity" global Returns the proper texture for a given time and base texture =============== */ texture_t *R_TextureAnimation (texture_t *base, int frame) { int relative; int count; if (frame) if (base->alternate_anims) base = base->alternate_anims; if (!base->anim_total) return base; relative = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > relative || base->anim_max <= relative) { base = base->anim_next; if (!base) Sys_Error ("R_TextureAnimation: broken cycle"); if (++count > 100) Sys_Error ("R_TextureAnimation: infinite cycle"); } return base; } /* ================ DrawGLPoly ================ */ void DrawGLPoly (glpoly_t *p) { float *v; int i; glBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[3], v[4]); glVertex3fv (v); } glEnd (); } /* ================ DrawGLTriangleFan -- johnfitz -- like DrawGLPoly but for r_showtris ================ */ void DrawGLTriangleFan (glpoly_t *p) { float *v; int i; glBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) { glVertex3fv (v); } glEnd (); } /* ============================================================= BRUSH MODELS ============================================================= */ #if 0 /* ================ R_DrawSequentialPoly -- johnfitz -- rewritten ================ */ void R_DrawSequentialPoly (msurface_t *s) { glpoly_t *p; texture_t *t; float *v; float entalpha; int i; t = R_TextureAnimation (s->texinfo->texture, currententity->frame); entalpha = ENTALPHA_DECODE(currententity->alpha); // drawflat if (r_drawflat_cheatsafe) { if ((s->flags & SURF_DRAWTURB) && r_oldwater.value) { for (p = s->polys->next; p; p = p->next) { srand((unsigned int) (uintptr_t) p); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (p); rs_brushpasses++; } return; } srand((unsigned int) (uintptr_t) s->polys); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (s->polys); rs_brushpasses++; return; } // fullbright if ((r_fullbright_cheatsafe) && !(s->flags & SURF_DRAWTILED)) { if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } if (s->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip on alpha test GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; if (s->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } goto fullbrights; } // r_lightmap if (r_lightmap_cheatsafe) { if (s->flags & SURF_DRAWTILED) { glDisable (GL_TEXTURE_2D); DrawGLPoly (s->polys); glEnable (GL_TEXTURE_2D); rs_brushpasses++; return; } R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); if (!gl_overbright.value) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0.5, 0.5, 0.5); } glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; i<s->polys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); if (!gl_overbright.value) { glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } rs_brushpasses++; return; } // sky poly -- skip it, already handled in gl_sky.c if (s->flags & SURF_DRAWSKY) return; // water poly if (s->flags & SURF_DRAWTURB) { if (currententity->alpha == ENTALPHA_DEFAULT) entalpha = CLAMP(0.0, GL_WaterAlphaForSurface(s), 1.0); if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } if (r_oldwater.value) { GL_Bind (s->texinfo->texture->gltexture); for (p = s->polys->next; p; p = p->next) { DrawWaterPoly (p); rs_brushpasses++; } rs_brushpasses++; } else { GL_Bind (s->texinfo->texture->warpimage); s->texinfo->texture->update_warp = true; // FIXME: one frame too late! DrawGLPoly (s->polys); rs_brushpasses++; } if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } return; } // missing texture if (s->flags & SURF_NOTEXTURE) { if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } return; } // lightmapped poly if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } else glColor3f(1, 1, 1); if (s->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip on alpha test if (gl_overbright.value) { if (gl_texture_env_combine && gl_mtexable) //case 1: texture and lightmap in one pass, overbright using texture combiners { GL_DisableMultitexture(); // selects TEXTURE0 GL_Bind (t->gltexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (lightmap_textures[s->lightmaptexturenum]); R_RenderDynamicLightmaps (s); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f); glBegin(GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; i<s->polys->numverts ; i++, v+= VERTEXSIZE) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv (v); } glEnd (); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DisableMultitexture (); rs_brushpasses++; } else if (entalpha < 1 || (s->flags & SURF_DRAWFENCE)) //case 2: can't do multipass if entity has alpha, so just draw the texture { GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; } else //case 3: texture in one pass, lightmap in second pass using 2x modulation blend func, fog in third pass { //first pass -- texture with no fog Fog_DisableGFog (); GL_Bind (t->gltexture); DrawGLPoly (s->polys); Fog_EnableGFog (); rs_brushpasses++; //second pass -- lightmap with black fog, modulate blended R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); //2x modulate Fog_StartAdditive (); glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; i<s->polys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); Fog_StopAdditive (); rs_brushpasses++; //third pass -- black geo with normal fog, additive blended if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); DrawGLPoly (s->polys); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); rs_brushpasses++; } glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } else { if (gl_mtexable) //case 4: texture and lightmap in one pass, regular modulation { GL_DisableMultitexture(); // selects TEXTURE0 GL_Bind (t->gltexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (lightmap_textures[s->lightmaptexturenum]); R_RenderDynamicLightmaps (s); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin(GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; i<s->polys->numverts ; i++, v+= VERTEXSIZE) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv (v); } glEnd (); GL_DisableMultitexture (); rs_brushpasses++; } else if (entalpha < 1 || (s->flags & SURF_DRAWFENCE)) //case 5: can't do multipass if entity has alpha, so just draw the texture { GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; } else //case 6: texture in one pass, lightmap in a second pass, fog in third pass { //first pass -- texture with no fog Fog_DisableGFog (); GL_Bind (t->gltexture); DrawGLPoly (s->polys); Fog_EnableGFog (); rs_brushpasses++; //second pass -- lightmap with black fog, modulate blended R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ZERO, GL_SRC_COLOR); //modulate Fog_StartAdditive (); glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; i<s->polys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); Fog_StopAdditive (); rs_brushpasses++; //third pass -- black geo with normal fog, additive blended if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); DrawGLPoly (s->polys); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); rs_brushpasses++; } glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } if (s->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off fullbrights: if (gl_fullbrights.value && t->fullbright) { glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f (entalpha, entalpha, entalpha); GL_Bind (t->fullbright); Fog_StartAdditive (); DrawGLPoly (s->polys); Fog_StopAdditive (); glColor3f(1, 1, 1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); rs_brushpasses++; } } #endif /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel (entity_t *e) { int i, k; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; if (R_CullModelForEntity(e)) return; currententity = e; clmodel = e->model; VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (e->angles[0] || e->angles[1] || e->angles[2]) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; // calculate dynamic lighting for bmodel if it's not an // instanced model if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) { for (k=0 ; k<MAX_DLIGHTS ; k++) { if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) continue; R_MarkLights (&cl_dlights[k], k, clmodel->nodes + clmodel->hulls[0].firstclipnode); } } glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug if (gl_zfix.value) { e->origin[0] -= DIST_EPSILON; e->origin[1] -= DIST_EPSILON; e->origin[2] -= DIST_EPSILON; } R_RotateForEntity (e->origin, e->angles); if (gl_zfix.value) { e->origin[0] += DIST_EPSILON; e->origin[1] += DIST_EPSILON; e->origin[2] += DIST_EPSILON; } e->angles[0] = -e->angles[0]; // stupid quake bug R_ClearTextureChains (clmodel, chain_model); for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++) { pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { R_ChainSurface (psurf, chain_model); rs_brushpolys++; } } R_DrawTextureChains (clmodel, e, chain_model); R_DrawTextureChains_Water (clmodel, e, chain_model); glPopMatrix (); } /* ================= R_DrawBrushModel_ShowTris -- johnfitz ================= */ void R_DrawBrushModel_ShowTris (entity_t *e) { int i; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; glpoly_t *p; if (R_CullModelForEntity(e)) return; currententity = e; clmodel = e->model; VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (e->angles[0] || e->angles[1] || e->angles[2]) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e->origin, e->angles); e->angles[0] = -e->angles[0]; // stupid quake bug // // draw it // for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++) { pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { if ((psurf->flags & SURF_DRAWTURB) && r_oldwater.value) for (p = psurf->polys->next; p; p = p->next) DrawGLTriangleFan (p); else DrawGLTriangleFan (psurf->polys); } } glPopMatrix (); } /* ============================================================= LIGHTMAPS ============================================================= */ /* ================ R_RenderDynamicLightmaps called during rendering ================ */ void R_RenderDynamicLightmaps (msurface_t *fa) { byte *base; int maps; glRect_t *theRect; int smax, tmax; if (fa->flags & SURF_DRAWTILED) //johnfitz -- not a lightmapped surface return; // add to lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; // check for lightmap modification for (maps=0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic; if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic: if (r_dynamic.value) { lightmap_modified[fa->lightmaptexturenum] = true; theRect = &lightmap_rectchange[fa->lightmaptexturenum]; if (fa->light_t < theRect->t) { if (theRect->h) theRect->h += theRect->t - fa->light_t; theRect->t = fa->light_t; } if (fa->light_s < theRect->l) { if (theRect->w) theRect->w += theRect->l - fa->light_s; theRect->l = fa->light_s; } smax = (fa->extents[0]>>4)+1; tmax = (fa->extents[1]>>4)+1; if ((theRect->w + theRect->l) < (fa->light_s + smax)) theRect->w = (fa->light_s-theRect->l)+smax; if ((theRect->h + theRect->t) < (fa->light_t + tmax)) theRect->h = (fa->light_t-theRect->t)+tmax; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } } /* ======================== AllocBlock -- returns a texture number and the position inside it ======================== */ int AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; // ericw -- rather than searching starting at lightmap 0 every time, // start at the last lightmap we allocated a surface in. // This makes AllocBlock much faster on large levels (can shave off 3+ seconds // of load time on a level with 180 lightmaps), at a cost of not quite packing // lightmaps as tightly vs. not doing this (uses ~5% more lightmaps) for (texnum=last_lightmap_allocated ; texnum<MAX_LIGHTMAPS ; texnum++, last_lightmap_allocated++) { best = BLOCK_HEIGHT; for (i=0 ; i<BLOCK_WIDTH-w ; i++) { best2 = 0; for (j=0 ; j<w ; j++) { if (allocated[texnum][i+j] >= best) break; if (allocated[texnum][i+j] > best2) best2 = allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; i<w ; i++) allocated[texnum][*x + i] = best + h; return texnum; } Sys_Error ("AllocBlock: full"); return 0; //johnfitz -- shut up compiler } mvertex_t *r_pcurrentvertbase; qmodel_t *currentmodel; int nColinElim; /* ======================== GL_CreateSurfaceLightmap ======================== */ void GL_CreateSurfaceLightmap (msurface_t *surf) { int smax, tmax; byte *base; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); } /* ================ BuildSurfaceDisplayList -- called at level load time ================ */ void BuildSurfaceDisplayList (msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; // reconstruct the polygon pedges = currentmodel->edges; lnumverts = fa->numedges; // // draw texture // poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); poly->next = fa->polys; fa->polys = poly; poly->numverts = lnumverts; for (i=0 ; i<lnumverts ; i++) { lindex = currentmodel->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = r_pcurrentvertbase[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = r_pcurrentvertbase[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } //johnfitz -- removed gl_keeptjunctions code poly->numverts = lnumverts; } /* ================== GL_BuildLightmaps -- called at level load time Builds the lightmap texture with all the surfaces from all brush models ================== */ void GL_BuildLightmaps (void) { char name[16]; byte *data; int i, j; qmodel_t *m; memset (allocated, 0, sizeof(allocated)); last_lightmap_allocated = 0; r_framecount = 1; // no dlightcache //johnfitz -- null out array (the gltexture objects themselves were already freed by Mod_ClearAll) for (i=0; i < MAX_LIGHTMAPS; i++) lightmap_textures[i] = NULL; //johnfitz gl_lightmap_format = GL_RGBA;//FIXME: hardcoded for now! switch (gl_lightmap_format) { case GL_RGBA: lightmap_bytes = 4; break; case GL_BGRA: lightmap_bytes = 4; break; default: Sys_Error ("GL_BuildLightmaps: bad lightmap format"); } for (j=1 ; j<MAX_MODELS ; j++) { m = cl.model_precache[j]; if (!m) break; if (m->name[0] == '*') continue; r_pcurrentvertbase = m->vertexes; currentmodel = m; for (i=0 ; i<m->numsurfaces ; i++) { //johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags if (m->surfaces[i].flags & SURF_DRAWTILED) continue; GL_CreateSurfaceLightmap (m->surfaces + i); BuildSurfaceDisplayList (m->surfaces + i); //johnfitz } } // // upload all lightmaps that were filled // for (i=0; i<MAX_LIGHTMAPS; i++) { if (!allocated[i][0]) break; // no more used lightmap_modified[i] = false; lightmap_rectchange[i].l = BLOCK_WIDTH; lightmap_rectchange[i].t = BLOCK_HEIGHT; lightmap_rectchange[i].w = 0; lightmap_rectchange[i].h = 0; //johnfitz -- use texture manager sprintf(name, "lightmap%03i",i); data = lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes; lightmap_textures[i] = TexMgr_LoadImage (cl.worldmodel, name, BLOCK_WIDTH, BLOCK_HEIGHT, SRC_LIGHTMAP, data, "", (src_offset_t)data, TEXPREF_LINEAR | TEXPREF_NOPICMIP); //johnfitz } //johnfitz -- warn about exceeding old limits if (i >= 64) Con_DWarning ("%i lightmaps exceeds standard limit of 64 (max = %d).\n", i, MAX_LIGHTMAPS); //johnfitz } /* ============================================================= VBO support ============================================================= */ GLuint gl_bmodel_vbo = 0; void GL_DeleteBModelVertexBuffer (void) { if (!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3)) return; GL_DeleteBuffersFunc (1, &gl_bmodel_vbo); gl_bmodel_vbo = 0; GL_ClearBufferBindings (); } /* ================== GL_BuildBModelVertexBuffer Deletes gl_bmodel_vbo if it already exists, then rebuilds it with all surfaces from world + all brush models ================== */ void GL_BuildBModelVertexBuffer (void) { unsigned int numverts, varray_bytes, varray_index; int i, j; qmodel_t *m; float *varray; if (!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3)) return; // ask GL for a name for our VBO GL_DeleteBuffersFunc (1, &gl_bmodel_vbo); GL_GenBuffersFunc (1, &gl_bmodel_vbo); // count all verts in all models numverts = 0; for (j=1 ; j<MAX_MODELS ; j++) { m = cl.model_precache[j]; if (!m || m->name[0] == '*' || m->type != mod_brush) continue; for (i=0 ; i<m->numsurfaces ; i++) { numverts += m->surfaces[i].numedges; } } // build vertex array varray_bytes = VERTEXSIZE * sizeof(float) * numverts; varray = (float *) malloc (varray_bytes); varray_index = 0; for (j=1 ; j<MAX_MODELS ; j++) { m = cl.model_precache[j]; if (!m || m->name[0] == '*' || m->type != mod_brush) continue; for (i=0 ; i<m->numsurfaces ; i++) { msurface_t *s = &m->surfaces[i]; s->vbo_firstvert = varray_index; memcpy (&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges); varray_index += s->numedges; } } // upload to GPU GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo); GL_BufferDataFunc (GL_ARRAY_BUFFER, varray_bytes, varray, GL_STATIC_DRAW); free (varray); // invalidate the cached bindings GL_ClearBufferBindings (); } /* =============== R_AddDynamicLights =============== */ void R_AddDynamicLights (msurface_t *surf) { int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; //johnfitz -- lit support via lordhavoc float cred, cgreen, cblue, brightness; unsigned *bl; //johnfitz smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; tex = surf->texinfo; for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++) { if (! (surf->dlightbits[lnum >> 5] & (1U << (lnum & 31)))) continue; // not lit by this light rad = cl_dlights[lnum].radius; dist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i=0 ; i<3 ; i++) { impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; //johnfitz -- lit support via lordhavoc bl = blocklights; cred = cl_dlights[lnum].color[0] * 256.0f; cgreen = cl_dlights[lnum].color[1] * 256.0f; cblue = cl_dlights[lnum].color[2] * 256.0f; //johnfitz for (t = 0 ; t<tmax ; t++) { td = local[1] - t*16; if (td < 0) td = -td; for (s=0 ; s<smax ; s++) { sd = local[0] - s*16; if (sd < 0) sd = -sd; if (sd > td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight) //johnfitz -- lit support via lordhavoc { brightness = rad - dist; bl[0] += (int) (brightness * cred); bl[1] += (int) (brightness * cgreen); bl[2] += (int) (brightness * cblue); } bl += 3; //johnfitz } } } } /* =============== R_BuildLightMap -- johnfitz -- revised for lit support via lordhavoc Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) { int smax, tmax; int r,g,b; int i, j, size; byte *lightmap; unsigned scale; int maps; unsigned *bl; surf->cached_dlight = (surf->dlightframe == r_framecount); smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; size = smax*tmax; lightmap = surf->samples; if (cl.worldmodel->lightdata) { // clear to no light memset (&blocklights[0], 0, size * 3 * sizeof (unsigned int)); //johnfitz -- lit support via lordhavoc // add all the lightmaps if (lightmap) { for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; surf->cached_light[maps] = scale; // 8.8 fraction //johnfitz -- lit support via lordhavoc bl = blocklights; for (i=0 ; i<size ; i++) { *bl++ += *lightmap++ * scale; *bl++ += *lightmap++ * scale; *bl++ += *lightmap++ * scale; } //johnfitz } } // add all the dynamic lights if (surf->dlightframe == r_framecount) R_AddDynamicLights (surf); } else { // set to full bright if no light data memset (&blocklights[0], 255, size * 3 * sizeof (unsigned int)); //johnfitz -- lit support via lordhavoc } // bound, invert, and shift // store: switch (gl_lightmap_format) { case GL_RGBA: stride -= smax * 4; bl = blocklights; for (i=0 ; i<tmax ; i++, dest += stride) { for (j=0 ; j<smax ; j++) { if (gl_overbright.value) { r = *bl++ >> 8; g = *bl++ >> 8; b = *bl++ >> 8; } else { r = *bl++ >> 7; g = *bl++ >> 7; b = *bl++ >> 7; } *dest++ = (r > 255)? 255 : r; *dest++ = (g > 255)? 255 : g; *dest++ = (b > 255)? 255 : b; *dest++ = 255; } } break; case GL_BGRA: stride -= smax * 4; bl = blocklights; for (i=0 ; i<tmax ; i++, dest += stride) { for (j=0 ; j<smax ; j++) { if (gl_overbright.value) { r = *bl++ >> 8; g = *bl++ >> 8; b = *bl++ >> 8; } else { r = *bl++ >> 7; g = *bl++ >> 7; b = *bl++ >> 7; } *dest++ = (b > 255)? 255 : b; *dest++ = (g > 255)? 255 : g; *dest++ = (r > 255)? 255 : r; *dest++ = 255; } } break; default: Sys_Error ("R_BuildLightMap: bad lightmap format"); } } /* =============== R_UploadLightmap -- johnfitz -- uploads the modified lightmap to opengl if necessary assumes lightmap texture is already bound =============== */ static void R_UploadLightmap(int lmap) { glRect_t *theRect; if (!lightmap_modified[lmap]) return; lightmap_modified[lmap] = false; theRect = &lightmap_rectchange[lmap]; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(lmap* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); theRect->l = BLOCK_WIDTH; theRect->t = BLOCK_HEIGHT; theRect->h = 0; theRect->w = 0; rs_dynamiclightmaps++; } void R_UploadLightmaps (void) { int lmap; for (lmap = 0; lmap < MAX_LIGHTMAPS; lmap++) { if (!lightmap_modified[lmap]) continue; GL_Bind (lightmap_textures[lmap]); R_UploadLightmap(lmap); } } /* ================ R_RebuildAllLightmaps -- johnfitz -- called when gl_overbright gets toggled ================ */ void R_RebuildAllLightmaps (void) { int i, j; qmodel_t *mod; msurface_t *fa; byte *base; if (!cl.worldmodel) // is this the correct test? return; //for each surface in each model, rebuild lightmap with new scale for (i=1; i<MAX_MODELS; i++) { if (!(mod = cl.model_precache[i])) continue; fa = &mod->surfaces[mod->firstmodelsurface]; for (j=0; j<mod->nummodelsurfaces; j++, fa++) { if (fa->flags & SURF_DRAWTILED) continue; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } //for each lightmap, upload it for (i=0; i<MAX_LIGHTMAPS; i++) { if (!allocated[i][0]) break; GL_Bind (lightmap_textures[i]); glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, BLOCK_HEIGHT, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); } } ��������������������������������������������������������quakespasm-0.93.0/Quake/common.h��������������������������������������������������������������������0000644�0000000�0000000�00000025124�13136631402�015165� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _Q_COMMON_H #define _Q_COMMON_H // comndef.h -- general definitions #if defined(_WIN32) #ifdef _MSC_VER # pragma warning(disable:4244) /* 'argument' : conversion from 'type1' to 'type2', possible loss of data */ # pragma warning(disable:4305) /* 'identifier' : truncation from 'type1' to 'type2' */ /* in our case, truncation from 'double' to 'float' */ # pragma warning(disable:4267) /* 'var' : conversion from 'size_t' to 'type', possible loss of data (/Wp64 warning) */ #endif /* _MSC_VER */ #endif /* _WIN32 */ #undef min #undef max #define q_min(a, b) (((a) < (b)) ? (a) : (b)) #define q_max(a, b) (((a) > (b)) ? (a) : (b)) #define CLAMP(_minval, x, _maxval) \ ((x) < (_minval) ? (_minval) : \ (x) > (_maxval) ? (_maxval) : (x)) typedef struct sizebuf_s { qboolean allowoverflow; // if false, do a Sys_Error qboolean overflowed; // set to true if the buffer size failed byte *data; int maxsize; int cursize; } sizebuf_t; void SZ_Alloc (sizebuf_t *buf, int startsize); void SZ_Free (sizebuf_t *buf); void SZ_Clear (sizebuf_t *buf); void *SZ_GetSpace (sizebuf_t *buf, int length); void SZ_Write (sizebuf_t *buf, const void *data, int length); void SZ_Print (sizebuf_t *buf, const char *data); // strcats onto the sizebuf //============================================================================ typedef struct link_s { struct link_s *prev, *next; } link_t; void ClearLink (link_t *l); void RemoveLink (link_t *l); void InsertLinkBefore (link_t *l, link_t *before); void InsertLinkAfter (link_t *l, link_t *after); // (type *)STRUCT_FROM_LINK(link_t *link, type, member) // ent = STRUCT_FROM_LINK(link,entity_t,order) // FIXME: remove this mess! #define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (intptr_t)&(((t *)0)->m))) //============================================================================ extern qboolean host_bigendian; extern short (*BigShort) (short l); extern short (*LittleShort) (short l); extern int (*BigLong) (int l); extern int (*LittleLong) (int l); extern float (*BigFloat) (float l); extern float (*LittleFloat) (float l); //============================================================================ void MSG_WriteChar (sizebuf_t *sb, int c); void MSG_WriteByte (sizebuf_t *sb, int c); void MSG_WriteShort (sizebuf_t *sb, int c); void MSG_WriteLong (sizebuf_t *sb, int c); void MSG_WriteFloat (sizebuf_t *sb, float f); void MSG_WriteString (sizebuf_t *sb, const char *s); void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags); void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags); void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags); //johnfitz extern int msg_readcount; extern qboolean msg_badread; // set if a read goes beyond end of message void MSG_BeginReading (void); int MSG_ReadChar (void); int MSG_ReadByte (void); int MSG_ReadShort (void); int MSG_ReadLong (void); float MSG_ReadFloat (void); const char *MSG_ReadString (void); float MSG_ReadCoord (unsigned int flags); float MSG_ReadAngle (unsigned int flags); float MSG_ReadAngle16 (unsigned int flags); //johnfitz //============================================================================ void Q_memset (void *dest, int fill, size_t count); void Q_memcpy (void *dest, const void *src, size_t count); int Q_memcmp (const void *m1, const void *m2, size_t count); void Q_strcpy (char *dest, const char *src); void Q_strncpy (char *dest, const char *src, int count); int Q_strlen (const char *str); char *Q_strrchr (const char *s, char c); void Q_strcat (char *dest, const char *src); int Q_strcmp (const char *s1, const char *s2); int Q_strncmp (const char *s1, const char *s2, int count); int Q_atoi (const char *str); float Q_atof (const char *str); #include "strl_fn.h" /* locale-insensitive strcasecmp replacement functions: */ extern int q_strcasecmp (const char * s1, const char * s2); extern int q_strncasecmp (const char *s1, const char *s2, size_t n); /* locale-insensitive case-insensitive alternative to strstr */ extern char *q_strcasestr(const char *haystack, const char *needle); /* locale-insensitive strlwr/upr replacement functions: */ extern char *q_strlwr (char *str); extern char *q_strupr (char *str); /* snprintf, vsnprintf : always use our versions. */ extern int q_snprintf (char *str, size_t size, const char *format, ...) FUNC_PRINTF(3,4); extern int q_vsnprintf(char *str, size_t size, const char *format, va_list args) FUNC_PRINTF(3,0); //============================================================================ extern char com_token[1024]; extern qboolean com_eof; const char *COM_Parse (const char *data); extern int com_argc; extern char **com_argv; extern int safemode; /* safe mode: in true, the engine will behave as if one of these arguments were actually on the command line: -nosound, -nocdaudio, -nomidi, -stdvid, -dibonly, -nomouse, -nojoy, -nolan */ int COM_CheckParm (const char *parm); void COM_Init (void); void COM_InitArgv (int argc, char **argv); void COM_InitFilesystem (void); const char *COM_SkipPath (const char *pathname); void COM_StripExtension (const char *in, char *out, size_t outsize); void COM_FileBase (const char *in, char *out, size_t outsize); void COM_AddExtension (char *path, const char *extension, size_t len); #if 0 /* COM_DefaultExtension can be dangerous */ void COM_DefaultExtension (char *path, const char *extension, size_t len); #endif const char *COM_FileGetExtension (const char *in); /* doesn't return NULL */ void COM_ExtractExtension (const char *in, char *out, size_t outsize); void COM_CreatePath (char *path); char *va (const char *format, ...) FUNC_PRINTF(1,2); // does a varargs printf into a temp buffer //============================================================================ // QUAKEFS typedef struct { char name[MAX_QPATH]; int filepos, filelen; } packfile_t; typedef struct pack_s { char filename[MAX_OSPATH]; int handle; int numfiles; packfile_t *files; } pack_t; typedef struct searchpath_s { unsigned int path_id; // identifier assigned to the game directory // Note that <install_dir>/game1 and // <userdir>/game1 have the same id. char filename[MAX_OSPATH]; pack_t *pack; // only one of filename / pack will be used struct searchpath_s *next; } searchpath_t; extern searchpath_t *com_searchpaths; extern searchpath_t *com_base_searchpaths; extern int com_filesize; struct cache_user_s; extern char com_basedir[MAX_OSPATH]; extern char com_gamedir[MAX_OSPATH]; extern int file_from_pak; // global indicating that file came from a pak void COM_WriteFile (const char *filename, const void *data, int len); int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id); int COM_FOpenFile (const char *filename, FILE **file, unsigned int *path_id); qboolean COM_FileExists (const char *filename, unsigned int *path_id); void COM_CloseFile (int h); // these procedures open a file using COM_FindFile and loads it into a proper // buffer. the buffer is allocated with a total size of com_filesize + 1. the // procedures differ by their buffer allocation method. byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize, unsigned int *path_id); // uses the specified stack stack buffer with the specified size // of bufsize. if bufsize is too short, uses temp hunk. the bufsize // must include the +1 byte *COM_LoadTempFile (const char *path, unsigned int *path_id); // allocates the buffer on the temp hunk. byte *COM_LoadHunkFile (const char *path, unsigned int *path_id); // allocates the buffer on the hunk. byte *COM_LoadZoneFile (const char *path, unsigned int *path_id); // allocates the buffer on the zone. void COM_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id); // uses cache mem for allocating the buffer. byte *COM_LoadMallocFile (const char *path, unsigned int *path_id); // allocates the buffer on the system mem (malloc). // Opens the given path directly, ignoring search paths. // Returns NULL on failure, or else a '\0'-terminated malloc'ed buffer. // Loads in "t" mode so CRLF to LF translation is performed on Windows. byte *COM_LoadMallocFile_TextMode_OSPath (const char *path, long *len_out); // Attempts to parse an int, followed by a newline. // Returns advanced buffer position. // Doesn't signal parsing failure, but this is not needed for savegame loading. const char *COM_ParseIntNewline(const char *buffer, int *value); // Attempts to parse a float followed by a newline. // Returns advanced buffer position. const char *COM_ParseFloatNewline(const char *buffer, float *value); // Parse a string of non-whitespace into com_token, then tries to consume a // newline. Returns advanced buffer position. const char *COM_ParseStringNewline(const char *buffer); /* The following FS_*() stdio replacements are necessary if one is * to perform non-sequential reads on files reopened on pak files * because we need the bookkeeping about file start/end positions. * Allocating and filling in the fshandle_t structure is the users' * responsibility when the file is initially opened. */ typedef struct _fshandle_t { FILE *file; qboolean pak; /* is the file read from a pak */ long start; /* file or data start position */ long length; /* file or data size */ long pos; /* current position relative to start */ } fshandle_t; size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh); int FS_fseek(fshandle_t *fh, long offset, int whence); long FS_ftell(fshandle_t *fh); void FS_rewind(fshandle_t *fh); int FS_feof(fshandle_t *fh); int FS_ferror(fshandle_t *fh); int FS_fclose(fshandle_t *fh); int FS_fgetc(fshandle_t *fh); char *FS_fgets(char *s, int size, fshandle_t *fh); long FS_filelength (fshandle_t *fh); extern struct cvar_s registered; extern qboolean standard_quake, rogue, hipnotic; extern qboolean fitzmode; /* if true, run in fitzquake mode disabling custom quakespasm hacks */ #endif /* _Q_COMMON_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_sdl.c�������������������������������������������������������������������0000644�0000000�0000000�00000012457�13025225430�015320� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * snd_sdl.c - SDL audio driver for Hexen II: Hammer of Thyrion (uHexen2) * based on implementations found in the quakeforge and ioquake3 projects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer <sezero@users.sourceforge.net> * Copyright (C) 2010-2014 QuakeSpasm developers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif static int buffersize; static void SDLCALL paint_audio (void *unused, Uint8 *stream, int len) { int pos, tobufend; int len1, len2; if (!shm) { /* shouldn't happen, but just in case */ memset(stream, 0, len); return; } pos = (shm->samplepos * (shm->samplebits / 8)); if (pos >= buffersize) shm->samplepos = pos = 0; tobufend = buffersize - pos; /* bytes to buffer's end. */ len1 = len; len2 = 0; if (len1 > tobufend) { len1 = tobufend; len2 = len - len1; } memcpy(stream, shm->buffer + pos, len1); if (len2 <= 0) { shm->samplepos += (len1 / (shm->samplebits / 8)); } else { /* wraparound? */ memcpy(stream + len1, shm->buffer, len2); shm->samplepos = (len2 / (shm->samplebits / 8)); } if (shm->samplepos >= buffersize) shm->samplepos = 0; } qboolean SNDDMA_Init (dma_t *dma) { SDL_AudioSpec desired, obtained; int tmp, val; char drivername[128]; if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { Con_Printf("Couldn't init SDL audio: %s\n", SDL_GetError()); return false; } /* Set up the desired format */ desired.freq = tmp = snd_mixspeed.value; desired.format = (loadas8bit.value) ? AUDIO_U8 : AUDIO_S16SYS; desired.channels = 2; /* = desired_channels; */ if (desired.freq <= 11025) desired.samples = 256; else if (desired.freq <= 22050) desired.samples = 512; else if (desired.freq <= 44100) desired.samples = 1024; else if (desired.freq <= 56000) desired.samples = 2048; /* for 48 kHz */ else desired.samples = 4096; /* for 96 kHz */ desired.callback = paint_audio; desired.userdata = NULL; /* Open the audio device */ if (SDL_OpenAudio(&desired, &obtained) == -1) { Con_Printf("Couldn't open SDL audio: %s\n", SDL_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } /* Make sure we can support the audio format */ switch (obtained.format) { case AUDIO_S8: /* maybe needed by AHI */ case AUDIO_U8: case AUDIO_S16SYS: /* Supported */ break; default: Con_Printf ("Unsupported audio format received (%u)\n", obtained.format); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; /* Fill the audio DMA information block */ shm->samplebits = (obtained.format & 0xFF); /* first byte of format is bits */ shm->signed8 = (obtained.format == AUDIO_S8); if (obtained.freq != tmp) Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", obtained.freq, tmp); shm->speed = obtained.freq; shm->channels = obtained.channels; tmp = (obtained.samples * obtained.channels) * 10; if (tmp & (tmp - 1)) { /* make it a power of two */ val = 1; while (val < tmp) val <<= 1; tmp = val; } shm->samples = tmp; shm->samplepos = 0; shm->submission_chunk = 1; Con_Printf ("SDL audio spec : %d Hz, %d samples, %d channels\n", obtained.freq, obtained.samples, obtained.channels); #if defined(USE_SDL2) { const char *driver = SDL_GetCurrentAudioDriver(); const char *device = SDL_GetAudioDeviceName(0, SDL_FALSE); q_snprintf(drivername, sizeof(drivername), "%s - %s", driver != NULL ? driver : "(UNKNOWN)", device != NULL ? device : "(UNKNOWN)"); } #else if (SDL_AudioDriverName(drivername, sizeof(drivername)) == NULL) strcpy(drivername, "(UNKNOWN)"); #endif buffersize = shm->samples * (shm->samplebits / 8); Con_Printf ("SDL audio driver: %s, %d bytes buffer\n", drivername, buffersize); shm->buffer = (unsigned char *) calloc (1, buffersize); if (!shm->buffer) { SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); shm = NULL; Con_Printf ("Failed allocating memory for SDL audio\n"); return false; } SDL_PauseAudio(0); return true; } int SNDDMA_GetDMAPos (void) { return shm->samplepos; } void SNDDMA_Shutdown (void) { if (shm) { Con_Printf ("Shutting down SDL sound\n"); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); if (shm->buffer) free (shm->buffer); shm->buffer = NULL; shm = NULL; } } void SNDDMA_LockBuffer (void) { SDL_LockAudio (); } void SNDDMA_Submit (void) { SDL_UnlockAudio(); } void SNDDMA_BlockSound (void) { SDL_PauseAudio(1); } void SNDDMA_UnblockSound (void) { SDL_PauseAudio(0); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_wipx.c������������������������������������������������������������������0000644�0000000�0000000�00000026523�12407762022�015534� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_wipx.c #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include <wsipx.h> #include "quakedef.h" #include "net_defs.h" #include "net_wipx.h" extern cvar_t hostname; static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static struct sockaddr_ipx broadcastaddr; /* externs from net_wins.c: */ extern qboolean winsock_initialized; extern WSADATA winsockdata; extern const char *__WSAE_StrError (int); #define IPXSOCKETS 18 static sys_socket_t ipxsocket[IPXSOCKETS]; static int sequence[IPXSOCKETS]; //============================================================================= sys_socket_t WIPX_Init (void) { int i, err; char *colon; char buff[MAXHOSTNAMELEN]; struct qsockaddr addr; if (COM_CheckParm ("-noipx")) return INVALID_SOCKET; if (winsock_initialized == 0) { err = WSAStartup(MAKEWORD(1,1), &winsockdata); if (err != 0) { Con_SafePrintf("Winsock initialization failed (%s)\n", socketerror(err)); return INVALID_SOCKET; } } winsock_initialized++; for (i = 0; i < IPXSOCKETS; i++) ipxsocket[i] = 0; // determine my name & address if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("WIPX_Init: gethostname failed (%s)\n", socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; } if ((net_controlsocket = WIPX_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("WIPX_Init: Unable to open control socket, IPX disabled\n"); if (--winsock_initialized == 0) WSACleanup (); return INVALID_SOCKET; } broadcastaddr.sa_family = AF_IPX; memset(broadcastaddr.sa_netnum, 0, 4); memset(broadcastaddr.sa_nodenum, 0xff, 6); broadcastaddr.sa_socket = htons((unsigned short)net_hostport); WIPX_GetSocketAddr (net_controlsocket, &addr); Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr)); colon = Q_strrchr (my_ipx_address, ':'); if (colon) *colon = 0; Con_SafePrintf("IPX Initialized\n"); ipxAvailable = true; return net_controlsocket; } //============================================================================= void WIPX_Shutdown (void) { WIPX_Listen (false); WIPX_CloseSocket (net_controlsocket); if (--winsock_initialized == 0) WSACleanup (); } //============================================================================= void WIPX_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("WIPX_Listen: Unable to open accept socket"); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; WIPX_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t WIPX_OpenSocket (int port) { int err; sys_socket_t handle, newsocket; struct sockaddr_ipx address; u_long _true = 1; for (handle = 0; handle < IPXSOCKETS; handle++) { if (ipxsocket[handle] == 0) break; } if (handle == IPXSOCKETS) { Con_SafePrintf("WIPX_OpenSocket: Out of free IPX handles.\n"); return INVALID_SOCKET; } if ((newsocket = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("WIPX_OpenSocket: %s\n", socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) goto ErrorReturn; if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) == SOCKET_ERROR) goto ErrorReturn; address.sa_family = AF_IPX; memset(address.sa_netnum, 0, 4); memset(address.sa_nodenum, 0, 6);; address.sa_socket = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) { ipxsocket[handle] = newsocket; sequence[handle] = 0; return handle; } if (ipxAvailable) { err = SOCKETERRNO; Sys_Error ("IPX bind failed (%s)", socketerror(err)); return INVALID_SOCKET; /* not reached */ } /* else: we are still in init phase, no need to error */ ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("WIPX_OpenSocket: %s\n", socketerror(err)); closesocket (newsocket); return INVALID_SOCKET; } //============================================================================= int WIPX_CloseSocket (sys_socket_t handle) { sys_socket_t socketid = ipxsocket[handle]; int ret; ret = closesocket (socketid); ipxsocket[handle] = 0; return ret; } //============================================================================= int WIPX_Connect (sys_socket_t handle, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t WIPX_CheckNewConnections (void) { u_long available; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (ioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == SOCKET_ERROR) { int err = SOCKETERRNO; Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed (%s)", socketerror(err)); } if (available) return net_acceptsocket; return INVALID_SOCKET; } //============================================================================= static byte netpacketBuffer[NET_DATAGRAMSIZE + 4]; int WIPX_Read (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); sys_socket_t socketid = ipxsocket[handle]; int ret; ret = recvfrom (socketid, (char *)netpacketBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafePrintf ("WIPX_Read, recvfrom: %s\n", socketerror(err)); } if (ret < 4) return 0; // remove sequence number, it's only needed for DOS IPX ret -= 4; memcpy(buf, netpacketBuffer+4, ret); return ret; } //============================================================================= int WIPX_Broadcast (sys_socket_t handle, byte *buf, int len) { return WIPX_Write (handle, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int WIPX_Write (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr) { sys_socket_t socketid = ipxsocket[handle]; int ret; // build packet with sequence number memcpy(&netpacketBuffer[0], &sequence[handle], 4); sequence[handle]++; memcpy(&netpacketBuffer[4], buf, len); len += 4; ret = sendto (socketid, (char *)netpacketBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafePrintf ("WIPX_Write, sendto: %s\n", socketerror(err)); } return ret; } //============================================================================= const char *WIPX_AddrToString (struct qsockaddr *addr) { static char buf[28]; sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u", ((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff, ntohs(((struct sockaddr_ipx *)addr)->sa_socket) ); return buf; } //============================================================================= int WIPX_StringToAddr (const char *string, struct qsockaddr *addr) { int val; char buf[3]; buf[2] = 0; Q_memset(addr, 0, sizeof(struct qsockaddr)); addr->qsa_family = AF_IPX; #define DO(src,dest) do { \ buf[0] = string[src]; \ buf[1] = string[src + 1]; \ if (sscanf (buf, "%x", &val) != 1) \ return -1; \ ((struct sockaddr_ipx *)addr)->dest = val; \ } while (0) DO(0, sa_netnum[0]); DO(2, sa_netnum[1]); DO(4, sa_netnum[2]); DO(6, sa_netnum[3]); DO(9, sa_nodenum[0]); DO(11, sa_nodenum[1]); DO(13, sa_nodenum[2]); DO(15, sa_nodenum[3]); DO(17, sa_nodenum[4]); DO(19, sa_nodenum[5]); #undef DO sscanf (&string[22], "%u", &val); ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val); return 0; } //============================================================================= int WIPX_GetSocketAddr (sys_socket_t handle, struct qsockaddr *addr) { sys_socket_t socketid = ipxsocket[handle]; socklen_t addrlen = sizeof(struct qsockaddr); Q_memset(addr, 0, sizeof(struct qsockaddr)); if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0) { int err = SOCKETERRNO; /* FIXME: what action should be taken?... */ Con_SafePrintf ("WIPX, getsockname: %s\n", socketerror(err)); } return 0; } //============================================================================= int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name) { Q_strcpy(name, WIPX_AddrToString(addr)); return 0; } //============================================================================= int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr) { int n; char buf[32]; n = Q_strlen(name); if (n == 12) { sprintf(buf, "00000000:%s:%u", name, net_hostport); return WIPX_StringToAddr (buf, addr); } if (n == 21) { sprintf(buf, "%s:%u", name, net_hostport); return WIPX_StringToAddr (buf, addr); } if (n > 21 && n <= 27) return WIPX_StringToAddr (name, addr); return -1; } //============================================================================= int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (*((struct sockaddr_ipx *)addr1)->sa_netnum && *((struct sockaddr_ipx *)addr2)->sa_netnum) { if (memcmp(((struct sockaddr_ipx *)addr1)->sa_netnum, ((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0) return -1; } if (memcmp(((struct sockaddr_ipx *)addr1)->sa_nodenum, ((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0) return -1; if (((struct sockaddr_ipx *)addr1)->sa_socket != ((struct sockaddr_ipx *)addr2)->sa_socket) return 1; return 0; } //============================================================================= int WIPX_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_ipx *)addr)->sa_socket); } int WIPX_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port); return 0; } //============================================================================= �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pl_osx.m��������������������������������������������������������������������0000644�0000000�0000000�00000004430�13136721614�015210� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif #import <Cocoa/Cocoa.h> void PL_SetWindowIcon (void) { /* nothing to do on OS X */ } void PL_VID_Shutdown (void) { } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *PL_GetClipboardData (void) { char *data = NULL; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSArray* types = [pasteboard types]; if ([types containsObject: NSStringPboardType]) { NSString* clipboardString = [pasteboard stringForType: NSStringPboardType]; if (clipboardString != NULL && [clipboardString length] > 0) { size_t sz = [clipboardString length] + 1; sz = q_min(MAX_CLIPBOARDTXT, sz); data = (char *) Z_Malloc(sz); #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* for ppc builds targeting 10.3 and older */ q_strlcpy (data, [clipboardString cString], sz); #else q_strlcpy (data, [clipboardString cStringUsingEncoding: NSASCIIStringEncoding], sz); #endif } } return data; } void PL_ErrorDialog(const char *errorMsg) { #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* ppc builds targeting 10.3 and older */ NSString* msg = [NSString stringWithCString:errorMsg]; #else NSString* msg = [NSString stringWithCString:errorMsg encoding:NSASCIIStringEncoding]; #endif NSRunCriticalAlertPanel (@"Quake Error", @"%@", @"OK", nil, nil, msg); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/r_sprite.c������������������������������������������������������������������0000644�0000000�0000000�00000011536�12571117266�015532� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //r_sprite.c -- sprite model rendering #include "quakedef.h" /* ================ R_GetSpriteFrame ================ */ mspriteframe_t *R_GetSpriteFrame (entity_t *currentent) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; psprite = (msprite_t *) currentent->model->cache.data; frame = currentent->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_DPrintf ("R_DrawSprite: no such frame %d for '%s'\n", frame, currentent->model->name); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currentent->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i=0 ; i<(numframes-1) ; i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel -- johnfitz -- rewritten: now supports all orientations ================= */ void R_DrawSpriteModel (entity_t *e) { vec3_t point, v_forward, v_right, v_up; msprite_t *psprite; mspriteframe_t *frame; float *s_up, *s_right; float angle, sr, cr; //TODO: frustum cull it? frame = R_GetSpriteFrame (e); psprite = (msprite_t *) currententity->model->cache.data; switch(psprite->type) { case SPR_VP_PARALLEL_UPRIGHT: //faces view plane, up is towards the heavens v_up[0] = 0; v_up[1] = 0; v_up[2] = 1; s_up = v_up; s_right = vright; break; case SPR_FACING_UPRIGHT: //faces camera origin, up is towards the heavens VectorSubtract(currententity->origin, r_origin, v_forward); v_forward[2] = 0; VectorNormalizeFast(v_forward); v_right[0] = v_forward[1]; v_right[1] = -v_forward[0]; v_right[2] = 0; v_up[0] = 0; v_up[1] = 0; v_up[2] = 1; s_up = v_up; s_right = v_right; break; case SPR_VP_PARALLEL: //faces view plane, up is towards the top of the screen s_up = vup; s_right = vright; break; case SPR_ORIENTED: //pitch yaw roll are independent of camera AngleVectors (currententity->angles, v_forward, v_right, v_up); s_up = v_up; s_right = v_right; break; case SPR_VP_PARALLEL_ORIENTED: //faces view plane, but obeys roll value angle = currententity->angles[ROLL] * M_PI_DIV_180; sr = sin(angle); cr = cos(angle); v_right[0] = vright[0] * cr + vup[0] * sr; v_right[1] = vright[1] * cr + vup[1] * sr; v_right[2] = vright[2] * cr + vup[2] * sr; v_up[0] = vright[0] * -sr + vup[0] * cr; v_up[1] = vright[1] * -sr + vup[1] * cr; v_up[2] = vright[2] * -sr + vup[2] * cr; s_up = v_up; s_right = v_right; break; default: return; } //johnfitz: offset decals if (psprite->type == SPR_ORIENTED) GL_PolygonOffset (OFFSET_DECAL); glColor3f (1,1,1); GL_DisableMultitexture(); GL_Bind(frame->gltexture); glEnable (GL_ALPHA_TEST); glBegin (GL_TRIANGLE_FAN); //was GL_QUADS, but changed to support r_showtris glTexCoord2f (0, frame->tmax); VectorMA (e->origin, frame->down, s_up, point); VectorMA (point, frame->left, s_right, point); glVertex3fv (point); glTexCoord2f (0, 0); VectorMA (e->origin, frame->up, s_up, point); VectorMA (point, frame->left, s_right, point); glVertex3fv (point); glTexCoord2f (frame->smax, 0); VectorMA (e->origin, frame->up, s_up, point); VectorMA (point, frame->right, s_right, point); glVertex3fv (point); glTexCoord2f (frame->smax, frame->tmax); VectorMA (e->origin, frame->down, s_up, point); VectorMA (point, frame->right, s_right, point); glVertex3fv (point); glEnd (); glDisable (GL_ALPHA_TEST); //johnfitz: offset decals if (psprite->type == SPR_ORIENTED) GL_PolygonOffset (OFFSET_NONE); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/chase.c���������������������������������������������������������������������0000644�0000000�0000000�00000006130�12407762022�014752� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // chase.c -- chase camera code #include "quakedef.h" cvar_t chase_back = {"chase_back", "100", CVAR_NONE}; cvar_t chase_up = {"chase_up", "16", CVAR_NONE}; cvar_t chase_right = {"chase_right", "0", CVAR_NONE}; cvar_t chase_active = {"chase_active", "0", CVAR_NONE}; /* ============== Chase_Init ============== */ void Chase_Init (void) { Cvar_RegisterVariable (&chase_back); Cvar_RegisterVariable (&chase_up); Cvar_RegisterVariable (&chase_right); Cvar_RegisterVariable (&chase_active); } /* ============== TraceLine TODO: impact on bmodels, monsters ============== */ void TraceLine (vec3_t start, vec3_t end, vec3_t impact) { trace_t trace; memset (&trace, 0, sizeof(trace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); VectorCopy (trace.endpos, impact); } /* ============== Chase_UpdateForClient -- johnfitz -- orient client based on camera. called after input ============== */ void Chase_UpdateForClient (void) { //place camera //assign client angles to camera //see where camera points //adjust client angles to point at the same place } /* ============== Chase_UpdateForDrawing -- johnfitz -- orient camera based on client. called before drawing TODO: stay at least 8 units away from all walls in this leaf ============== */ void Chase_UpdateForDrawing (void) { int i; vec3_t forward, up, right; vec3_t ideal, crosshair, temp; AngleVectors (cl.viewangles, forward, right, up); // calc ideal camera location before checking for walls for (i=0 ; i<3 ; i++) ideal[i] = cl.viewent.origin[i] - forward[i]*chase_back.value + right[i]*chase_right.value; //+ up[i]*chase_up.value; ideal[2] = cl.viewent.origin[2] + chase_up.value; // make sure camera is not in or behind a wall TraceLine(r_refdef.vieworg, ideal, temp); if (VectorLength(temp) != 0) VectorCopy(temp, ideal); // place camera VectorCopy (ideal, r_refdef.vieworg); // find the spot the player is looking at VectorMA (cl.viewent.origin, 4096, forward, temp); TraceLine (cl.viewent.origin, temp, crosshair); // calculate camera angles to look at the same spot VectorSubtract (crosshair, r_refdef.vieworg, temp); VectorAngles (temp, r_refdef.viewangles); if (r_refdef.viewangles[PITCH] == 90 || r_refdef.viewangles[PITCH] == -90) r_refdef.viewangles[YAW] = cl.viewangles[YAW]; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/qs_bmp.h��������������������������������������������������������������������0000644�0000000�0000000�00000043243�12030217765�015164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0x42, 0x4d, 0xc6, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x00, 0x05, 0x06, 0x07, 0x00, 0x06, 0x06, 0x07, 0x00, 0x07, 0x06, 0x07, 0x00, 0x06, 0x07, 0x06, 0x00, 0x06, 0x07, 0x07, 0x00, 0x07, 0x07, 0x07, 0x00, 0x07, 0x08, 0x07, 0x00, 0x08, 0x08, 0x08, 0x00, 0x07, 0x08, 0x09, 0x00, 0x08, 0x08, 0x09, 0x00, 0x07, 0x09, 0x08, 0x00, 0x08, 0x09, 0x08, 0x00, 0x09, 0x09, 0x08, 0x00, 0x08, 0x09, 0x09, 0x00, 0x09, 0x09, 0x09, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x09, 0x09, 0x0a, 0x00, 0x09, 0x09, 0x0b, 0x00, 0x09, 0x0a, 0x09, 0x00, 0x08, 0x0a, 0x0a, 0x00, 0x09, 0x0a, 0x0a, 0x00, 0x0a, 0x0a, 0x0a, 0x00, 0x09, 0x0a, 0x0b, 0x00, 0x0a, 0x0a, 0x0b, 0x00, 0x09, 0x0a, 0x0c, 0x00, 0x09, 0x0b, 0x0b, 0x00, 0x0a, 0x0b, 0x0b, 0x00, 0x0a, 0x0b, 0x0c, 0x00, 0x0b, 0x0b, 0x0c, 0x00, 0x0a, 0x0b, 0x0d, 0x00, 0x0b, 0x0c, 0x0c, 0x00, 0x0b, 0x0c, 0x0d, 0x00, 0x0a, 0x0c, 0x0e, 0x00, 0x0b, 0x0c, 0x0e, 0x00, 0x0c, 0x0c, 0x0f, 0x00, 0x0b, 0x0d, 0x0d, 0x00, 0x0b, 0x0d, 0x0e, 0x00, 0x0c, 0x0d, 0x0e, 0x00, 0x0c, 0x0d, 0x0f, 0x00, 0x0d, 0x0d, 0x0f, 0x00, 0x0b, 0x0d, 0x10, 0x00, 0x0c, 0x0d, 0x10, 0x00, 0x0c, 0x0e, 0x0f, 0x00, 0x0d, 0x0e, 0x0f, 0x00, 0x0c, 0x0e, 0x10, 0x00, 0x0d, 0x0e, 0x10, 0x00, 0x0b, 0x0e, 0x11, 0x00, 0x0c, 0x0e, 0x11, 0x00, 0x0d, 0x0e, 0x11, 0x00, 0x0d, 0x0f, 0x10, 0x00, 0x0d, 0x0f, 0x11, 0x00, 0x0e, 0x0f, 0x11, 0x00, 0x0d, 0x0f, 0x12, 0x00, 0x0e, 0x0f, 0x12, 0x00, 0x0d, 0x0f, 0x13, 0x00, 0x0e, 0x10, 0x11, 0x00, 0x0d, 0x10, 0x12, 0x00, 0x0b, 0x10, 0x13, 0x00, 0x0e, 0x10, 0x12, 0x00, 0x0e, 0x10, 0x13, 0x00, 0x0d, 0x10, 0x14, 0x00, 0x0e, 0x10, 0x14, 0x00, 0x0d, 0x11, 0x13, 0x00, 0x0e, 0x11, 0x13, 0x00, 0x0f, 0x11, 0x13, 0x00, 0x0e, 0x11, 0x14, 0x00, 0x0f, 0x11, 0x14, 0x00, 0x0f, 0x11, 0x15, 0x00, 0x0f, 0x12, 0x14, 0x00, 0x0f, 0x12, 0x15, 0x00, 0x10, 0x12, 0x15, 0x00, 0x0f, 0x12, 0x16, 0x00, 0x10, 0x12, 0x16, 0x00, 0x0f, 0x12, 0x17, 0x00, 0x0f, 0x13, 0x16, 0x00, 0x10, 0x13, 0x16, 0x00, 0x10, 0x13, 0x17, 0x00, 0x11, 0x13, 0x17, 0x00, 0x11, 0x13, 0x18, 0x00, 0x10, 0x14, 0x17, 0x00, 0x11, 0x14, 0x17, 0x00, 0x10, 0x14, 0x18, 0x00, 0x12, 0x15, 0x18, 0x00, 0x10, 0x15, 0x19, 0x00, 0x12, 0x15, 0x19, 0x00, 0x10, 0x15, 0x1a, 0x00, 0x11, 0x15, 0x1a, 0x00, 0x12, 0x15, 0x1a, 0x00, 0x12, 0x15, 0x1b, 0x00, 0x11, 0x16, 0x1a, 0x00, 0x12, 0x16, 0x1a, 0x00, 0x13, 0x16, 0x1a, 0x00, 0x12, 0x16, 0x1b, 0x00, 0x13, 0x16, 0x1b, 0x00, 0x12, 0x17, 0x1b, 0x00, 0x13, 0x17, 0x1b, 0x00, 0x13, 0x17, 0x1c, 0x00, 0x14, 0x17, 0x1c, 0x00, 0x14, 0x17, 0x1d, 0x00, 0x13, 0x18, 0x1d, 0x00, 0x14, 0x18, 0x1d, 0x00, 0x15, 0x18, 0x1f, 0x00, 0x14, 0x19, 0x1d, 0x00, 0x15, 0x19, 0x1d, 0x00, 0x15, 0x19, 0x1e, 0x00, 0x15, 0x19, 0x1f, 0x00, 0x16, 0x19, 0x1f, 0x00, 0x15, 0x19, 0x20, 0x00, 0x16, 0x1a, 0x1e, 0x00, 0x14, 0x1a, 0x1f, 0x00, 0x15, 0x1a, 0x1f, 0x00, 0x16, 0x1a, 0x1f, 0x00, 0x15, 0x1a, 0x20, 0x00, 0x16, 0x1a, 0x21, 0x00, 0x15, 0x1b, 0x21, 0x00, 0x17, 0x1b, 0x22, 0x00, 0x17, 0x1b, 0x23, 0x00, 0x16, 0x1c, 0x22, 0x00, 0x17, 0x1d, 0x22, 0x00, 0x17, 0x1d, 0x23, 0x00, 0x18, 0x1d, 0x23, 0x00, 0x19, 0x1d, 0x23, 0x00, 0x17, 0x1d, 0x24, 0x00, 0x18, 0x1e, 0x23, 0x00, 0x18, 0x1e, 0x24, 0x00, 0x18, 0x1e, 0x25, 0x00, 0x19, 0x1e, 0x25, 0x00, 0x18, 0x1e, 0x26, 0x00, 0x19, 0x1f, 0x26, 0x00, 0x1a, 0x1f, 0x26, 0x00, 0x1a, 0x1f, 0x27, 0x00, 0x1a, 0x1f, 0x28, 0x00, 0x1a, 0x20, 0x26, 0x00, 0x19, 0x20, 0x27, 0x00, 0x1a, 0x20, 0x27, 0x00, 0x1a, 0x20, 0x28, 0x00, 0x1b, 0x20, 0x29, 0x00, 0x1b, 0x21, 0x28, 0x00, 0x1b, 0x21, 0x29, 0x00, 0x1c, 0x21, 0x2a, 0x00, 0x1d, 0x22, 0x29, 0x00, 0x1c, 0x22, 0x2a, 0x00, 0x1d, 0x22, 0x2a, 0x00, 0x1e, 0x23, 0x2c, 0x00, 0x1d, 0x24, 0x2b, 0x00, 0x1d, 0x24, 0x2c, 0x00, 0x1c, 0x24, 0x2d, 0x00, 0x1e, 0x25, 0x2d, 0x00, 0x1f, 0x25, 0x2e, 0x00, 0x1e, 0x26, 0x2e, 0x00, 0x1f, 0x26, 0x2e, 0x00, 0x1f, 0x26, 0x30, 0x00, 0x21, 0x27, 0x2e, 0x00, 0x20, 0x27, 0x2f, 0x00, 0x20, 0x27, 0x31, 0x00, 0x20, 0x29, 0x32, 0x00, 0x20, 0x29, 0x33, 0x00, 0x21, 0x2a, 0x33, 0x00, 0x24, 0x2c, 0x35, 0x00, 0x24, 0x2c, 0x38, 0x00, 0x26, 0x31, 0x3b, 0x00, 0x27, 0x31, 0x3b, 0x00, 0xff, 0x00, 0xff, 0x00, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x05, 0x1a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x48, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4a, 0x54, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a, 0x8c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x41, 0x5e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x0d, 0x27, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1b, 0x1f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x00, 0x32, 0x69, 0x29, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x21, 0x38, 0x63, 0x2b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3d, 0x2b, 0x1c, 0x0e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5d, 0x7e, 0x2b, 0x08, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5d, 0x80, 0x4d, 0x4c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x24, 0x4a, 0x9c, 0xa1, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x10, 0x39, 0x76, 0x46, 0x6d, 0x9a, 0x60, 0x3f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x01, 0x78, 0x8e, 0x32, 0x37, 0x81, 0x6c, 0x2a, 0x3c, 0x55, 0x2c, 0x1d, 0x2d, 0x17, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x06, 0x27, 0x55, 0x82, 0x9b, 0x45, 0x0e, 0x47, 0x61, 0x0f, 0x1a, 0x0f, 0x20, 0x62, 0x28, 0x0f, 0x18, 0x08, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x35, 0x1b, 0x40, 0x98, 0x4d, 0x18, 0x15, 0x1c, 0x26, 0x1b, 0x3b, 0x49, 0x20, 0x46, 0x67, 0x1b, 0x13, 0x0c, 0x15, 0x20, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x4d, 0x3c, 0x34, 0x2b, 0x1c, 0x08, 0x0b, 0x0f, 0x1b, 0x32, 0x9e, 0x8a, 0x08, 0x27, 0x22, 0x0f, 0x36, 0x6f, 0x75, 0x3b, 0x39, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x7d, 0x7a, 0x56, 0x48, 0x68, 0x32, 0x06, 0xa3, 0xa3, 0xa3, 0x14, 0x40, 0x86, 0x6e, 0xa3, 0xa3, 0xa3, 0x06, 0x2d, 0x85, 0x92, 0x5b, 0x32, 0x6a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1c, 0x58, 0x8a, 0x65, 0x18, 0x21, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x07, 0x43, 0x83, 0x43, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x73, 0x91, 0x7f, 0x62, 0x20, 0x15, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1e, 0x31, 0x0f, 0x2e, 0x1c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1b, 0x1b, 0x32, 0x16, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x43, 0x3b, 0x22, 0x55, 0x47, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x72, 0x9d, 0x60, 0x33, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2d, 0x0f, 0x1b, 0x0f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x11, 0x2d, 0x93, 0x55, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x50, 0x44, 0x90, 0x8a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3a, 0x77, 0x88, 0x47, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x45, 0x25, 0x25, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a, 0x51, 0x23, 0x43, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x57, 0x5a, 0xa0, 0x84, 0x87, 0x74, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x02, 0x08, 0x8f, 0x8b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x96, 0x6a, 0x41, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4e, 0x95, 0x68, 0x5a, 0x4b, 0x7b, 0x70, 0x4c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x04, 0x53, 0x8d, 0x79, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x79, 0x80, 0x5b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x0e, 0x7c, 0x94, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4f, 0x6a, 0x47, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5f, 0x40, 0x1b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x46, 0x2d, 0x08, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x97, 0x82, 0x18, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x58, 0x43, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x69, 0x5c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x71, 0x40, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x65, 0x60, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x52, 0x2d, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x66, 0x5e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2d, 0x20, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x6f, 0x6b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2b, 0x5b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2e, 0x44, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x41, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x40, 0x1e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x10, 0x38, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x69, 0x42, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3b, 0x19, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x03, 0x20, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x53, 0x1d, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4f, 0x15, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1d, 0x09, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x48, 0x89, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1e, 0x33, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5b, 0x39, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3b, 0x2a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x59, 0x9f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x33, 0x0a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2f, 0x64, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1d, 0x2b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x12, 0x99, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x49, 0x3e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1c, 0x36, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x30, 0x5e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a, 0x48, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_rlight.c�����������������������������������������������������������������0000644�0000000�0000000�00000024477�12653757413�015673� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_light.c #include "quakedef.h" int r_dlightframecount; extern cvar_t r_flatlightstyles; //johnfitz /* ================== R_AnimateLight ================== */ void R_AnimateLight (void) { int i,j,k; // // light animations // 'm' is normal light, 'a' is no light, 'z' is double bright i = (int)(cl.time*10); for (j=0 ; j<MAX_LIGHTSTYLES ; j++) { if (!cl_lightstyle[j].length) { d_lightstylevalue[j] = 256; continue; } //johnfitz -- r_flatlightstyles if (r_flatlightstyles.value == 2) k = cl_lightstyle[j].peak - 'a'; else if (r_flatlightstyles.value == 1) k = cl_lightstyle[j].average - 'a'; else { k = i % cl_lightstyle[j].length; k = cl_lightstyle[j].map[k] - 'a'; } d_lightstylevalue[j] = k*22; //johnfitz } } /* ============================================================================= DYNAMIC LIGHTS BLEND RENDERING (gl_flashblend 1) ============================================================================= */ void AddLightBlend (float r, float g, float b, float a2) { float a; v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]); a2 = a2/a; v_blend[0] = v_blend[1]*(1-a2) + r*a2; v_blend[1] = v_blend[1]*(1-a2) + g*a2; v_blend[2] = v_blend[2]*(1-a2) + b*a2; } void R_RenderDlight (dlight_t *light) { int i, j; float a; vec3_t v; float rad; rad = light->radius * 0.35; VectorSubtract (light->origin, r_origin, v); if (VectorLength (v) < rad) { // view is inside the dlight AddLightBlend (1, 0.5, 0, light->radius * 0.0003); return; } glBegin (GL_TRIANGLE_FAN); glColor3f (0.2,0.1,0.0); for (i=0 ; i<3 ; i++) v[i] = light->origin[i] - vpn[i]*rad; glVertex3fv (v); glColor3f (0,0,0); for (i=16 ; i>=0 ; i--) { a = i/16.0 * M_PI*2; for (j=0 ; j<3 ; j++) v[j] = light->origin[j] + vright[j]*cos(a)*rad + vup[j]*sin(a)*rad; glVertex3fv (v); } glEnd (); } /* ============= R_RenderDlights ============= */ void R_RenderDlights (void) { int i; dlight_t *l; if (!gl_flashblend.value) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame glDepthMask (0); glDisable (GL_TEXTURE_2D); glShadeModel (GL_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); l = cl_dlights; for (i=0 ; i<MAX_DLIGHTS ; i++, l++) { if (l->die < cl.time || !l->radius) continue; R_RenderDlight (l); } glColor3f (1,1,1); glDisable (GL_BLEND); glEnable (GL_TEXTURE_2D); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask (1); } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ /* ============= R_MarkLights -- johnfitz -- rewritten to use LordHavoc's lighting speedup ============= */ void R_MarkLights (dlight_t *light, int num, mnode_t *node) { mplane_t *splitplane; msurface_t *surf; vec3_t impact; float dist, l, maxdist; int i, j, s, t; start: if (node->contents < 0) return; splitplane = node->plane; if (splitplane->type < 3) dist = light->origin[splitplane->type] - splitplane->dist; else dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; if (dist > light->radius) { node = node->children[0]; goto start; } if (dist < -light->radius) { node = node->children[1]; goto start; } maxdist = light->radius*light->radius; // mark the polygons surf = cl.worldmodel->surfaces + node->firstsurface; for (i=0 ; i<node->numsurfaces ; i++, surf++) { for (j=0 ; j<3 ; j++) impact[j] = light->origin[j] - surf->plane->normal[j]*dist; // clamp center of light to corner and check brightness l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0]; s = l - s; l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1]; t = l - t; // compare to minimum light if ((s*s+t*t+dist*dist) < maxdist) { if (surf->dlightframe != r_dlightframecount) // not dynamic until now { surf->dlightbits[num >> 5] = 1U << (num & 31); surf->dlightframe = r_dlightframecount; } else // already dynamic surf->dlightbits[num >> 5] |= 1U << (num & 31); } } if (node->children[0]->contents >= 0) R_MarkLights (light, num, node->children[0]); if (node->children[1]->contents >= 0) R_MarkLights (light, num, node->children[1]); } /* ============= R_PushDlights ============= */ void R_PushDlights (void) { int i; dlight_t *l; if (gl_flashblend.value) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame l = cl_dlights; for (i=0 ; i<MAX_DLIGHTS ; i++, l++) { if (l->die < cl.time || !l->radius) continue; R_MarkLights (l, i, cl.worldmodel->nodes); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ mplane_t *lightplane; vec3_t lightspot; vec3_t lightcolor; //johnfitz -- lit support via lordhavoc /* ============= RecursiveLightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc ============= */ int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end) { float front, back, frac; vec3_t mid; loc0: if (node->contents < 0) return false; // didn't hit anything // calculate mid point if (node->plane->type < 3) { front = start[node->plane->type] - node->plane->dist; back = end[node->plane->type] - node->plane->dist; } else { front = DotProduct(start, node->plane->normal) - node->plane->dist; back = DotProduct(end, node->plane->normal) - node->plane->dist; } // LordHavoc: optimized recursion if ((back < 0) == (front < 0)) // return RecursiveLightPoint (color, node->children[front < 0], start, end); { node = node->children[front < 0]; goto loc0; } frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side if (RecursiveLightPoint (color, node->children[front < 0], start, mid)) return true; // hit something else { int i, ds, dt; msurface_t *surf; // check for impact on this node VectorCopy (mid, lightspot); lightplane = node->plane; surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0;i < node->numsurfaces;i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps // ericw -- added double casts to force 64-bit precision. // Without them the zombie at the start of jam3_ericw.bsp was // incorrectly being lit up in SSE builds. ds = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]); dt = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]); if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) continue; ds -= surf->texturemins[0]; dt -= surf->texturemins[1]; if (ds > surf->extents[0] || dt > surf->extents[1]) continue; if (surf->samples) { // LordHavoc: enhanced to interpolate lighting byte *lightmap; int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; float scale; line3 = ((surf->extents[0]>>4)+1)*3; lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++) { scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0; r00 += (float) lightmap[ 0] * scale;g00 += (float) lightmap[ 1] * scale;b00 += (float) lightmap[2] * scale; r01 += (float) lightmap[ 3] * scale;g01 += (float) lightmap[ 4] * scale;b01 += (float) lightmap[5] * scale; r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale; r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale; lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting } color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00))); color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00))); color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00))); } return true; // success } // go down back side return RecursiveLightPoint (color, node->children[front >= 0], mid, end); } } /* ============= R_LightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc ============= */ int R_LightPoint (vec3_t p) { vec3_t end; if (!cl.worldmodel->lightdata) { lightcolor[0] = lightcolor[1] = lightcolor[2] = 255; return 255; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 8192; //johnfitz -- was 2048 lightcolor[0] = lightcolor[1] = lightcolor[2] = 0; RecursiveLightPoint (lightcolor, cl.worldmodel->nodes, p, end); return ((lightcolor[0] + lightcolor[1] + lightcolor[2]) * (1.0f / 3.0f)); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_umx.c�������������������������������������������������������������������0000644�0000000�0000000�00000024430�12276500543�015351� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Unreal UMX container support. * UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3 * by Andy Ward <wardwh@swbell.net>, with additional updates * by O. Sezer - see git repo at https://github.com/sezero/umr/ * * The cheaper way, i.e. linear search of music object like libxmp * and libmodplug does, is possible. With this however we're using * the embedded offset, size and object type directly from the umx * file, and I feel safer with it. * * Copyright (C) 2013 O. Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_UMX) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_umx.h" typedef int32_t fci_t; /* FCompactIndex */ #define UPKG_HDR_TAG 0x9e2a83c1 struct _genhist { /* for upkg versions >= 68 */ int32_t export_count; int32_t name_count; }; struct upkg_hdr { uint32_t tag; /* UPKG_HDR_TAG */ int32_t file_version; uint32_t pkg_flags; int32_t name_count; /* number of names in name table (>= 0) */ int32_t name_offset; /* offset to name table (>= 0) */ int32_t export_count; /* num. exports in export table (>= 0) */ int32_t export_offset; /* offset to export table (>= 0) */ int32_t import_count; /* num. imports in export table (>= 0) */ int32_t import_offset; /* offset to import table (>= 0) */ /* number of GUIDs in heritage table (>= 1) and table's offset: * only with versions < 68. */ int32_t heritage_count; int32_t heritage_offset; /* with versions >= 68: a GUID, a dword for generation count * and export_count and name_count dwords for each generation: */ uint32_t guid[4]; int32_t generation_count; #define UPKG_HDR_SIZE 64 /* 64 bytes up until here */ /*struct _genhist *gen;*/ }; /*COMPILE_TIME_ASSERT(upkg_hdr, offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE);*/ COMPILE_TIME_ASSERT(upkg_hdr, sizeof(struct upkg_hdr) == UPKG_HDR_SIZE); #define UMUSIC_IT 0 #define UMUSIC_S3M 1 #define UMUSIC_XM 2 #define UMUSIC_MOD 3 #define UMUSIC_WAV 4 #define UMUSIC_MP2 5 static const char *mustype[] = { "IT", "S3M", "XM", "MOD", "WAV", "MP2", NULL }; /* decode an FCompactIndex. * original documentation by Tim Sweeney was at * http://unreal.epicgames.com/Packages.htm * also see Unreal Wiki: * http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details */ static fci_t get_fci (const char *in, int *pos) { int32_t a; int size; size = 1; a = in[0] & 0x3f; if (in[0] & 0x40) { size++; a |= (in[1] & 0x7f) << 6; if (in[1] & 0x80) { size++; a |= (in[2] & 0x7f) << 13; if (in[2] & 0x80) { size++; a |= (in[3] & 0x7f) << 20; if (in[3] & 0x80) { size++; a |= (in[4] & 0x3f) << 27; } } } } if (in[0] & 0x80) a = -a; *pos += size; return a; } static int get_objtype (fshandle_t *f, int32_t ofs, int type) { char sig[16]; _retry: FS_fseek(f, ofs, SEEK_SET); FS_fread(sig, 16, 1, f); if (type == UMUSIC_IT) { if (memcmp(sig, "IMPM", 4) == 0) return UMUSIC_IT; return -1; } if (type == UMUSIC_XM) { if (memcmp(sig, "Extended Module:", 16) != 0) return -1; FS_fread(sig, 16, 1, f); if (sig[0] != ' ') return -1; FS_fread(sig, 16, 1, f); if (sig[5] != 0x1a) return -1; return UMUSIC_XM; } if (type == UMUSIC_MP2) { unsigned char *p = (unsigned char *)sig; uint16_t u = ((p[0] << 8) | p[1]) & 0xFFFE; if (u == 0xFFFC || u == 0xFFF4) return UMUSIC_MP2; return -1; } if (type == UMUSIC_WAV) { if (memcmp(sig, "RIFF", 4) == 0 && memcmp(&sig[8], "WAVE", 4) == 0) return UMUSIC_WAV; return -1; } FS_fseek(f, ofs + 44, SEEK_SET); FS_fread(sig, 4, 1, f); if (type == UMUSIC_S3M) { if (memcmp(sig, "SCRM", 4) == 0) return UMUSIC_S3M; /*return -1;*/ /* SpaceMarines.umx and Starseek.umx from Return to NaPali * report as "s3m" whereas the actual music format is "it" */ type = UMUSIC_IT; goto _retry; } FS_fseek(f, ofs + 1080, SEEK_SET); FS_fread(sig, 4, 1, f); if (type == UMUSIC_MOD) { if (memcmp(sig, "M.K.", 4) == 0 || memcmp(sig, "M!K!", 4) == 0) return UMUSIC_MOD; return -1; } return -1; } static int read_export (fshandle_t *f, const struct upkg_hdr *hdr, int32_t *ofs, int32_t *objsize) { char buf[40]; int idx = 0, t; FS_fseek(f, *ofs, SEEK_SET); if (FS_fread(buf, 4, 10, f) < 10) return -1; if (hdr->file_version < 40) idx += 8; /* 00 00 00 00 00 00 00 00 */ if (hdr->file_version < 60) idx += 16; /* 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 */ get_fci(&buf[idx], &idx); /* skip junk */ t = get_fci(&buf[idx], &idx); /* type_name */ if (hdr->file_version > 61) idx += 4; /* skip export size */ *objsize = get_fci(&buf[idx], &idx); *ofs += idx; /* offset for real data */ return t; /* return type_name index */ } static int read_typname(fshandle_t *f, const struct upkg_hdr *hdr, int idx, char *out) { int i, s; long l; char buf[64]; if (idx >= hdr->name_count) return -1; buf[63] = '\0'; for (i = 0, l = 0; i <= idx; i++) { FS_fseek(f, hdr->name_offset + l, SEEK_SET); FS_fread(buf, 1, 63, f); if (hdr->file_version >= 64) { s = *(signed char *)buf; /* numchars *including* terminator */ if (s <= 0 || s > 64) return -1; l += s + 5; /* 1 for buf[0], 4 for int32_t name_flags */ } else { l += (long)strlen(buf); l += 5; /* 1 for terminator, 4 for int32_t name_flags */ } } strcpy(out, (hdr->file_version >= 64)? &buf[1] : buf); return 0; } static int probe_umx (fshandle_t *f, const struct upkg_hdr *hdr, int32_t *ofs, int32_t *objsize) { int i, idx, t; int32_t s, pos; long fsiz; char buf[64]; idx = 0; fsiz = FS_filelength (f); /* Find the offset and size of the first IT, S3M or XM * by parsing the exports table. The umx files should * have only one export. Kran32.umx from Unreal has two, * but both pointing to the same music. */ if (hdr->export_offset >= fsiz) return -1; memset(buf, 0, 64); FS_fseek(f, hdr->export_offset, SEEK_SET); FS_fread(buf, 1, 64, f); get_fci(&buf[idx], &idx); /* skip class_index */ get_fci(&buf[idx], &idx); /* skip super_index */ if (hdr->file_version >= 60) idx += 4; /* skip int32 package_index */ get_fci(&buf[idx], &idx); /* skip object_name */ idx += 4; /* skip int32 object_flags */ s = get_fci(&buf[idx], &idx); /* get serial_size */ if (s <= 0) return -1; pos = get_fci(&buf[idx],&idx); /* get serial_offset */ if (pos < 0 || pos > fsiz - 40) return -1; if ((t = read_export(f, hdr, &pos, &s)) < 0) return -1; if (s <= 0 || s > fsiz - pos) return -1; if (read_typname(f, hdr, t, buf) < 0) return -1; for (i = 0; mustype[i] != NULL; i++) { if (!q_strcasecmp(buf, mustype[i])) { t = i; break; } } if (mustype[i] == NULL) return -1; if ((t = get_objtype(f, pos, t)) < 0) return -1; *ofs = pos; *objsize = s; return t; } static int32_t probe_header (void *header) { struct upkg_hdr *hdr; unsigned char *p; uint32_t *swp; int i; /* byte swap the header - all members are 32 bit LE values */ p = (unsigned char *) header; swp = (uint32_t *) header; for (i = 0; i < UPKG_HDR_SIZE/4; i++, p += 4) { swp[i] = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); } hdr = (struct upkg_hdr *) header; if (hdr->tag != UPKG_HDR_TAG) { Con_DPrintf("Unknown header tag 0x%x\n", hdr->tag); return -1; } if (hdr->name_count < 0 || hdr->name_offset < 0 || hdr->export_count < 0 || hdr->export_offset < 0 || hdr->import_count < 0 || hdr->import_offset < 0 ) { Con_DPrintf("Negative values in header\n"); return -1; } switch (hdr->file_version) { case 35: case 37: /* Unreal beta - */ case 40: case 41: /* 1998 */ case 61:/* Unreal */ case 62:/* Unreal Tournament */ case 63:/* Return to NaPali */ case 64:/* Unreal Tournament */ case 66:/* Unreal Tournament */ case 68:/* Unreal Tournament */ case 69:/* Tactical Ops */ case 75:/* Harry Potter and the Philosopher's Stone */ case 76: /* mpeg layer II data */ case 83:/* Mobile Forces */ return 0; } Con_DPrintf("Unknown upkg version %d\n", hdr->file_version); return -1; } static int process_upkg (fshandle_t *f, int32_t *ofs, int32_t *objsize) { char header[UPKG_HDR_SIZE]; if (FS_fread(header, 1, UPKG_HDR_SIZE, f) < UPKG_HDR_SIZE) return -1; if (probe_header(header) < 0) return -1; return probe_umx(f, (struct upkg_hdr *)header, ofs, objsize); } static qboolean S_UMX_CodecInitialize (void) { return true; } static void S_UMX_CodecShutdown (void) { } static qboolean S_UMX_CodecOpenStream (snd_stream_t *stream) { int type; int32_t ofs = 0, size = 0; type = process_upkg(&stream->fh, &ofs, &size); if (type < 0) { Con_DPrintf("%s: unrecognized umx\n", stream->name); return false; } Con_DPrintf("%s: %s data @ 0x%x, %d bytes\n", stream->name, mustype[type], ofs, size); /* hack the fshandle_t start pos and length members so * that only the relevant data is accessed from now on */ stream->fh.start += ofs; stream->fh.length = size; FS_fseek(&stream->fh, 0, SEEK_SET); switch (type) { case UMUSIC_IT: case UMUSIC_S3M: case UMUSIC_XM: case UMUSIC_MOD: return S_CodecForwardStream(stream, CODECTYPE_MOD); case UMUSIC_WAV: return S_CodecForwardStream(stream, CODECTYPE_WAV); case UMUSIC_MP2: return S_CodecForwardStream(stream, CODECTYPE_MP3); } return false; } static int S_UMX_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { return -1; } static void S_UMX_CodecCloseStream (snd_stream_t *stream) { S_CodecUtilClose(&stream); } static int S_UMX_CodecRewindStream (snd_stream_t *stream) { return -1; } snd_codec_t umx_codec = { CODECTYPE_UMX, true, /* always available. */ "umx", S_UMX_CodecInitialize, S_UMX_CodecShutdown, S_UMX_CodecOpenStream, S_UMX_CodecReadStream, S_UMX_CodecRewindStream, S_UMX_CodecCloseStream, NULL }; #endif /* USE_CODEC_UMX */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sys_sdl_win.c���������������������������������������������������������������0000644�0000000�0000000�00000022067�13141155354�016233� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <mmsystem.h> #include "quakedef.h" #include <sys/types.h> #include <errno.h> #include <io.h> #include <direct.h> #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif qboolean isDedicated; qboolean Win95, Win95old, WinNT, WinVista; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; static HANDLE hinput, houtput; #define MAX_HANDLES 32 /* johnfitz -- was 10 */ static FILE *sys_handles[MAX_HANDLES]; static int findhandle (void) { int i; for (i = 1; i < MAX_HANDLES; i++) { if (!sys_handles[i]) return i; } Sys_Error ("out of handles"); return -1; } long Sys_filelength (FILE *f) { long pos, end; pos = ftell (f); fseek (f, 0, SEEK_END); end = ftell (f); fseek (f, pos, SEEK_SET); return end; } int Sys_FileOpenRead (const char *path, int *hndl) { FILE *f; int i, retval; i = findhandle (); f = fopen(path, "rb"); if (!f) { *hndl = -1; retval = -1; } else { sys_handles[i] = f; *hndl = i; retval = Sys_filelength(f); } return retval; } int Sys_FileOpenWrite (const char *path) { FILE *f; int i; i = findhandle (); f = fopen(path, "wb"); if (!f) Sys_Error ("Error opening %s: %s", path, strerror(errno)); sys_handles[i] = f; return i; } void Sys_FileClose (int handle) { fclose (sys_handles[handle]); sys_handles[handle] = NULL; } void Sys_FileSeek (int handle, int position) { fseek (sys_handles[handle], position, SEEK_SET); } int Sys_FileRead (int handle, void *dest, int count) { return fread (dest, 1, count, sys_handles[handle]); } int Sys_FileWrite (int handle, const void *data, int count) { return fwrite (data, 1, count, sys_handles[handle]); } int Sys_FileTime (const char *path) { FILE *f; f = fopen(path, "rb"); if (f) { fclose(f); return 1; } return -1; } static char cwd[1024]; static void Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; size_t rc; rc = GetCurrentDirectory(dstsize, dst); if (rc == 0 || rc > dstsize) Sys_Error ("Couldn't determine current directory"); tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } } typedef enum { dpi_unaware = 0, dpi_system_aware = 1, dpi_monitor_aware = 2 } dpi_awareness; typedef BOOL (WINAPI *SetProcessDPIAwareFunc)(); typedef HRESULT (WINAPI *SetProcessDPIAwarenessFunc)(dpi_awareness value); static void Sys_SetDPIAware (void) { HMODULE hUser32, hShcore; SetProcessDPIAwarenessFunc setDPIAwareness; SetProcessDPIAwareFunc setDPIAware; /* Neither SDL 1.2 nor SDL 2.0.3 can handle the OS scaling our window. (e.g. https://bugzilla.libsdl.org/show_bug.cgi?id=2713) Call SetProcessDpiAwareness/SetProcessDPIAware to opt out of scaling. */ hShcore = LoadLibraryA ("Shcore.dll"); hUser32 = LoadLibraryA ("user32.dll"); setDPIAwareness = (SetProcessDPIAwarenessFunc) (hShcore ? GetProcAddress (hShcore, "SetProcessDpiAwareness") : NULL); setDPIAware = (SetProcessDPIAwareFunc) (hUser32 ? GetProcAddress (hUser32, "SetProcessDPIAware") : NULL); if (setDPIAwareness) /* Windows 8.1+ */ setDPIAwareness (dpi_monitor_aware); else if (setDPIAware) /* Windows Vista-8.0 */ setDPIAware (); if (hShcore) FreeLibrary (hShcore); if (hUser32) FreeLibrary (hUser32); } static void Sys_SetTimerResolution(void) { /* Set OS timer resolution to 1ms. Works around buffer underruns with directsound and SDL2, but also will make Sleep()/SDL_Dleay() accurate to 1ms which should help framerate stability. */ timeBeginPeriod (1); } void Sys_Init (void) { OSVERSIONINFO vinfo; Sys_SetTimerResolution (); Sys_SetDPIAware (); memset (cwd, 0, sizeof(cwd)); Sys_GetBasedir(NULL, cwd, sizeof(cwd)); host_parms->basedir = cwd; /* userdirs not really necessary for windows guys. * can be done if necessary, though... */ host_parms->userdir = host_parms->basedir; /* code elsewhere relies on this ! */ vinfo.dwOSVersionInfoSize = sizeof(vinfo); if (!GetVersionEx (&vinfo)) Sys_Error ("Couldn't get OS info"); if ((vinfo.dwMajorVersion < 4) || (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) { Sys_Error ("QuakeSpasm requires at least Win95 or NT 4.0"); } if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { SYSTEM_INFO info; WinNT = true; if (vinfo.dwMajorVersion >= 6) WinVista = true; GetSystemInfo(&info); host_parms->numcpus = info.dwNumberOfProcessors; if (host_parms->numcpus < 1) host_parms->numcpus = 1; } else { WinNT = false; /* Win9x or WinME */ host_parms->numcpus = 1; if ((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0)) { Win95 = true; /* Win95-gold or Win95A can't switch bpp automatically */ if (vinfo.szCSDVersion[1] != 'C' && vinfo.szCSDVersion[1] != 'B') Win95old = true; } } Sys_Printf("Detected %d CPUs.\n", host_parms->numcpus); if (isDedicated) { if (!AllocConsole ()) { isDedicated = false; /* so that we have a graphical error dialog */ Sys_Error ("Couldn't create dedicated server console"); } hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); } } void Sys_mkdir (const char *path) { if (CreateDirectory(path, NULL) != 0) return; if (GetLastError() != ERROR_ALREADY_EXISTS) Sys_Error("Unable to create directory %s", path); } static const char errortxt1[] = "\nERROR-OUT BEGIN\n\n"; static const char errortxt2[] = "\nQUAKE ERROR: "; void Sys_Error (const char *error, ...) { va_list argptr; char text[1024]; DWORD dummy; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (isDedicated) WriteFile (houtput, errortxt1, strlen(errortxt1), &dummy, NULL); /* SDL will put these into its own stderr log, so print to stderr even in graphical mode. */ fputs (errortxt1, stderr); Host_Shutdown (); fputs (errortxt2, stderr); fputs (text, stderr); fputs ("\n\n", stderr); if (!isDedicated) PL_ErrorDialog(text); else { WriteFile (houtput, errortxt2, strlen(errortxt2), &dummy, NULL); WriteFile (houtput, text, strlen(text), &dummy, NULL); WriteFile (houtput, "\r\n", 2, &dummy, NULL); SDL_Delay (3000); /* show the console 3 more seconds */ } exit (1); } void Sys_Printf (const char *fmt, ...) { va_list argptr; char text[1024]; DWORD dummy; va_start (argptr,fmt); q_vsnprintf (text, sizeof(text), fmt, argptr); va_end (argptr); if (isDedicated) { WriteFile(houtput, text, strlen(text), &dummy, NULL); } else { /* SDL will put these into its own stdout log, so print to stdout even in graphical mode. */ fputs (text, stdout); } } void Sys_Quit (void) { Host_Shutdown(); if (isDedicated) FreeConsole (); exit (0); } double Sys_DoubleTime (void) { return SDL_GetTicks() / 1000.0; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; for ( ;; ) { if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0) Sys_Error ("Error getting # of console events"); if (! numevents) break; if (ReadConsoleInput(hinput, recs, 1, &numread) == 0) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (recs[0].EventType == KEY_EVENT) { if (recs[0].Event.KeyEvent.bKeyDown == FALSE) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (textlen != 0) { con_text[textlen] = 0; textlen = 0; return con_text; } break; case '\b': WriteFile(houtput, "\b \b", 3, &dummy, NULL); if (textlen != 0) textlen--; break; default: if (ch >= ' ') { WriteFile(houtput, &ch, 1, &dummy, NULL); con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; } break; } } } } return NULL; } void Sys_Sleep (unsigned long msecs) { /* Sleep (msecs);*/ SDL_Delay (msecs); } void Sys_SendKeyEvents (void) { IN_Commands(); //ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage IN_SendKeyEvents(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cl_main.c�������������������������������������������������������������������0000644�0000000�0000000�00000045111�13147214764�015302� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_main.c -- client main loop #include "quakedef.h" #include "bgmusic.h" // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. // these two are not intended to be set directly cvar_t cl_name = {"_cl_name", "player", CVAR_ARCHIVE}; cvar_t cl_color = {"_cl_color", "0", CVAR_ARCHIVE}; cvar_t cl_shownet = {"cl_shownet","0",CVAR_NONE}; // can be 0, 1, or 2 cvar_t cl_nolerp = {"cl_nolerp","0",CVAR_NONE}; cvar_t cfg_unbindall = {"cfg_unbindall", "1", CVAR_ARCHIVE}; cvar_t lookspring = {"lookspring","0", CVAR_ARCHIVE}; cvar_t lookstrafe = {"lookstrafe","0", CVAR_ARCHIVE}; cvar_t sensitivity = {"sensitivity","3", CVAR_ARCHIVE}; cvar_t m_pitch = {"m_pitch","0.022", CVAR_ARCHIVE}; cvar_t m_yaw = {"m_yaw","0.022", CVAR_ARCHIVE}; cvar_t m_forward = {"m_forward","1", CVAR_ARCHIVE}; cvar_t m_side = {"m_side","0.8", CVAR_ARCHIVE}; cvar_t cl_maxpitch = {"cl_maxpitch", "90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping cvar_t cl_minpitch = {"cl_minpitch", "-90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping client_static_t cls; client_state_t cl; // FIXME: put these on hunk? entity_t cl_static_entities[MAX_STATIC_ENTITIES]; lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; dlight_t cl_dlights[MAX_DLIGHTS]; entity_t *cl_entities; //johnfitz -- was a static array, now on hunk int cl_max_edicts; //johnfitz -- only changes when new map loads int cl_numvisedicts; entity_t *cl_visedicts[MAX_VISEDICTS]; extern cvar_t r_lerpmodels, r_lerpmove; //johnfitz /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { if (!sv.active) Host_ClearMemory (); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); SZ_Clear (&cls.message); // clear other arrays memset (cl_dlights, 0, sizeof(cl_dlights)); memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); memset (cl_temp_entities, 0, sizeof(cl_temp_entities)); memset (cl_beams, 0, sizeof(cl_beams)); //johnfitz -- cl_entities is now dynamically allocated cl_max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS); cl_entities = (entity_t *) Hunk_AllocName (cl_max_edicts*sizeof(entity_t), "cl_entities"); //johnfitz } /* ===================== CL_Disconnect Sends a disconnect message to the server This is also called on Host_Error, so it shouldn't cause any errors ===================== */ void CL_Disconnect (void) { if (key_dest == key_message) Key_EndChat (); // don't get stuck in chat mode // stop sounds (especially looping!) S_StopAllSounds (true); BGM_Stop(); CDAudio_Stop(); // if running a local server, shut it down if (cls.demoplayback) CL_StopPlayback (); else if (cls.state == ca_connected) { if (cls.demorecording) CL_Stop_f (); Con_DPrintf ("Sending clc_disconnect\n"); SZ_Clear (&cls.message); MSG_WriteByte (&cls.message, clc_disconnect); NET_SendUnreliableMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); NET_Close (cls.netcon); cls.state = ca_disconnected; if (sv.active) Host_ShutdownServer(false); } cls.demoplayback = cls.timedemo = false; cls.demopaused = false; cls.signon = 0; cl.intermission = 0; } void CL_Disconnect_f (void) { CL_Disconnect (); if (sv.active) Host_ShutdownServer (false); } /* ===================== CL_EstablishConnection Host should be either "local" or a net address to be passed on ===================== */ void CL_EstablishConnection (const char *host) { if (cls.state == ca_dedicated) return; if (cls.demoplayback) return; CL_Disconnect (); cls.netcon = NET_Connect (host); if (!cls.netcon) Host_Error ("CL_Connect: connect failed\n"); Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host); cls.demonum = -1; // not in the demo loop now cls.state = ca_connected; cls.signon = 0; // need all the signon messages before playing MSG_WriteByte (&cls.message, clc_nop); // NAT Fix from ProQuake } /* ===================== CL_SignonReply An svc_signonnum has been received, perform a client side setup ===================== */ void CL_SignonReply (void) { char str[8192]; Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); switch (cls.signon) { case 1: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "prespawn"); break; case 2: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15)); MSG_WriteByte (&cls.message, clc_stringcmd); sprintf (str, "spawn %s", cls.spawnparms); MSG_WriteString (&cls.message, str); break; case 3: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "begin"); Cache_Report (); // print remaining memory break; case 4: SCR_EndLoadingPlaque (); // allow normal screen updates break; } } /* ===================== CL_NextDemo Called to play the next demo in the demo loop ===================== */ void CL_NextDemo (void) { char str[1024]; if (cls.demonum == -1) return; // don't play demos if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (!cls.demos[cls.demonum][0]) { Con_Printf ("No demos listed with startdemos\n"); cls.demonum = -1; CL_Disconnect(); return; } } SCR_BeginLoadingPlaque (); sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]); Cbuf_InsertText (str); cls.demonum++; } /* ============== CL_PrintEntities_f ============== */ void CL_PrintEntities_f (void) { entity_t *ent; int i; if (cls.state != ca_connected) return; for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++) { Con_Printf ("%3i:",i); if (!ent->model) { Con_Printf ("EMPTY\n"); continue; } Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n" ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]); } } /* =============== CL_AllocDlight =============== */ dlight_t *CL_AllocDlight (int key) { int i; dlight_t *dl; // first look for an exact key match if (key) { dl = cl_dlights; for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) { if (dl->key == key) { memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } } } // then look for anything else dl = cl_dlights; for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) { if (dl->die < cl.time) { memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } } dl = &cl_dlights[0]; memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } /* =============== CL_DecayLights =============== */ void CL_DecayLights (void) { int i; dlight_t *dl; float time; time = cl.time - cl.oldtime; dl = cl_dlights; for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) { if (dl->die < cl.time || !dl->radius) continue; dl->radius -= time*dl->decay; if (dl->radius < 0) dl->radius = 0; } } /* =============== CL_LerpPoint Determines the fraction between the last two messages that the objects should be put at. =============== */ float CL_LerpPoint (void) { float f, frac; f = cl.mtime[0] - cl.mtime[1]; if (!f || cls.timedemo || sv.active) { cl.time = cl.mtime[0]; return 1; } if (f > 0.1) // dropped packet, or start of demo { cl.mtime[1] = cl.mtime[0] - 0.1; f = 0.1; } frac = (cl.time - cl.mtime[1]) / f; if (frac < 0) { if (frac < -0.01) cl.time = cl.mtime[1]; frac = 0; } else if (frac > 1) { if (frac > 1.01) cl.time = cl.mtime[0]; frac = 1; } //johnfitz -- better nolerp behavior if (cl_nolerp.value) return 1; //johnfitz return frac; } /* =============== CL_RelinkEntities =============== */ void CL_RelinkEntities (void) { entity_t *ent; int i, j; float frac, f, d; vec3_t delta; float bobjrotate; vec3_t oldorg; dlight_t *dl; // determine partial update time frac = CL_LerpPoint (); cl_numvisedicts = 0; // // interpolate player info // for (i=0 ; i<3 ; i++) cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); if (cls.demoplayback) { // interpolate the angles for (j=0 ; j<3 ; j++) { d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; cl.viewangles[j] = cl.mviewangles[1][j] + frac*d; } } bobjrotate = anglemod(100*cl.time); // start on the entity after the world for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++) { if (!ent->model) { // empty slot // ericw -- efrags are only used for static entities in GLQuake // ent can't be static, so this is a no-op. //if (ent->forcelink) // R_RemoveEfrags (ent); // just became empty continue; } // if the object wasn't included in the last packet, remove it if (ent->msgtime != cl.mtime[0]) { ent->model = NULL; ent->lerpflags |= LERP_RESETMOVE|LERP_RESETANIM; //johnfitz -- next time this entity slot is reused, the lerp will need to be reset continue; } VectorCopy (ent->origin, oldorg); if (ent->forcelink) { // the entity was not updated in the last message // so move to the final spot VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->angles); } else { // if the delta is large, assume a teleport and don't lerp f = frac; for (j=0 ; j<3 ; j++) { delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j]; if (delta[j] > 100 || delta[j] < -100) { f = 1; // assume a teleportation, not a motion ent->lerpflags |= LERP_RESETMOVE; //johnfitz -- don't lerp teleports } } //johnfitz -- don't cl_lerp entities that will be r_lerped if (r_lerpmove.value && (ent->lerpflags & LERP_MOVESTEP)) f = 1; //johnfitz // interpolate the origin and angles for (j=0 ; j<3 ; j++) { ent->origin[j] = ent->msg_origins[1][j] + f*delta[j]; d = ent->msg_angles[0][j] - ent->msg_angles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; ent->angles[j] = ent->msg_angles[1][j] + f*d; } } // rotate binary objects locally if (ent->model->flags & EF_ROTATE) ent->angles[1] = bobjrotate; if (ent->effects & EF_BRIGHTFIELD) R_EntityParticles (ent); if (ent->effects & EF_MUZZLEFLASH) { vec3_t fv, rv, uv; dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; AngleVectors (ent->angles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand()&31); dl->minlight = 32; dl->die = cl.time + 0.1; //johnfitz -- assume muzzle flash accompanied by muzzle flare, which looks bad when lerped if (r_lerpmodels.value != 2) { if (ent == &cl_entities[cl.viewentity]) cl.viewent.lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames else ent->lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames } //johnfitz } if (ent->effects & EF_BRIGHTLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; dl->radius = 400 + (rand()&31); dl->die = cl.time + 0.001; } if (ent->effects & EF_DIMLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001; } if (ent->model->flags & EF_GIB) R_RocketTrail (oldorg, ent->origin, 2); else if (ent->model->flags & EF_ZOMGIB) R_RocketTrail (oldorg, ent->origin, 4); else if (ent->model->flags & EF_TRACER) R_RocketTrail (oldorg, ent->origin, 3); else if (ent->model->flags & EF_TRACER2) R_RocketTrail (oldorg, ent->origin, 5); else if (ent->model->flags & EF_ROCKET) { R_RocketTrail (oldorg, ent->origin, 0); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.01; } else if (ent->model->flags & EF_GRENADE) R_RocketTrail (oldorg, ent->origin, 1); else if (ent->model->flags & EF_TRACER3) R_RocketTrail (oldorg, ent->origin, 6); ent->forcelink = false; if (i == cl.viewentity && !chase_active.value) continue; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } } } /* =============== CL_ReadFromServer Read all incoming data from the server =============== */ int CL_ReadFromServer (void) { int ret; extern int num_temp_entities; //johnfitz int num_beams = 0; //johnfitz int num_dlights = 0; //johnfitz beam_t *b; //johnfitz dlight_t *l; //johnfitz int i; //johnfitz cl.oldtime = cl.time; cl.time += host_frametime; do { ret = CL_GetMessage (); if (ret == -1) Host_Error ("CL_ReadFromServer: lost server connection"); if (!ret) break; cl.last_received_message = realtime; CL_ParseServerMessage (); } while (ret && cls.state == ca_connected); if (cl_shownet.value) Con_Printf ("\n"); CL_RelinkEntities (); CL_UpdateTEnts (); //johnfitz -- devstats //visedicts if (cl_numvisedicts > 256 && dev_peakstats.visedicts <= 256) Con_DWarning ("%i visedicts exceeds standard limit of 256 (max = %d).\n", cl_numvisedicts, MAX_VISEDICTS); dev_stats.visedicts = cl_numvisedicts; dev_peakstats.visedicts = q_max(cl_numvisedicts, dev_peakstats.visedicts); //temp entities if (num_temp_entities > 64 && dev_peakstats.tempents <= 64) Con_DWarning ("%i tempentities exceeds standard limit of 64 (max = %d).\n", num_temp_entities, MAX_TEMP_ENTITIES); dev_stats.tempents = num_temp_entities; dev_peakstats.tempents = q_max(num_temp_entities, dev_peakstats.tempents); //beams for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) if (b->model && b->endtime >= cl.time) num_beams++; if (num_beams > 24 && dev_peakstats.beams <= 24) Con_DWarning ("%i beams exceeded standard limit of 24 (max = %d).\n", num_beams, MAX_BEAMS); dev_stats.beams = num_beams; dev_peakstats.beams = q_max(num_beams, dev_peakstats.beams); //dlights for (i=0, l=cl_dlights ; i<MAX_DLIGHTS ; i++, l++) if (l->die >= cl.time && l->radius) num_dlights++; if (num_dlights > 32 && dev_peakstats.dlights <= 32) Con_DWarning ("%i dlights exceeded standard limit of 32 (max = %d).\n", num_dlights, MAX_DLIGHTS); dev_stats.dlights = num_dlights; dev_peakstats.dlights = q_max(num_dlights, dev_peakstats.dlights); //johnfitz // // bring the links up to date // return 0; } /* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { usercmd_t cmd; if (cls.state != ca_connected) return; if (cls.signon == SIGNONS) { // get basic movement from keyboard CL_BaseMove (&cmd); // allow mice or other external controllers to add to the move IN_Move (&cmd); // send the unreliable message CL_SendMove (&cmd); } if (cls.demoplayback) { SZ_Clear (&cls.message); return; } // send the reliable message if (!cls.message.cursize) return; // no message at all if (!NET_CanSendMessage (cls.netcon)) { Con_DPrintf ("CL_SendCmd: can't send\n"); return; } if (NET_SendMessage (cls.netcon, &cls.message) == -1) Host_Error ("CL_SendCmd: lost server connection"); SZ_Clear (&cls.message); } /* ============= CL_Tracepos_f -- johnfitz display impact point of trace along VPN ============= */ void CL_Tracepos_f (void) { vec3_t v, w; if (cls.state != ca_connected) return; VectorMA(r_refdef.vieworg, 8192.0, vpn, v); TraceLine(r_refdef.vieworg, v, w); if (VectorLength(w) == 0) Con_Printf ("Tracepos: trace didn't hit anything\n"); else Con_Printf ("Tracepos: (%i %i %i)\n", (int)w[0], (int)w[1], (int)w[2]); } /* ============= CL_Viewpos_f -- johnfitz display client's position and angles ============= */ void CL_Viewpos_f (void) { if (cls.state != ca_connected) return; #if 0 //camera position Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n", (int)r_refdef.vieworg[0], (int)r_refdef.vieworg[1], (int)r_refdef.vieworg[2], (int)r_refdef.viewangles[PITCH], (int)r_refdef.viewangles[YAW], (int)r_refdef.viewangles[ROLL]); #else //player position Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n", (int)cl_entities[cl.viewentity].origin[0], (int)cl_entities[cl.viewentity].origin[1], (int)cl_entities[cl.viewentity].origin[2], (int)cl.viewangles[PITCH], (int)cl.viewangles[YAW], (int)cl.viewangles[ROLL]); #endif } /* ================= CL_Init ================= */ void CL_Init (void) { SZ_Alloc (&cls.message, 1024); CL_InitInput (); CL_InitTEnts (); Cvar_RegisterVariable (&cl_name); Cvar_RegisterVariable (&cl_color); Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); Cvar_RegisterVariable (&cl_sidespeed); Cvar_RegisterVariable (&cl_movespeedkey); Cvar_RegisterVariable (&cl_yawspeed); Cvar_RegisterVariable (&cl_pitchspeed); Cvar_RegisterVariable (&cl_anglespeedkey); Cvar_RegisterVariable (&cl_shownet); Cvar_RegisterVariable (&cl_nolerp); Cvar_RegisterVariable (&lookspring); Cvar_RegisterVariable (&lookstrafe); Cvar_RegisterVariable (&sensitivity); Cvar_RegisterVariable (&cl_alwaysrun); Cvar_RegisterVariable (&m_pitch); Cvar_RegisterVariable (&m_yaw); Cvar_RegisterVariable (&m_forward); Cvar_RegisterVariable (&m_side); Cvar_RegisterVariable (&cfg_unbindall); Cvar_RegisterVariable (&cl_maxpitch); //johnfitz -- variable pitch clamping Cvar_RegisterVariable (&cl_minpitch); //johnfitz -- variable pitch clamping Cmd_AddCommand ("entities", CL_PrintEntities_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("stop", CL_Stop_f); Cmd_AddCommand ("playdemo", CL_PlayDemo_f); Cmd_AddCommand ("timedemo", CL_TimeDemo_f); Cmd_AddCommand ("tracepos", CL_Tracepos_f); //johnfitz Cmd_AddCommand ("viewpos", CL_Viewpos_f); //johnfitz } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/world.c���������������������������������������������������������������������0000644�0000000�0000000�00000051766�13150676250�015040� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // world.c -- world query functions #include "quakedef.h" /* entities never clip against themselves, or their owner line of sight checks trace->crosscontent, but bullets don't */ typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move float *mins, *maxs; // size of the moving object vec3_t mins2, maxs2; // size when clipping against mosnters float *start, *end; trace_t trace; int type; edict_t *passedict; } moveclip_t; int SV_HullPointContents (hull_t *hull, int num, vec3_t p); /* =============================================================================== HULL BOXES =============================================================================== */ static hull_t box_hull; static mclipnode_t box_clipnodes[6]; //johnfitz -- was dclipnode_t static mplane_t box_planes[6]; /* =================== SV_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ void SV_InitBoxHull (void) { int i; int side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; for (i=0 ; i<6 ; i++) { box_clipnodes[i].planenum = i; side = i&1; box_clipnodes[i].children[side] = CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side^1] = i + 1; else box_clipnodes[i].children[side^1] = CONTENTS_SOLID; box_planes[i].type = i>>1; box_planes[i].normal[i>>1] = 1; } } /* =================== SV_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } /* ================ SV_HullForEntity Returns a hull that can be used for testing or clipping an object of mins/maxs size. Offset is filled in to contain the adjustment that must be added to the testing object's origin to get a point to use with the returned hull. ================ */ hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) { qmodel_t *model; vec3_t size; vec3_t hullmins, hullmaxs; hull_t *hull; // decide which clipping hull to use, based on the size if (ent->v.solid == SOLID_BSP) { // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH) Host_Error ("SOLID_BSP without MOVETYPE_PUSH (%s at %f %f %f)", PR_GetString(ent->v.classname), ent->v.origin[0], ent->v.origin[1], ent->v.origin[2]); model = sv.models[ (int)ent->v.modelindex ]; if (!model || model->type != mod_brush) Host_Error ("SOLID_BSP with a non bsp model (%s at %f %f %f)", PR_GetString(ent->v.classname), ent->v.origin[0], ent->v.origin[1], ent->v.origin[2]); VectorSubtract (maxs, mins, size); if (size[0] < 3) hull = &model->hulls[0]; else if (size[0] <= 32) hull = &model->hulls[1]; else hull = &model->hulls[2]; // calculate an offset value to center the origin VectorSubtract (hull->clip_mins, mins, offset); VectorAdd (offset, ent->v.origin, offset); } else { // create a temp hull from bounding box sizes VectorSubtract (ent->v.mins, maxs, hullmins); VectorSubtract (ent->v.maxs, mins, hullmaxs); hull = SV_HullForBox (hullmins, hullmaxs); VectorCopy (ent->v.origin, offset); } return hull; } /* =============================================================================== ENTITY AREA CHECKING =============================================================================== */ typedef struct areanode_s { int axis; // -1 = leaf node float dist; struct areanode_s *children[2]; link_t trigger_edicts; link_t solid_edicts; } areanode_t; #define AREA_DEPTH 4 #define AREA_NODES 32 static areanode_t sv_areanodes[AREA_NODES]; static int sv_numareanodes; /* =============== SV_CreateAreaNode =============== */ areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) { areanode_t *anode; vec3_t size; vec3_t mins1, maxs1, mins2, maxs2; anode = &sv_areanodes[sv_numareanodes]; sv_numareanodes++; ClearLink (&anode->trigger_edicts); ClearLink (&anode->solid_edicts); if (depth == AREA_DEPTH) { anode->axis = -1; anode->children[0] = anode->children[1] = NULL; return anode; } VectorSubtract (maxs, mins, size); if (size[0] > size[1]) anode->axis = 0; else anode->axis = 1; anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); VectorCopy (mins, mins1); VectorCopy (mins, mins2); VectorCopy (maxs, maxs1); VectorCopy (maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); return anode; } /* =============== SV_ClearWorld =============== */ void SV_ClearWorld (void) { SV_InitBoxHull (); memset (sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); } /* =============== SV_UnlinkEdict =============== */ void SV_UnlinkEdict (edict_t *ent) { if (!ent->area.prev) return; // not linked in anywhere RemoveLink (&ent->area); ent->area.prev = ent->area.next = NULL; } /* ==================== SV_AreaTriggerEdicts Spike -- just builds a list of entities within the area, rather than walking them and risking the list getting corrupt. ==================== */ static void SV_AreaTriggerEdicts ( edict_t *ent, areanode_t *node, edict_t **list, int *listcount, const int listspace ) { link_t *l, *next; edict_t *touch; // touch linked edicts for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next) { next = l->next; touch = EDICT_FROM_AREA(l); if (touch == ent) continue; if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) continue; if (ent->v.absmin[0] > touch->v.absmax[0] || ent->v.absmin[1] > touch->v.absmax[1] || ent->v.absmin[2] > touch->v.absmax[2] || ent->v.absmax[0] < touch->v.absmin[0] || ent->v.absmax[1] < touch->v.absmin[1] || ent->v.absmax[2] < touch->v.absmin[2] ) continue; if (*listcount == listspace) return; // should never happen list[*listcount] = touch; (*listcount)++; } // recurse down both sides if (node->axis == -1) return; if ( ent->v.absmax[node->axis] > node->dist ) SV_AreaTriggerEdicts ( ent, node->children[0], list, listcount, listspace ); if ( ent->v.absmin[node->axis] < node->dist ) SV_AreaTriggerEdicts ( ent, node->children[1], list, listcount, listspace ); } /* ==================== SV_TouchLinks ericw -- copy the touching edicts to an array (on the hunk) so we can avoid iteating the trigger_edicts linked list while calling PR_ExecuteProgram which could potentially corrupt the list while it's being iterated. Based on code from Spike. ==================== */ void SV_TouchLinks (edict_t *ent) { edict_t **list; edict_t *touch; int old_self, old_other; int i, listcount; int mark; mark = Hunk_LowMark (); list = (edict_t **) Hunk_Alloc (sv.num_edicts*sizeof(edict_t *)); listcount = 0; SV_AreaTriggerEdicts (ent, sv_areanodes, list, &listcount, sv.num_edicts); for (i = 0; i < listcount; i++) { touch = list[i]; // re-validate in case of PR_ExecuteProgram having side effects that make // edicts later in the list no longer touch if (touch == ent) continue; if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) continue; if (ent->v.absmin[0] > touch->v.absmax[0] || ent->v.absmin[1] > touch->v.absmax[1] || ent->v.absmin[2] > touch->v.absmax[2] || ent->v.absmax[0] < touch->v.absmin[0] || ent->v.absmax[1] < touch->v.absmin[1] || ent->v.absmax[2] < touch->v.absmin[2] ) continue; old_self = pr_global_struct->self; old_other = pr_global_struct->other; pr_global_struct->self = EDICT_TO_PROG(touch); pr_global_struct->other = EDICT_TO_PROG(ent); pr_global_struct->time = sv.time; PR_ExecuteProgram (touch->v.touch); pr_global_struct->self = old_self; pr_global_struct->other = old_other; } // free hunk-allocated edicts array Hunk_FreeToLowMark (mark); } /* =============== SV_FindTouchedLeafs =============== */ void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) { mplane_t *splitplane; mleaf_t *leaf; int sides; int leafnum; if (node->contents == CONTENTS_SOLID) return; // add an efrag if the node is a leaf if ( node->contents < 0) { if (ent->num_leafs == MAX_ENT_LEAFS) return; leaf = (mleaf_t *)node; leafnum = leaf - sv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); // recurse down the contacted sides if (sides & 1) SV_FindTouchedLeafs (ent, node->children[0]); if (sides & 2) SV_FindTouchedLeafs (ent, node->children[1]); } /* =============== SV_LinkEdict =============== */ void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) { areanode_t *node; if (ent->area.prev) SV_UnlinkEdict (ent); // unlink from old position if (ent == sv.edicts) return; // don't add the world if (ent->free) return; // set the abs box VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded // if ((int)ent->v.flags & FL_ITEM) { ent->v.absmin[0] -= 15; ent->v.absmin[1] -= 15; ent->v.absmax[0] += 15; ent->v.absmax[1] += 15; } else { // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->v.absmin[0] -= 1; ent->v.absmin[1] -= 1; ent->v.absmin[2] -= 1; ent->v.absmax[0] += 1; ent->v.absmax[1] += 1; ent->v.absmax[2] += 1; } // link to PVS leafs ent->num_leafs = 0; if (ent->v.modelindex) SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); if (ent->v.solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; if (ent->v.absmin[node->axis] > node->dist) node = node->children[0]; else if (ent->v.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in if (ent->v.solid == SOLID_TRIGGER) InsertLinkBefore (&ent->area, &node->trigger_edicts); else InsertLinkBefore (&ent->area, &node->solid_edicts); // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) SV_TouchLinks ( ent ); } /* =============================================================================== POINT TESTING IN HULLS =============================================================================== */ /* ================== SV_HullPointContents ================== */ int SV_HullPointContents (hull_t *hull, int num, vec3_t p) { float d; mclipnode_t *node; //johnfitz -- was dclipnode_t mplane_t *plane; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("SV_HullPointContents: bad node number"); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DoublePrecisionDotProduct (plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } return num; } /* ================== SV_PointContents ================== */ int SV_PointContents (vec3_t p) { int cont; cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; } int SV_TruePointContents (vec3_t p) { return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); } //=========================================================================== /* ============ SV_TestEntityPosition This could be a lot more efficient... ============ */ edict_t *SV_TestEntityPosition (edict_t *ent) { trace_t trace; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); if (trace.startsolid) return sv.edicts; return NULL; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ /* ================== SV_RecursiveHullCheck ================== */ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) { mclipnode_t *node; //johnfitz -- was dclipnode_t mplane_t *plane; float t1, t2; float frac; int i; vec3_t mid; int side; float midf; // check for empty if (num < 0) { if (num != CONTENTS_SOLID) { trace->allsolid = false; if (num == CONTENTS_EMPTY) trace->inopen = true; else trace->inwater = true; } else trace->startsolid = true; return true; // empty } if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("SV_RecursiveHullCheck: bad node number"); // // find the point distances // node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } else { t1 = DoublePrecisionDotProduct (plane->normal, p1) - plane->dist; t2 = DoublePrecisionDotProduct (plane->normal, p2) - plane->dist; } #if 1 if (t1 >= 0 && t2 >= 0) return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if (t1 < 0 && t2 < 0) return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #else if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #endif // put the crosspoint DIST_EPSILON pixels on the near side if (t1 < 0) frac = (t1 + DIST_EPSILON)/(t1-t2); else frac = (t1 - DIST_EPSILON)/(t1-t2); if (frac < 0) frac = 0; if (frac > 1) frac = 1; midf = p1f + (p2f - p1f)*frac; for (i=0 ; i<3 ; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); side = (t1 < 0); // move up to the node if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) return false; #ifdef PARANOID if (SV_HullPointContents (sv_hullmodel, mid, node->children[side]) == CONTENTS_SOLID) { Con_Printf ("mid PointInHullSolid\n"); return false; } #endif if (SV_HullPointContents (hull, node->children[side^1], mid) != CONTENTS_SOLID) // go past the node return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); if (trace->allsolid) return false; // never got out of the solid area //================== // the other side of the node is solid, this is the impact point //================== if (!side) { VectorCopy (plane->normal, trace->plane.normal); trace->plane.dist = plane->dist; } else { VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); trace->plane.dist = -plane->dist; } while (SV_HullPointContents (hull, hull->firstclipnode, mid) == CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1; if (frac < 0) { trace->fraction = midf; VectorCopy (mid, trace->endpos); Con_DPrintf ("backup past 0\n"); return false; } midf = p1f + (p2f - p1f)*frac; for (i=0 ; i<3 ; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); } trace->fraction = midf; VectorCopy (mid, trace->endpos); return false; } /* ================== SV_ClipMoveToEntity Handles selection or creation of a clipping hull, and offseting (and eventually rotation) of the end points ================== */ trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { trace_t trace; vec3_t offset; vec3_t start_l, end_l; hull_t *hull; // fill in a default trace memset (&trace, 0, sizeof(trace_t)); trace.fraction = 1; trace.allsolid = true; VectorCopy (end, trace.endpos); // get the clipping hull hull = SV_HullForEntity (ent, mins, maxs, offset); VectorSubtract (start, offset, start_l); VectorSubtract (end, offset, end_l); // trace a line through the apropriate clipping hull SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); // fix trace up by the offset if (trace.fraction != 1) VectorAdd (trace.endpos, offset, trace.endpos); // did we clip the move? if (trace.fraction < 1 || trace.startsolid ) trace.ent = ent; return trace; } //=========================================================================== /* ==================== SV_ClipToLinks Mins and maxs enclose the entire area swept by the move ==================== */ void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip ) { link_t *l, *next; edict_t *touch; trace_t trace; // touch linked edicts for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) { next = l->next; touch = EDICT_FROM_AREA(l); if (touch->v.solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; if (touch->v.solid == SOLID_TRIGGER) Sys_Error ("Trigger in clipping list"); if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP) continue; if (clip->boxmins[0] > touch->v.absmax[0] || clip->boxmins[1] > touch->v.absmax[1] || clip->boxmins[2] > touch->v.absmax[2] || clip->boxmaxs[0] < touch->v.absmin[0] || clip->boxmaxs[1] < touch->v.absmin[1] || clip->boxmaxs[2] < touch->v.absmin[2] ) continue; if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) continue; // points never interact // might intersect, so do an exact clip if (clip->trace.allsolid) return; if (clip->passedict) { if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) continue; // don't clip against own missiles if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) continue; // don't clip against owner } if ((int)touch->v.flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end); else trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end); if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) { trace.ent = touch; if (clip->trace.startsolid) { clip->trace = trace; clip->trace.startsolid = true; } else clip->trace = trace; } else if (trace.startsolid) clip->trace.startsolid = true; } // recurse down both sides if (node->axis == -1) return; if ( clip->boxmaxs[node->axis] > node->dist ) SV_ClipToLinks ( node->children[0], clip ); if ( clip->boxmins[node->axis] < node->dist ) SV_ClipToLinks ( node->children[1], clip ); } /* ================== SV_MoveBounds ================== */ void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) { #if 0 // debug to test against everything boxmins[0] = boxmins[1] = boxmins[2] = -9999; boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999; #else int i; for (i=0 ; i<3 ; i++) { if (end[i] > start[i]) { boxmins[i] = start[i] + mins[i] - 1; boxmaxs[i] = end[i] + maxs[i] + 1; } else { boxmins[i] = end[i] + mins[i] - 1; boxmaxs[i] = start[i] + maxs[i] + 1; } } #endif } /* ================== SV_Move ================== */ trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) { moveclip_t clip; int i; memset ( &clip, 0, sizeof ( moveclip_t ) ); // clip to world clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end ); clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.type = type; clip.passedict = passedict; if (type == MOVE_MISSILE) { for (i=0 ; i<3 ; i++) { clip.mins2[i] = -15; clip.maxs2[i] = 15; } } else { VectorCopy (mins, clip.mins2); VectorCopy (maxs, clip.maxs2); } // create the bounding box of the entire move SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); // clip to entities SV_ClipToLinks ( sv_areanodes, &clip ); return clip.trace; } ����������quakespasm-0.93.0/Quake/spritegn.h������������������������������������������������������������������0000644�0000000�0000000�00000005564�11340073704�015536� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SPRITEGEN_H #define __SPRITEGEN_H // // spritegn.h: header file for sprite generation program // // ********************************************************** // * This file must be identical in the spritegen directory * // * and in the Quake directory, because it's used to * // * pass data from one to the other via .spr files. * // ********************************************************** //------------------------------------------------------- // This program generates .spr sprite package files. // The format of the files is as follows: // // dsprite_t file header structure // <repeat dsprite_t.numframes times> // <if spritegroup, repeat dspritegroup_t.numframes times> // dspriteframe_t frame header structure // sprite bitmap // <else (single sprite frame)> // dspriteframe_t frame header structure // sprite bitmap // <endrepeat> //------------------------------------------------------- #ifdef INCLUDELIBS #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h> #include "cmdlib.h" #include "scriplib.h" #include "dictlib.h" #include "trilib.h" #include "lbmlib.h" #include "mathlib.h" #endif #define SPRITE_VERSION 1 // must match definition in modelgen.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum {ST_SYNC=0, ST_RAND } synctype_t; #endif // TODO: shorten these? typedef struct { int ident; int version; int type; float boundingradius; int width; int height; int numframes; float beamlength; synctype_t synctype; } dsprite_t; #define SPR_VP_PARALLEL_UPRIGHT 0 #define SPR_FACING_UPRIGHT 1 #define SPR_VP_PARALLEL 2 #define SPR_ORIENTED 3 #define SPR_VP_PARALLEL_ORIENTED 4 typedef struct { int origin[2]; int width; int height; } dspriteframe_t; typedef struct { int numframes; } dspritegroup_t; typedef struct { float interval; } dspriteinterval_t; typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; typedef struct { spriteframetype_t type; } dspriteframetype_t; #define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') // little-endian "IDSP" #endif /* __SPRITEGEN_H */ ��������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cl_parse.c������������������������������������������������������������������0000644�0000000�0000000�00000073727�13067122410�015471� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_parse.c -- parse a message received from the server #include "quakedef.h" #include "bgmusic.h" const char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // <see code> "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's console buffer // the string should be \n terminated "svc_setangle", // [vec3] set the view angle to this absolute value "svc_serverinfo", // [long] version // [string] signon string // [string]..[0]model cache [string]...[0]sounds cache // [string]..[0]item cache "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // <shortbits + data> "svc_stopsound", // <see code> "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] <variable> "svc_damage", // [byte] impact [byte] blood [vec3] from "svc_spawnstatic", "OBSOLETE svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", // <variable> "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_cutscene", //johnfitz -- new server messages "", // 35 "", // 36 "svc_skybox", // 37 // [string] skyname "", // 38 "", // 39 "svc_bf", // 40 // no data "svc_fog", // 41 // [byte] density [byte] red [byte] green [byte] blue [float] time "svc_spawnbaseline2", //42 // support for large modelindex, large framenum, alpha, using flags "svc_spawnstatic2", // 43 // support for large modelindex, large framenum, alpha, using flags "svc_spawnstaticsound2", // 44 // [coord3] [short] samp [byte] vol [byte] aten "", // 44 "", // 45 "", // 46 "", // 47 "", // 48 "", // 49 //johnfitz }; qboolean warn_about_nehahra_protocol; //johnfitz extern vec3_t v_punchangles[2]; //johnfitz //============================================================================= /* =============== CL_EntityNum This error checks and tracks the total number of entities =============== */ entity_t *CL_EntityNum (int num) { //johnfitz -- check minimum number too if (num < 0) Host_Error ("CL_EntityNum: %i is an invalid number",num); //john if (num >= cl.num_entities) { if (num >= cl_max_edicts) //johnfitz -- no more MAX_EDICTS Host_Error ("CL_EntityNum: %i is an invalid number",num); while (cl.num_entities<=num) { cl_entities[cl.num_entities].colormap = vid.colormap; cl_entities[cl.num_entities].lerpflags |= LERP_RESETMOVE|LERP_RESETANIM; //johnfitz cl.num_entities++; } } return &cl_entities[num]; } /* ================== CL_ParseStartSoundPacket ================== */ void CL_ParseStartSoundPacket(void) { vec3_t pos; int channel, ent; int sound_num; int volume; int field_mask; float attenuation; int i; field_mask = MSG_ReadByte(); if (field_mask & SND_VOLUME) volume = MSG_ReadByte (); else volume = DEFAULT_SOUND_PACKET_VOLUME; if (field_mask & SND_ATTENUATION) attenuation = MSG_ReadByte () / 64.0; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; //johnfitz -- PROTOCOL_FITZQUAKE if (field_mask & SND_LARGEENTITY) { ent = (unsigned short) MSG_ReadShort (); channel = MSG_ReadByte (); } else { channel = (unsigned short) MSG_ReadShort (); ent = channel >> 3; channel &= 7; } if (field_mask & SND_LARGESOUND) sound_num = (unsigned short) MSG_ReadShort (); else sound_num = MSG_ReadByte (); //johnfitz //johnfitz -- check soundnum if (sound_num >= MAX_SOUNDS) Host_Error ("CL_ParseStartSoundPacket: %i > MAX_SOUNDS", sound_num); //johnfitz if (ent > cl_max_edicts) //johnfitz -- no more MAX_EDICTS Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (cl.protocolflags); S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); } /* ================== CL_KeepaliveMessage When the client is taking a long time to load stuff, send keepalive messages so the server doesn't disconnect. ================== */ static byte net_olddata[NET_MAXMESSAGE]; void CL_KeepaliveMessage (void) { float time; static float lastmsg; int ret; sizebuf_t old; byte *olddata; if (sv.active) return; // no need if server is local if (cls.demoplayback) return; // read messages from server, should just be nops olddata = net_olddata; old = net_message; memcpy (olddata, net_message.data, net_message.cursize); do { ret = CL_GetMessage (); switch (ret) { default: Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed"); case 0: break; // nothing waiting case 1: Host_Error ("CL_KeepaliveMessage: received a message"); break; case 2: if (MSG_ReadByte() != svc_nop) Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop"); break; } } while (ret); net_message = old; memcpy (net_message.data, olddata, net_message.cursize); // check time time = Sys_DoubleTime (); if (time - lastmsg < 5) return; lastmsg = time; // write out a nop Con_Printf ("--> client to server keepalive\n"); MSG_WriteByte (&cls.message, clc_nop); NET_SendMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); } /* ================== CL_ParseServerInfo ================== */ void CL_ParseServerInfo (void) { const char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; Con_DPrintf ("Serverinfo packet received.\n"); // ericw -- bring up loading plaque for map changes within a demo. // it will be hidden in CL_SignonReply. if (cls.demoplayback) SCR_BeginLoadingPlaque(); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); //johnfitz -- support multiple protocols if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) { Con_Printf ("\n"); //because there's no newline after serverinfo print Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); } cl.protocol = i; //johnfitz if (cl.protocol == PROTOCOL_RMQ) { const unsigned int supportedflags = (PRFL_SHORTANGLE | PRFL_FLOATANGLE | PRFL_24BITCOORD | PRFL_FLOATCOORD | PRFL_EDICTSCALE | PRFL_INT32COORD); // mh - read protocol flags from server so that we know what protocol features to expect cl.protocolflags = (unsigned int) MSG_ReadLong (); if (0 != (cl.protocolflags & (~supportedflags))) { Con_Warning("PROTOCOL_RMQ protocolflags %i contains unsupported flags\n", cl.protocolflags); } } else cl.protocolflags = 0; // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Host_Error ("Bad maxclients (%u) from server", cl.maxclients); } cl.scores = (scoreboard_t *) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); // parse signon message str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // seperate the printfs so the server message can have a color Con_Printf ("\n%s\n", Con_Quakebar(40)); //johnfitz Con_Printf ("%c%s\n", 2, str); //johnfitz -- tell user which protocol this is Con_Printf ("Using protocol %i\n", i); // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels==MAX_MODELS) { Host_Error ("Server sent too many model precaches"); } q_strlcpy (model_precache[nummodels], str, MAX_QPATH); Mod_TouchModel (str); } //johnfitz -- check for excessive models if (nummodels >= 256) Con_DWarning ("%i models exceeds standard limit of 256 (max = %d).\n", nummodels, MAX_MODELS); //johnfitz // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds==MAX_SOUNDS) { Host_Error ("Server sent too many sound precaches"); } q_strlcpy (sound_precache[numsounds], str, MAX_QPATH); S_TouchSound (str); } //johnfitz -- check for excessive sounds if (numsounds >= 256) Con_DWarning ("%i sounds exceeds standard limit of 256 (max = %d).\n", numsounds, MAX_SOUNDS); //johnfitz // // now we try to load everything else until a cache allocation fails // // copy the naked name of the map file to the cl structure -- O.S COM_StripExtension (COM_SkipPath(model_precache[1]), cl.mapname, sizeof(cl.mapname)); for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); if (cl.model_precache[i] == NULL) { Host_Error ("Model %s not found", model_precache[i]); } CL_KeepaliveMessage (); } S_BeginPrecaching (); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); CL_KeepaliveMessage (); } S_EndPrecaching (); // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); //johnfitz -- clear out string; we don't consider identical //messages to be duplicates if the map has changed in between con_lastcenterstring[0] = 0; //johnfitz Hunk_Check (); // make sure nothing is hurt noclip_anglehack = false; // noclip is turned off at start warn_about_nehahra_protocol = true; //johnfitz -- warn about nehahra protocol hack once per server connection //johnfitz -- reset developer stats memset(&dev_stats, 0, sizeof(dev_stats)); memset(&dev_peakstats, 0, sizeof(dev_peakstats)); memset(&dev_overflows, 0, sizeof(dev_overflows)); } /* ================== CL_ParseUpdate Parse an entity update message from the server If an entities model or origin changes from frame to frame, it must be relinked. Other attributes can change without relinking. ================== */ void CL_ParseUpdate (int bits) { int i; qmodel_t *model; int modnum; qboolean forcelink; entity_t *ent; int num; int skin; if (cls.signon == SIGNONS - 1) { // first update is the final signon stage cls.signon = SIGNONS; CL_SignonReply (); } if (bits & U_MOREBITS) { i = MSG_ReadByte (); bits |= (i<<8); } //johnfitz -- PROTOCOL_FITZQUAKE if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ) { if (bits & U_EXTEND1) bits |= MSG_ReadByte() << 16; if (bits & U_EXTEND2) bits |= MSG_ReadByte() << 24; } //johnfitz if (bits & U_LONGENTITY) num = MSG_ReadShort (); else num = MSG_ReadByte (); ent = CL_EntityNum (num); if (ent->msgtime != cl.mtime[1]) forcelink = true; // no previous frame to lerp from else forcelink = false; //johnfitz -- lerping if (ent->msgtime + 0.2 < cl.mtime[0]) //more than 0.2 seconds since the last message (most entities think every 0.1 sec) ent->lerpflags |= LERP_RESETANIM; //if we missed a think, we'd be lerping from the wrong frame //johnfitz ent->msgtime = cl.mtime[0]; if (bits & U_MODEL) { modnum = MSG_ReadByte (); if (modnum >= MAX_MODELS) Host_Error ("CL_ParseModel: bad modnum"); } else modnum = ent->baseline.modelindex; if (bits & U_FRAME) ent->frame = MSG_ReadByte (); else ent->frame = ent->baseline.frame; if (bits & U_COLORMAP) i = MSG_ReadByte(); else i = ent->baseline.colormap; if (!i) ent->colormap = vid.colormap; else { if (i > cl.maxclients) Sys_Error ("i >= cl.maxclients"); ent->colormap = cl.scores[i-1].translations; } if (bits & U_SKIN) skin = MSG_ReadByte(); else skin = ent->baseline.skin; if (skin != ent->skinnum) { ent->skinnum = skin; if (num > 0 && num <= cl.maxclients) R_TranslateNewPlayerSkin (num - 1); //johnfitz -- was R_TranslatePlayerSkin } if (bits & U_EFFECTS) ent->effects = MSG_ReadByte(); else ent->effects = ent->baseline.effects; // shift the known values for interpolation VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); if (bits & U_ORIGIN1) ent->msg_origins[0][0] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][0] = ent->baseline.origin[0]; if (bits & U_ANGLE1) ent->msg_angles[0][0] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][0] = ent->baseline.angles[0]; if (bits & U_ORIGIN2) ent->msg_origins[0][1] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][1] = ent->baseline.origin[1]; if (bits & U_ANGLE2) ent->msg_angles[0][1] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][1] = ent->baseline.angles[1]; if (bits & U_ORIGIN3) ent->msg_origins[0][2] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][2] = ent->baseline.origin[2]; if (bits & U_ANGLE3) ent->msg_angles[0][2] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][2] = ent->baseline.angles[2]; //johnfitz -- lerping for movetype_step entities if (bits & U_STEP) { ent->lerpflags |= LERP_MOVESTEP; ent->forcelink = true; } else ent->lerpflags &= ~LERP_MOVESTEP; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE and PROTOCOL_NEHAHRA if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ) { if (bits & U_ALPHA) ent->alpha = MSG_ReadByte(); else ent->alpha = ent->baseline.alpha; if (bits & U_SCALE) MSG_ReadByte(); // PROTOCOL_RMQ: currently ignored if (bits & U_FRAME2) ent->frame = (ent->frame & 0x00FF) | (MSG_ReadByte() << 8); if (bits & U_MODEL2) modnum = (modnum & 0x00FF) | (MSG_ReadByte() << 8); if (bits & U_LERPFINISH) { ent->lerpfinish = ent->msgtime + ((float)(MSG_ReadByte()) / 255); ent->lerpflags |= LERP_FINISH; } else ent->lerpflags &= ~LERP_FINISH; } else if (cl.protocol == PROTOCOL_NETQUAKE) { //HACK: if this bit is set, assume this is PROTOCOL_NEHAHRA if (bits & U_TRANS) { float a, b; if (warn_about_nehahra_protocol) { Con_Warning ("nonstandard update bit, assuming Nehahra protocol\n"); warn_about_nehahra_protocol = false; } a = MSG_ReadFloat(); b = MSG_ReadFloat(); //alpha if (a == 2) MSG_ReadFloat(); //fullbright (not using this yet) ent->alpha = ENTALPHA_ENCODE(b); } else ent->alpha = ent->baseline.alpha; } //johnfitz //johnfitz -- moved here from above model = cl.model_precache[modnum]; if (model != ent->model) { ent->model = model; // automatic animation (torches, etc) can be either all together // or randomized if (model) { if (model->synctype == ST_RAND) ent->syncbase = (float)(rand()&0x7fff) / 0x7fff; else ent->syncbase = 0.0; } else forcelink = true; // hack to make null model players work if (num > 0 && num <= cl.maxclients) R_TranslateNewPlayerSkin (num - 1); //johnfitz -- was R_TranslatePlayerSkin ent->lerpflags |= LERP_RESETANIM; //johnfitz -- don't lerp animation across model changes } //johnfitz if ( forcelink ) { // didn't have an update last message VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); VectorCopy (ent->msg_angles[0], ent->angles); ent->forcelink = true; } } /* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline (entity_t *ent, int version) //johnfitz -- added argument { int i; int bits; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE bits = (version == 2) ? MSG_ReadByte() : 0; ent->baseline.modelindex = (bits & B_LARGEMODEL) ? MSG_ReadShort() : MSG_ReadByte(); ent->baseline.frame = (bits & B_LARGEFRAME) ? MSG_ReadShort() : MSG_ReadByte(); //johnfitz ent->baseline.colormap = MSG_ReadByte(); ent->baseline.skin = MSG_ReadByte(); for (i = 0; i < 3; i++) { ent->baseline.origin[i] = MSG_ReadCoord (cl.protocolflags); ent->baseline.angles[i] = MSG_ReadAngle (cl.protocolflags); } ent->baseline.alpha = (bits & B_ALPHA) ? MSG_ReadByte() : ENTALPHA_DEFAULT; //johnfitz -- PROTOCOL_FITZQUAKE } /* ================== CL_ParseClientdata Server information pertaining to this client only ================== */ void CL_ParseClientdata (void) { int i, j; int bits; //johnfitz bits = (unsigned short)MSG_ReadShort (); //johnfitz -- read bits here isntead of in CL_ParseServerMessage() //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_EXTEND1) bits |= (MSG_ReadByte() << 16); if (bits & SU_EXTEND2) bits |= (MSG_ReadByte() << 24); //johnfitz if (bits & SU_VIEWHEIGHT) cl.viewheight = MSG_ReadChar (); else cl.viewheight = DEFAULT_VIEWHEIGHT; if (bits & SU_IDEALPITCH) cl.idealpitch = MSG_ReadChar (); else cl.idealpitch = 0; VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); for (i = 0; i < 3; i++) { if (bits & (SU_PUNCH1<<i) ) cl.punchangle[i] = MSG_ReadChar(); else cl.punchangle[i] = 0; if (bits & (SU_VELOCITY1<<i) ) cl.mvelocity[0][i] = MSG_ReadChar()*16; else cl.mvelocity[0][i] = 0; } //johnfitz -- update v_punchangles if (v_punchangles[0][0] != cl.punchangle[0] || v_punchangles[0][1] != cl.punchangle[1] || v_punchangles[0][2] != cl.punchangle[2]) { VectorCopy (v_punchangles[0], v_punchangles[1]); VectorCopy (cl.punchangle, v_punchangles[0]); } //johnfitz // [always sent] if (bits & SU_ITEMS) i = MSG_ReadLong (); if (cl.items != i) { // set flash times Sbar_Changed (); for (j = 0; j < 32; j++) if ( (i & (1<<j)) && !(cl.items & (1<<j))) cl.item_gettime[j] = cl.time; cl.items = i; } cl.onground = (bits & SU_ONGROUND) != 0; cl.inwater = (bits & SU_INWATER) != 0; if (bits & SU_WEAPONFRAME) cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte (); else cl.stats[STAT_WEAPONFRAME] = 0; if (bits & SU_ARMOR) i = MSG_ReadByte (); else i = 0; if (cl.stats[STAT_ARMOR] != i) { cl.stats[STAT_ARMOR] = i; Sbar_Changed (); } if (bits & SU_WEAPON) i = MSG_ReadByte (); else i = 0; if (cl.stats[STAT_WEAPON] != i) { cl.stats[STAT_WEAPON] = i; Sbar_Changed (); } i = MSG_ReadShort (); if (cl.stats[STAT_HEALTH] != i) { cl.stats[STAT_HEALTH] = i; Sbar_Changed (); } i = MSG_ReadByte (); if (cl.stats[STAT_AMMO] != i) { cl.stats[STAT_AMMO] = i; Sbar_Changed (); } for (i = 0; i < 4; i++) { j = MSG_ReadByte (); if (cl.stats[STAT_SHELLS+i] != j) { cl.stats[STAT_SHELLS+i] = j; Sbar_Changed (); } } i = MSG_ReadByte (); if (standard_quake) { if (cl.stats[STAT_ACTIVEWEAPON] != i) { cl.stats[STAT_ACTIVEWEAPON] = i; Sbar_Changed (); } } else { if (cl.stats[STAT_ACTIVEWEAPON] != (1<<i)) { cl.stats[STAT_ACTIVEWEAPON] = (1<<i); Sbar_Changed (); } } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_WEAPON2) cl.stats[STAT_WEAPON] |= (MSG_ReadByte() << 8); if (bits & SU_ARMOR2) cl.stats[STAT_ARMOR] |= (MSG_ReadByte() << 8); if (bits & SU_AMMO2) cl.stats[STAT_AMMO] |= (MSG_ReadByte() << 8); if (bits & SU_SHELLS2) cl.stats[STAT_SHELLS] |= (MSG_ReadByte() << 8); if (bits & SU_NAILS2) cl.stats[STAT_NAILS] |= (MSG_ReadByte() << 8); if (bits & SU_ROCKETS2) cl.stats[STAT_ROCKETS] |= (MSG_ReadByte() << 8); if (bits & SU_CELLS2) cl.stats[STAT_CELLS] |= (MSG_ReadByte() << 8); if (bits & SU_WEAPONFRAME2) cl.stats[STAT_WEAPONFRAME] |= (MSG_ReadByte() << 8); if (bits & SU_WEAPONALPHA) cl.viewent.alpha = MSG_ReadByte(); else cl.viewent.alpha = ENTALPHA_DEFAULT; //johnfitz //johnfitz -- lerping //ericw -- this was done before the upper 8 bits of cl.stats[STAT_WEAPON] were filled in, breaking on large maps like zendar.bsp if (cl.viewent.model != cl.model_precache[cl.stats[STAT_WEAPON]]) { cl.viewent.lerpflags |= LERP_RESETANIM; //don't lerp animation across model changes } //johnfitz } /* ===================== CL_NewTranslation ===================== */ void CL_NewTranslation (int slot) { int i, j; int top, bottom; byte *dest, *source; if (slot > cl.maxclients) Sys_Error ("CL_NewTranslation: slot > cl.maxclients"); dest = cl.scores[slot].translations; source = vid.colormap; memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations)); top = cl.scores[slot].colors & 0xf0; bottom = (cl.scores[slot].colors &15)<<4; R_TranslatePlayerSkin (slot); for (i = 0; i < VID_GRADES; i++, dest += 256, source+=256) { if (top < 128) // the artists made some backwards ranges. sigh. memcpy (dest + TOP_RANGE, source + top, 16); else { for (j = 0; j < 16; j++) dest[TOP_RANGE+j] = source[top+15-j]; } if (bottom < 128) memcpy (dest + BOTTOM_RANGE, source + bottom, 16); else { for (j = 0; j < 16; j++) dest[BOTTOM_RANGE+j] = source[bottom+15-j]; } } } /* ===================== CL_ParseStatic ===================== */ void CL_ParseStatic (int version) //johnfitz -- added a parameter { entity_t *ent; int i; i = cl.num_statics; if (i >= MAX_STATIC_ENTITIES) Host_Error ("Too many static entities"); ent = &cl_static_entities[i]; cl.num_statics++; CL_ParseBaseline (ent, version); //johnfitz -- added second parameter // copy it to the current state ent->model = cl.model_precache[ent->baseline.modelindex]; ent->lerpflags |= LERP_RESETANIM; //johnfitz -- lerping ent->frame = ent->baseline.frame; ent->colormap = vid.colormap; ent->skinnum = ent->baseline.skin; ent->effects = ent->baseline.effects; ent->alpha = ent->baseline.alpha; //johnfitz -- alpha VectorCopy (ent->baseline.origin, ent->origin); VectorCopy (ent->baseline.angles, ent->angles); R_AddEfrags (ent); } /* =================== CL_ParseStaticSound =================== */ void CL_ParseStaticSound (int version) //johnfitz -- added argument { vec3_t org; int sound_num, vol, atten; int i; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (cl.protocolflags); //johnfitz -- PROTOCOL_FITZQUAKE if (version == 2) sound_num = MSG_ReadShort (); else sound_num = MSG_ReadByte (); //johnfitz vol = MSG_ReadByte (); atten = MSG_ReadByte (); S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } #define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage (void) { int cmd; int i; const char *str; //johnfitz int total, j, lastcmd; //johnfitz // // if recording demos, copy the message out // if (cl_shownet.value == 1) Con_Printf ("%i ",net_message.cursize); else if (cl_shownet.value == 2) Con_Printf ("------------------\n"); cl.onground = false; // unless the server says otherwise // // parse the message // MSG_BeginReading (); lastcmd = 0; while (1) { if (msg_badread) Host_Error ("CL_ParseServerMessage: Bad server message"); cmd = MSG_ReadByte (); if (cmd == -1) { SHOWNET("END OF MESSAGE"); return; // end of message } // if the high bit of the command byte is set, it is a fast update if (cmd & U_SIGNAL) //johnfitz -- was 128, changed for clarity { SHOWNET("fast update"); CL_ParseUpdate (cmd&127); continue; } SHOWNET(svc_strings[cmd]); // other commands switch (cmd) { default: Host_Error ("Illegible server message, previous was %s", svc_strings[lastcmd]); //johnfitz -- added svc_strings[lastcmd] break; case svc_nop: // Con_Printf ("svc_nop\n"); break; case svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat (); break; case svc_clientdata: CL_ParseClientdata (); //johnfitz -- removed bits parameter, we will read this inside CL_ParseClientdata() break; case svc_version: i = MSG_ReadLong (); //johnfitz -- support multiple protocols if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); cl.protocol = i; //johnfitz break; case svc_disconnect: Host_EndGame ("Server disconnected\n"); case svc_print: Con_Printf ("%s", MSG_ReadString ()); break; case svc_centerprint: //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz break; case svc_stufftext: Cbuf_AddText (MSG_ReadString ()); break; case svc_damage: V_ParseDamage (); break; case svc_serverinfo: CL_ParseServerInfo (); vid.recalc_refdef = true; // leave intermission full screen break; case svc_setangle: for (i=0 ; i<3 ; i++) cl.viewangles[i] = MSG_ReadAngle (cl.protocolflags); break; case svc_setview: cl.viewentity = MSG_ReadShort (); break; case svc_lightstyle: i = MSG_ReadByte (); if (i >= MAX_LIGHTSTYLES) Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); q_strlcpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map); //johnfitz -- save extra info if (cl_lightstyle[i].length) { total = 0; cl_lightstyle[i].peak = 'a'; for (j=0; j<cl_lightstyle[i].length; j++) { total += cl_lightstyle[i].map[j] - 'a'; cl_lightstyle[i].peak = q_max(cl_lightstyle[i].peak, cl_lightstyle[i].map[j]); } cl_lightstyle[i].average = total / cl_lightstyle[i].length + 'a'; } else cl_lightstyle[i].average = cl_lightstyle[i].peak = 'm'; //johnfitz break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_stopsound: i = MSG_ReadShort(); S_StopSound(i>>3, i&7); break; case svc_updatename: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD"); q_strlcpy (cl.scores[i].name, MSG_ReadString(), MAX_SCOREBOARDNAME); break; case svc_updatefrags: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); cl.scores[i].frags = MSG_ReadShort (); break; case svc_updatecolors: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"); cl.scores[i].colors = MSG_ReadByte (); CL_NewTranslation (i); break; case svc_particle: R_ParseParticleEffect (); break; case svc_spawnbaseline: i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i), 1); // johnfitz -- added second parameter break; case svc_spawnstatic: CL_ParseStatic (1); //johnfitz -- added parameter break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_setpause: cl.paused = MSG_ReadByte (); if (cl.paused) { CDAudio_Pause (); BGM_Pause (); } else { CDAudio_Resume (); BGM_Resume (); } break; case svc_signonnum: i = MSG_ReadByte (); if (i <= cls.signon) Host_Error ("Received signon %i when at %i", i, cls.signon); cls.signon = i; //johnfitz -- if signonnum==2, signon packet has been fully parsed, so check for excessive static ents and efrags if (i == 2) { if (cl.num_statics > 128) Con_DWarning ("%i static entities exceeds standard limit of 128 (max = %d).\n", cl.num_statics, MAX_STATIC_ENTITIES); R_CheckEfrags (); } //johnfitz CL_SignonReply (); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; break; case svc_updatestat: i = MSG_ReadByte (); if (i < 0 || i >= MAX_CL_STATS) Sys_Error ("svc_updatestat: %i is invalid", i); cl.stats[i] = MSG_ReadLong ();; break; case svc_spawnstaticsound: CL_ParseStaticSound (1); //johnfitz -- added parameter break; case svc_cdtrack: cl.cdtrack = MSG_ReadByte (); cl.looptrack = MSG_ReadByte (); if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) ) BGM_PlayCDtrack ((byte)cls.forcetrack, true); else BGM_PlayCDtrack ((byte)cl.cdtrack, true); break; case svc_intermission: cl.intermission = 1; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen break; case svc_finale: cl.intermission = 2; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz break; case svc_sellscreen: Cmd_ExecuteString ("help", src_command); break; //johnfitz -- new svc types case svc_skybox: Sky_LoadSkyBox (MSG_ReadString()); break; case svc_bf: Cmd_ExecuteString ("bf", src_command); break; case svc_fog: Fog_ParseServerMessage (); break; case svc_spawnbaseline2: //PROTOCOL_FITZQUAKE i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i), 2); break; case svc_spawnstatic2: //PROTOCOL_FITZQUAKE CL_ParseStatic (2); break; case svc_spawnstaticsound2: //PROTOCOL_FITZQUAKE CL_ParseStaticSound (2); break; //johnfitz } lastcmd = cmd; //johnfitz } } �����������������������������������������quakespasm-0.93.0/Quake/anorms.h��������������������������������������������������������������������0000644�0000000�0000000�00000015443�12407762022�015202� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * anorms.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ { -0.525731, 0.000000, 0.850651 }, { -0.442863, 0.238856, 0.864188 }, { -0.295242, 0.000000, 0.955423 }, { -0.309017, 0.500000, 0.809017 }, { -0.162460, 0.262866, 0.951056 }, { 0.000000, 0.000000, 1.000000 }, { 0.000000, 0.850651, 0.525731 }, { -0.147621, 0.716567, 0.681718 }, { 0.147621, 0.716567, 0.681718 }, { 0.000000, 0.525731, 0.850651 }, { 0.309017, 0.500000, 0.809017 }, { 0.525731, 0.000000, 0.850651 }, { 0.295242, 0.000000, 0.955423 }, { 0.442863, 0.238856, 0.864188 }, { 0.162460, 0.262866, 0.951056 }, { -0.681718, 0.147621, 0.716567 }, { -0.809017, 0.309017, 0.500000 }, { -0.587785, 0.425325, 0.688191 }, { -0.850651, 0.525731, 0.000000 }, { -0.864188, 0.442863, 0.238856 }, { -0.716567, 0.681718, 0.147621 }, { -0.688191, 0.587785, 0.425325 }, { -0.500000, 0.809017, 0.309017 }, { -0.238856, 0.864188, 0.442863 }, { -0.425325, 0.688191, 0.587785 }, { -0.716567, 0.681718, -0.147621 }, { -0.500000, 0.809017, -0.309017 }, { -0.525731, 0.850651, 0.000000 }, { 0.000000, 0.850651, -0.525731 }, { -0.238856, 0.864188, -0.442863 }, { 0.000000, 0.955423, -0.295242 }, { -0.262866, 0.951056, -0.162460 }, { 0.000000, 1.000000, 0.000000 }, { 0.000000, 0.955423, 0.295242 }, { -0.262866, 0.951056, 0.162460 }, { 0.238856, 0.864188, 0.442863 }, { 0.262866, 0.951056, 0.162460 }, { 0.500000, 0.809017, 0.309017 }, { 0.238856, 0.864188, -0.442863 }, { 0.262866, 0.951056, -0.162460 }, { 0.500000, 0.809017, -0.309017 }, { 0.850651, 0.525731, 0.000000 }, { 0.716567, 0.681718, 0.147621 }, { 0.716567, 0.681718, -0.147621 }, { 0.525731, 0.850651, 0.000000 }, { 0.425325, 0.688191, 0.587785 }, { 0.864188, 0.442863, 0.238856 }, { 0.688191, 0.587785, 0.425325 }, { 0.809017, 0.309017, 0.500000 }, { 0.681718, 0.147621, 0.716567 }, { 0.587785, 0.425325, 0.688191 }, { 0.955423, 0.295242, 0.000000 }, { 1.000000, 0.000000, 0.000000 }, { 0.951056, 0.162460, 0.262866 }, { 0.850651, -0.525731, 0.000000 }, { 0.955423, -0.295242, 0.000000 }, { 0.864188, -0.442863, 0.238856 }, { 0.951056, -0.162460, 0.262866 }, { 0.809017, -0.309017, 0.500000 }, { 0.681718, -0.147621, 0.716567 }, { 0.850651, 0.000000, 0.525731 }, { 0.864188, 0.442863, -0.238856 }, { 0.809017, 0.309017, -0.500000 }, { 0.951056, 0.162460, -0.262866 }, { 0.525731, 0.000000, -0.850651 }, { 0.681718, 0.147621, -0.716567 }, { 0.681718, -0.147621, -0.716567 }, { 0.850651, 0.000000, -0.525731 }, { 0.809017, -0.309017, -0.500000 }, { 0.864188, -0.442863, -0.238856 }, { 0.951056, -0.162460, -0.262866 }, { 0.147621, 0.716567, -0.681718 }, { 0.309017, 0.500000, -0.809017 }, { 0.425325, 0.688191, -0.587785 }, { 0.442863, 0.238856, -0.864188 }, { 0.587785, 0.425325, -0.688191 }, { 0.688191, 0.587785, -0.425325 }, { -0.147621, 0.716567, -0.681718 }, { -0.309017, 0.500000, -0.809017 }, { 0.000000, 0.525731, -0.850651 }, { -0.525731, 0.000000, -0.850651 }, { -0.442863, 0.238856, -0.864188 }, { -0.295242, 0.000000, -0.955423 }, { -0.162460, 0.262866, -0.951056 }, { 0.000000, 0.000000, -1.000000 }, { 0.295242, 0.000000, -0.955423 }, { 0.162460, 0.262866, -0.951056 }, { -0.442863, -0.238856, -0.864188 }, { -0.309017, -0.500000, -0.809017 }, { -0.162460, -0.262866, -0.951056 }, { 0.000000, -0.850651, -0.525731 }, { -0.147621, -0.716567, -0.681718 }, { 0.147621, -0.716567, -0.681718 }, { 0.000000, -0.525731, -0.850651 }, { 0.309017, -0.500000, -0.809017 }, { 0.442863, -0.238856, -0.864188 }, { 0.162460, -0.262866, -0.951056 }, { 0.238856, -0.864188, -0.442863 }, { 0.500000, -0.809017, -0.309017 }, { 0.425325, -0.688191, -0.587785 }, { 0.716567, -0.681718, -0.147621 }, { 0.688191, -0.587785, -0.425325 }, { 0.587785, -0.425325, -0.688191 }, { 0.000000, -0.955423, -0.295242 }, { 0.000000, -1.000000, 0.000000 }, { 0.262866, -0.951056, -0.162460 }, { 0.000000, -0.850651, 0.525731 }, { 0.000000, -0.955423, 0.295242 }, { 0.238856, -0.864188, 0.442863 }, { 0.262866, -0.951056, 0.162460 }, { 0.500000, -0.809017, 0.309017 }, { 0.716567, -0.681718, 0.147621 }, { 0.525731, -0.850651, 0.000000 }, { -0.238856, -0.864188, -0.442863 }, { -0.500000, -0.809017, -0.309017 }, { -0.262866, -0.951056, -0.162460 }, { -0.850651, -0.525731, 0.000000 }, { -0.716567, -0.681718, -0.147621 }, { -0.716567, -0.681718, 0.147621 }, { -0.525731, -0.850651, 0.000000 }, { -0.500000, -0.809017, 0.309017 }, { -0.238856, -0.864188, 0.442863 }, { -0.262866, -0.951056, 0.162460 }, { -0.864188, -0.442863, 0.238856 }, { -0.809017, -0.309017, 0.500000 }, { -0.688191, -0.587785, 0.425325 }, { -0.681718, -0.147621, 0.716567 }, { -0.442863, -0.238856, 0.864188 }, { -0.587785, -0.425325, 0.688191 }, { -0.309017, -0.500000, 0.809017 }, { -0.147621, -0.716567, 0.681718 }, { -0.425325, -0.688191, 0.587785 }, { -0.162460, -0.262866, 0.951056 }, { 0.442863, -0.238856, 0.864188 }, { 0.162460, -0.262866, 0.951056 }, { 0.309017, -0.500000, 0.809017 }, { 0.147621, -0.716567, 0.681718 }, { 0.000000, -0.525731, 0.850651 }, { 0.425325, -0.688191, 0.587785 }, { 0.587785, -0.425325, 0.688191 }, { 0.688191, -0.587785, 0.425325 }, { -0.955423, 0.295242, 0.000000 }, { -0.951056, 0.162460, 0.262866 }, { -1.000000, 0.000000, 0.000000 }, { -0.850651, 0.000000, 0.525731 }, { -0.955423, -0.295242, 0.000000 }, { -0.951056, -0.162460, 0.262866 }, { -0.864188, 0.442863, -0.238856 }, { -0.951056, 0.162460, -0.262866 }, { -0.809017, 0.309017, -0.500000 }, { -0.864188, -0.442863, -0.238856 }, { -0.951056, -0.162460, -0.262866 }, { -0.809017, -0.309017, -0.500000 }, { -0.681718, 0.147621, -0.716567 }, { -0.681718, -0.147621, -0.716567 }, { -0.850651, 0.000000, -0.525731 }, { -0.688191, 0.587785, -0.425325 }, { -0.587785, 0.425325, -0.688191 }, { -0.425325, 0.688191, -0.587785 }, { -0.425325, -0.688191, -0.587785 }, { -0.587785, -0.425325, -0.688191 }, { -0.688191, -0.587785, -0.425325 }, �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/zone.c����������������������������������������������������������������������0000644�0000000�0000000�00000050722�12644413102�014643� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // zone.c #include "quakedef.h" #define DYNAMIC_SIZE (4 * 1024 * 1024) // ericw -- was 512KB (64-bit) / 384KB (32-bit) #define ZONEID 0x1d4a11 #define MINFRAGMENT 64 typedef struct memblock_s { int size; // including the header and possibly tiny fragments int tag; // a tag of 0 is a free block int id; // should be ZONEID int pad; // pad to 64 bit boundary struct memblock_s *next, *prev; } memblock_t; typedef struct { int size; // total bytes malloced, including header memblock_t blocklist; // start / end cap for linked list memblock_t *rover; } memzone_t; void Cache_FreeLow (int new_low_hunk); void Cache_FreeHigh (int new_high_hunk); /* ============================================================================== ZONE MEMORY ALLOCATION There is never any space between memblocks, and there will never be two contiguous free memblocks. The rover can be left pointing at a non-empty block The zone calls are pretty much only used for small strings and structures, all big things are allocated on the hunk. ============================================================================== */ static memzone_t *mainzone; /* ======================== Z_Free ======================== */ void Z_Free (void *ptr) { memblock_t *block, *other; if (!ptr) Sys_Error ("Z_Free: NULL pointer"); block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) Sys_Error ("Z_Free: freed a pointer without ZONEID"); if (block->tag == 0) Sys_Error ("Z_Free: freed a freed pointer"); block->tag = 0; // mark as free other = block->prev; if (!other->tag) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == mainzone->rover) mainzone->rover = other; block = other; } other = block->next; if (!other->tag) { // merge the next free block onto the end block->size += other->size; block->next = other->next; block->next->prev = block; if (other == mainzone->rover) mainzone->rover = block; } } static void *Z_TagMalloc (int size, int tag) { int extra; memblock_t *start, *rover, *newblock, *base; if (!tag) Sys_Error ("Z_TagMalloc: tried to use a 0 tag"); // // scan through the block list looking for the first free block // of sufficient size // size += sizeof(memblock_t); // account for size of block header size += 4; // space for memory trash tester size = (size + 7) & ~7; // align to 8-byte boundary base = rover = mainzone->rover; start = base->prev; do { if (rover == start) // scaned all the way around the list return NULL; if (rover->tag) base = rover = rover->next; else rover = rover->next; } while (base->tag || base->size < size); // // found a block big enough // extra = base->size - size; if (extra > MINFRAGMENT) { // there will be a free fragment after the allocated block newblock = (memblock_t *) ((byte *)base + size ); newblock->size = extra; newblock->tag = 0; // free block newblock->prev = base; newblock->id = ZONEID; newblock->next = base->next; newblock->next->prev = newblock; base->next = newblock; base->size = size; } base->tag = tag; // no longer a free block mainzone->rover = base->next; // next allocation will start looking here base->id = ZONEID; // marker for memory trash testing *(int *)((byte *)base + base->size - 4) = ZONEID; return (void *) ((byte *)base + sizeof(memblock_t)); } /* ======================== Z_CheckHeap ======================== */ static void Z_CheckHeap (void) { memblock_t *block; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (!block->tag && !block->next->tag) Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); } } /* ======================== Z_Malloc ======================== */ void *Z_Malloc (int size) { void *buf; Z_CheckHeap (); // DEBUG buf = Z_TagMalloc (size, 1); if (!buf) Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); Q_memset (buf, 0, size); return buf; } /* ======================== Z_Realloc ======================== */ void *Z_Realloc(void *ptr, int size) { int old_size; void *old_ptr; memblock_t *block; if (!ptr) return Z_Malloc (size); block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); if (block->id != ZONEID) Sys_Error ("Z_Realloc: realloced a pointer without ZONEID"); if (block->tag == 0) Sys_Error ("Z_Realloc: realloced a freed pointer"); old_size = block->size; old_size -= (4 + (int)sizeof(memblock_t)); /* see Z_TagMalloc() */ old_ptr = ptr; Z_Free (ptr); ptr = Z_TagMalloc (size, 1); if (!ptr) Sys_Error ("Z_Realloc: failed on allocation of %i bytes", size); if (ptr != old_ptr) memmove (ptr, old_ptr, q_min(old_size, size)); if (old_size < size) memset ((byte *)ptr + old_size, 0, size - old_size); return ptr; } char *Z_Strdup (const char *s) { size_t sz = strlen(s) + 1; char *ptr = (char *) Z_Malloc (sz); memcpy (ptr, s, sz); return ptr; } /* ======================== Z_Print ======================== */ void Z_Print (memzone_t *zone) { memblock_t *block; Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); for (block = zone->blocklist.next ; ; block = block->next) { Con_Printf ("block:%p size:%7i tag:%3i\n", block, block->size, block->tag); if (block->next == &zone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) Con_Printf ("ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) Con_Printf ("ERROR: next block doesn't have proper back link\n"); if (!block->tag && !block->next->tag) Con_Printf ("ERROR: two consecutive free blocks\n"); } } //============================================================================ #define HUNK_SENTINAL 0x1df001ed #define HUNKNAME_LEN 24 typedef struct { int sentinal; int size; // including sizeof(hunk_t), -1 = not allocated char name[HUNKNAME_LEN]; } hunk_t; byte *hunk_base; int hunk_size; int hunk_low_used; int hunk_high_used; qboolean hunk_tempactive; int hunk_tempmark; /* ============== Hunk_Check Run consistancy and sentinal trahing checks ============== */ void Hunk_Check (void) { hunk_t *h; for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) { if (h->sentinal != HUNK_SENTINAL) Sys_Error ("Hunk_Check: trahsed sentinal"); if (h->size < (int) sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) Sys_Error ("Hunk_Check: bad size"); h = (hunk_t *)((byte *)h+h->size); } } /* ============== Hunk_Print If "all" is specified, every single allocation is printed. Otherwise, allocations with the same name will be totaled up before printing. ============== */ void Hunk_Print (qboolean all) { hunk_t *h, *next, *endlow, *starthigh, *endhigh; int count, sum; int totalblocks; char name[HUNKNAME_LEN]; count = 0; sum = 0; totalblocks = 0; h = (hunk_t *)hunk_base; endlow = (hunk_t *)(hunk_base + hunk_low_used); starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); endhigh = (hunk_t *)(hunk_base + hunk_size); Con_Printf (" :%8i total hunk size\n", hunk_size); Con_Printf ("-------------------------\n"); while (1) { // // skip to the high hunk if done with low hunk // if ( h == endlow ) { Con_Printf ("-------------------------\n"); Con_Printf (" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); Con_Printf ("-------------------------\n"); h = starthigh; } // // if totally done, break // if ( h == endhigh ) break; // // run consistancy checks // if (h->sentinal != HUNK_SENTINAL) Sys_Error ("Hunk_Check: trahsed sentinal"); if (h->size < (int) sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) Sys_Error ("Hunk_Check: bad size"); next = (hunk_t *)((byte *)h+h->size); count++; totalblocks++; sum += h->size; // // print the single block // memcpy (name, h->name, HUNKNAME_LEN); if (all) Con_Printf ("%8p :%8i %8s\n",h, h->size, name); // // print the total // if (next == endlow || next == endhigh || strncmp (h->name, next->name, HUNKNAME_LEN - 1)) { if (!all) Con_Printf (" :%8i %8s (TOTAL)\n",sum, name); count = 0; sum = 0; } h = next; } Con_Printf ("-------------------------\n"); Con_Printf ("%8i total blocks\n", totalblocks); } /* =================== Hunk_Print_f -- johnfitz -- console command to call hunk_print =================== */ void Hunk_Print_f (void) { Hunk_Print (false); } /* =================== Hunk_AllocName =================== */ void *Hunk_AllocName (int size, const char *name) { hunk_t *h; #ifdef PARANOID Hunk_Check (); #endif if (size < 0) Sys_Error ("Hunk_Alloc: bad size: %i", size); size = sizeof(hunk_t) + ((size+15)&~15); if (hunk_size - hunk_low_used - hunk_high_used < size) Sys_Error ("Hunk_Alloc: failed on %i bytes",size); h = (hunk_t *)(hunk_base + hunk_low_used); hunk_low_used += size; Cache_FreeLow (hunk_low_used); memset (h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; q_strlcpy (h->name, name, HUNKNAME_LEN); return (void *)(h+1); } /* =================== Hunk_Alloc =================== */ void *Hunk_Alloc (int size) { return Hunk_AllocName (size, "unknown"); } int Hunk_LowMark (void) { return hunk_low_used; } void Hunk_FreeToLowMark (int mark) { if (mark < 0 || mark > hunk_low_used) Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); memset (hunk_base + mark, 0, hunk_low_used - mark); hunk_low_used = mark; } int Hunk_HighMark (void) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark (hunk_tempmark); } return hunk_high_used; } void Hunk_FreeToHighMark (int mark) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark (hunk_tempmark); } if (mark < 0 || mark > hunk_high_used) Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); hunk_high_used = mark; } /* =================== Hunk_HighAllocName =================== */ void *Hunk_HighAllocName (int size, const char *name) { hunk_t *h; if (size < 0) Sys_Error ("Hunk_HighAllocName: bad size: %i", size); if (hunk_tempactive) { Hunk_FreeToHighMark (hunk_tempmark); hunk_tempactive = false; } #ifdef PARANOID Hunk_Check (); #endif size = sizeof(hunk_t) + ((size+15)&~15); if (hunk_size - hunk_low_used - hunk_high_used < size) { Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); return NULL; } hunk_high_used += size; Cache_FreeHigh (hunk_high_used); h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); memset (h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; q_strlcpy (h->name, name, HUNKNAME_LEN); return (void *)(h+1); } /* ================= Hunk_TempAlloc Return space from the top of the hunk ================= */ void *Hunk_TempAlloc (int size) { void *buf; size = (size+15)&~15; if (hunk_tempactive) { Hunk_FreeToHighMark (hunk_tempmark); hunk_tempactive = false; } hunk_tempmark = Hunk_HighMark (); buf = Hunk_HighAllocName (size, "temp"); hunk_tempactive = true; return buf; } char *Hunk_Strdup (const char *s, const char *name) { size_t sz = strlen(s) + 1; char *ptr = (char *) Hunk_AllocName (sz, name); memcpy (ptr, s, sz); return ptr; } /* =============================================================================== CACHE MEMORY =============================================================================== */ #define CACHENAME_LEN 32 typedef struct cache_system_s { int size; // including this header cache_user_t *user; char name[CACHENAME_LEN]; struct cache_system_s *prev, *next; struct cache_system_s *lru_prev, *lru_next; // for LRU flushing } cache_system_t; cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); cache_system_t cache_head; /* =========== Cache_Move =========== */ void Cache_Move ( cache_system_t *c) { cache_system_t *new_cs; // we are clearing up space at the bottom, so only allocate it late new_cs = Cache_TryAlloc (c->size, true); if (new_cs) { // Con_Printf ("cache_move ok\n"); Q_memcpy ( new_cs+1, c+1, c->size - sizeof(cache_system_t) ); new_cs->user = c->user; Q_memcpy (new_cs->name, c->name, sizeof(new_cs->name)); Cache_Free (c->user, false); //johnfitz -- added second argument new_cs->user->data = (void *)(new_cs+1); } else { // Con_Printf ("cache_move failed\n"); Cache_Free (c->user, true); // tough luck... //johnfitz -- added second argument } } /* ============ Cache_FreeLow Throw things out until the hunk can be expanded to the given point ============ */ void Cache_FreeLow (int new_low_hunk) { cache_system_t *c; while (1) { c = cache_head.next; if (c == &cache_head) return; // nothing in cache at all if ((byte *)c >= hunk_base + new_low_hunk) return; // there is space to grow the hunk Cache_Move ( c ); // reclaim the space } } /* ============ Cache_FreeHigh Throw things out until the hunk can be expanded to the given point ============ */ void Cache_FreeHigh (int new_high_hunk) { cache_system_t *c, *prev; prev = NULL; while (1) { c = cache_head.prev; if (c == &cache_head) return; // nothing in cache at all if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) return; // there is space to grow the hunk if (c == prev) Cache_Free (c->user, true); // didn't move out of the way //johnfitz -- added second argument else { Cache_Move (c); // try to move it prev = c; } } } void Cache_UnlinkLRU (cache_system_t *cs) { if (!cs->lru_next || !cs->lru_prev) Sys_Error ("Cache_UnlinkLRU: NULL link"); cs->lru_next->lru_prev = cs->lru_prev; cs->lru_prev->lru_next = cs->lru_next; cs->lru_prev = cs->lru_next = NULL; } void Cache_MakeLRU (cache_system_t *cs) { if (cs->lru_next || cs->lru_prev) Sys_Error ("Cache_MakeLRU: active link"); cache_head.lru_next->lru_prev = cs; cs->lru_next = cache_head.lru_next; cs->lru_prev = &cache_head; cache_head.lru_next = cs; } /* ============ Cache_TryAlloc Looks for a free block of memory between the high and low hunk marks Size should already include the header and padding ============ */ cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) { cache_system_t *cs, *new_cs; // is the cache completely empty? if (!nobottom && cache_head.prev == &cache_head) { if (hunk_size - hunk_high_used - hunk_low_used < size) Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); new_cs = (cache_system_t *) (hunk_base + hunk_low_used); memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; cache_head.prev = cache_head.next = new_cs; new_cs->prev = new_cs->next = &cache_head; Cache_MakeLRU (new_cs); return new_cs; } // search from the bottom up for space new_cs = (cache_system_t *) (hunk_base + hunk_low_used); cs = cache_head.next; do { if (!nobottom || cs != cache_head.next) { if ( (byte *)cs - (byte *)new_cs >= size) { // found space memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; new_cs->next = cs; new_cs->prev = cs->prev; cs->prev->next = new_cs; cs->prev = new_cs; Cache_MakeLRU (new_cs); return new_cs; } } // continue looking new_cs = (cache_system_t *)((byte *)cs + cs->size); cs = cs->next; } while (cs != &cache_head); // try to allocate one at the very end if ( hunk_base + hunk_size - hunk_high_used - (byte *)new_cs >= size) { memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; new_cs->next = &cache_head; new_cs->prev = cache_head.prev; cache_head.prev->next = new_cs; cache_head.prev = new_cs; Cache_MakeLRU (new_cs); return new_cs; } return NULL; // couldn't allocate } /* ============ Cache_Flush Throw everything out, so new data will be demand cached ============ */ void Cache_Flush (void) { while (cache_head.next != &cache_head) Cache_Free ( cache_head.next->user, true); // reclaim the space //johnfitz -- added second argument } /* ============ Cache_Print ============ */ void Cache_Print (void) { cache_system_t *cd; for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) { Con_Printf ("%8i : %s\n", cd->size, cd->name); } } /* ============ Cache_Report ============ */ void Cache_Report (void) { Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); } /* ============ Cache_Init ============ */ void Cache_Init (void) { cache_head.next = cache_head.prev = &cache_head; cache_head.lru_next = cache_head.lru_prev = &cache_head; Cmd_AddCommand ("flush", Cache_Flush); } /* ============== Cache_Free Frees the memory and removes it from the LRU list ============== */ void Cache_Free (cache_user_t *c, qboolean freetextures) //johnfitz -- added second argument { cache_system_t *cs; if (!c->data) Sys_Error ("Cache_Free: not allocated"); cs = ((cache_system_t *)c->data) - 1; cs->prev->next = cs->next; cs->next->prev = cs->prev; cs->next = cs->prev = NULL; c->data = NULL; Cache_UnlinkLRU (cs); //johnfitz -- if a model becomes uncached, free the gltextures. This only works //becuase the cache_user_t is the last component of the qmodel_t struct. Should //fail harmlessly if *c is actually part of an sfx_t struct. I FEEL DIRTY if (freetextures) TexMgr_FreeTexturesForOwner ((qmodel_t *)(c + 1) - 1); } /* ============== Cache_Check ============== */ void *Cache_Check (cache_user_t *c) { cache_system_t *cs; if (!c->data) return NULL; cs = ((cache_system_t *)c->data) - 1; // move to head of LRU Cache_UnlinkLRU (cs); Cache_MakeLRU (cs); return c->data; } /* ============== Cache_Alloc ============== */ void *Cache_Alloc (cache_user_t *c, int size, const char *name) { cache_system_t *cs; if (c->data) Sys_Error ("Cache_Alloc: allready allocated"); if (size <= 0) Sys_Error ("Cache_Alloc: size %i", size); size = (size + sizeof(cache_system_t) + 15) & ~15; // find memory for it while (1) { cs = Cache_TryAlloc (size, false); if (cs) { q_strlcpy (cs->name, name, CACHENAME_LEN); c->data = (void *)(cs+1); cs->user = c; break; } // free the least recently used cahedat if (cache_head.lru_prev == &cache_head) Sys_Error ("Cache_Alloc: out of memory"); // not enough memory at all Cache_Free (cache_head.lru_prev->user, true); //johnfitz -- added second argument } return Cache_Check (c); } //============================================================================ static void Memory_InitZone (memzone_t *zone, int size) { memblock_t *block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); zone->blocklist.tag = 1; // in use block zone->blocklist.id = 0; zone->blocklist.size = 0; zone->rover = block; block->prev = block->next = &zone->blocklist; block->tag = 0; // free block block->id = ZONEID; block->size = size - sizeof(memzone_t); } /* ======================== Memory_Init ======================== */ void Memory_Init (void *buf, int size) { int p; int zonesize = DYNAMIC_SIZE; hunk_base = (byte *) buf; hunk_size = size; hunk_low_used = 0; hunk_high_used = 0; Cache_Init (); p = COM_CheckParm ("-zone"); if (p) { if (p < com_argc-1) zonesize = Q_atoi (com_argv[p+1]) * 1024; else Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); } mainzone = (memzone_t *) Hunk_AllocName (zonesize, "zone" ); Memory_InitZone (mainzone, zonesize); Cmd_AddCommand ("hunk_print", Hunk_Print_f); //johnfitz } ����������������������������������������������quakespasm-0.93.0/Quake/progdefs.q1�����������������������������������������������������������������0000644�0000000�0000000�00000004627�11336354077�015617� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� /* file generated by qcc, do not modify */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; float deathmatch; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float parm1; float parm2; float parm3; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientDisconnect; func_t SetNewParms; func_t SetChangeParms; } globalvars_t; typedef struct { float modelindex; vec3_t absmin; vec3_t absmax; float ltime; float movetype; float solid; vec3_t origin; vec3_t oldorigin; vec3_t velocity; vec3_t angles; vec3_t avelocity; vec3_t punchangle; string_t classname; string_t model; float frame; float skin; float effects; vec3_t mins; vec3_t maxs; vec3_t size; func_t touch; func_t use; func_t think; func_t blocked; float nextthink; int groundentity; float health; float frags; float weapon; string_t weaponmodel; float weaponframe; float currentammo; float ammo_shells; float ammo_nails; float ammo_rockets; float ammo_cells; float items; float takedamage; int chain; float deadflag; vec3_t view_ofs; float button0; float button1; float button2; float impulse; float fixangle; vec3_t v_angle; float idealpitch; string_t netname; int enemy; float flags; float colormap; float team; float max_health; float teleport_time; float armortype; float armorvalue; float waterlevel; float watertype; float ideal_yaw; float yaw_speed; int aiment; int goalentity; float spawnflags; string_t target; string_t targetname; float dmg_take; float dmg_save; int dmg_inflictor; int owner; vec3_t movedir; string_t message; float sounds; string_t noise; string_t noise1; string_t noise2; string_t noise3; } entvars_t; #define PROGHEADER_CRC 5927 ���������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/common.c��������������������������������������������������������������������0000644�0000000�0000000�00000142721�13156064741�015173� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // common.c -- misc functions used in client and server #include "quakedef.h" #include "q_ctype.h" #include <errno.h> static char *largv[MAX_NUM_ARGVS + 1]; static char argvdummy[] = " "; int safemode; cvar_t registered = {"registered","1",CVAR_ROM}; /* set to correct value in COM_CheckRegistered() */ cvar_t cmdline = {"cmdline","",CVAR_ROM/*|CVAR_SERVERINFO*/}; /* sending cmdline upon CCREQ_RULE_INFO is evil */ static qboolean com_modified; // set true if using non-id files qboolean fitzmode; static void COM_Path_f (void); // if a packfile directory differs from this, it is assumed to be hacked #define PAK0_COUNT 339 /* id1/pak0.pak - v1.0x */ #define PAK0_CRC_V100 13900 /* id1/pak0.pak - v1.00 */ #define PAK0_CRC_V101 62751 /* id1/pak0.pak - v1.01 */ #define PAK0_CRC_V106 32981 /* id1/pak0.pak - v1.06 */ #define PAK0_CRC (PAK0_CRC_V106) #define PAK0_COUNT_V091 308 /* id1/pak0.pak - v0.91/0.92, not supported */ #define PAK0_CRC_V091 28804 /* id1/pak0.pak - v0.91/0.92, not supported */ char com_token[1024]; int com_argc; char **com_argv; #define CMDLINE_LENGTH 256 /* johnfitz -- mirrored in cmd.c */ char com_cmdline[CMDLINE_LENGTH]; qboolean standard_quake = true, rogue, hipnotic; // this graphic needs to be in the pak file to use registered features static unsigned short pop[] = { 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000, 0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000, 0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600, 0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563, 0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564, 0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564, 0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563, 0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500, 0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200, 0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000, 0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000 }; /* All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is only used during filesystem initialization. The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory specified, when a file is found by the normal search path, it will be mirrored into the cache directory, then opened there. FIXME: The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently. This could be used to add a "-sspeed 22050" for the high quality sound edition. Because they are added at the end, they will not override an explicit setting on the original command line. */ //============================================================================ // ClearLink is used for new headnodes void ClearLink (link_t *l) { l->prev = l->next = l; } void RemoveLink (link_t *l) { l->next->prev = l->prev; l->prev->next = l->next; } void InsertLinkBefore (link_t *l, link_t *before) { l->next = before; l->prev = before->prev; l->prev->next = l; l->next->prev = l; } void InsertLinkAfter (link_t *l, link_t *after) { l->next = after->next; l->prev = after; l->prev->next = l; l->next->prev = l; } /* ============================================================================ LIBRARY REPLACEMENT FUNCTIONS ============================================================================ */ int q_strcasecmp(const char * s1, const char * s2) { const char * p1 = s1; const char * p2 = s2; char c1, c2; if (p1 == p2) return 0; do { c1 = q_tolower (*p1++); c2 = q_tolower (*p2++); if (c1 == '\0') break; } while (c1 == c2); return (int)(c1 - c2); } int q_strncasecmp(const char *s1, const char *s2, size_t n) { const char * p1 = s1; const char * p2 = s2; char c1, c2; if (p1 == p2 || n == 0) return 0; do { c1 = q_tolower (*p1++); c2 = q_tolower (*p2++); if (c1 == '\0' || c1 != c2) break; } while (--n > 0); return (int)(c1 - c2); } //spike -- grabbed this from fte, because its useful to me char *q_strcasestr(const char *haystack, const char *needle) { int c1, c2, c2f; int i; c2f = *needle; if (c2f >= 'a' && c2f <= 'z') c2f -= ('a' - 'A'); if (!c2f) return (char*)haystack; while (1) { c1 = *haystack; if (!c1) return NULL; if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A'); if (c1 == c2f) { for (i = 1; ; i++) { c1 = haystack[i]; c2 = needle[i]; if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A'); if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A'); if (!c2) return (char*)haystack; //end of needle means we found a complete match if (!c1) //end of haystack means we can't possibly find needle in it any more return NULL; if (c1 != c2) //mismatch means no match starting at haystack[0] break; } } haystack++; } return NULL; //didn't find it } char *q_strlwr (char *str) { char *c; c = str; while (*c) { *c = q_tolower(*c); c++; } return str; } char *q_strupr (char *str) { char *c; c = str; while (*c) { *c = q_toupper(*c); c++; } return str; } /* platform dependant (v)snprintf function names: */ #if defined(_WIN32) #define snprintf_func _snprintf #define vsnprintf_func _vsnprintf #else #define snprintf_func snprintf #define vsnprintf_func vsnprintf #endif int q_vsnprintf(char *str, size_t size, const char *format, va_list args) { int ret; ret = vsnprintf_func (str, size, format, args); if (ret < 0) ret = (int)size; if (size == 0) /* no buffer */ return ret; if ((size_t)ret >= size) str[size - 1] = '\0'; return ret; } int q_snprintf (char *str, size_t size, const char *format, ...) { int ret; va_list argptr; va_start (argptr, format); ret = q_vsnprintf (str, size, format, argptr); va_end (argptr); return ret; } void Q_memset (void *dest, int fill, size_t count) { size_t i; if ( (((size_t)dest | count) & 3) == 0) { count >>= 2; fill = fill | (fill<<8) | (fill<<16) | (fill<<24); for (i = 0; i < count; i++) ((int *)dest)[i] = fill; } else for (i = 0; i < count; i++) ((byte *)dest)[i] = fill; } void Q_memcpy (void *dest, const void *src, size_t count) { size_t i; if (( ( (size_t)dest | (size_t)src | count) & 3) == 0 ) { count >>= 2; for (i = 0; i < count; i++) ((int *)dest)[i] = ((int *)src)[i]; } else for (i = 0; i < count; i++) ((byte *)dest)[i] = ((byte *)src)[i]; } int Q_memcmp (const void *m1, const void *m2, size_t count) { while(count) { count--; if (((byte *)m1)[count] != ((byte *)m2)[count]) return -1; } return 0; } void Q_strcpy (char *dest, const char *src) { while (*src) { *dest++ = *src++; } *dest++ = 0; } void Q_strncpy (char *dest, const char *src, int count) { while (*src && count--) { *dest++ = *src++; } if (count) *dest++ = 0; } int Q_strlen (const char *str) { int count; count = 0; while (str[count]) count++; return count; } char *Q_strrchr(const char *s, char c) { int len = Q_strlen(s); s += len; while (len--) { if (*--s == c) return (char *)s; } return NULL; } void Q_strcat (char *dest, const char *src) { dest += Q_strlen(dest); Q_strcpy (dest, src); } int Q_strcmp (const char *s1, const char *s2) { while (1) { if (*s1 != *s2) return -1; // strings not equal if (!*s1) return 0; // strings are equal s1++; s2++; } return -1; } int Q_strncmp (const char *s1, const char *s2, int count) { while (1) { if (!count--) return 0; if (*s1 != *s2) return -1; // strings not equal if (!*s1) return 0; // strings are equal s1++; s2++; } return -1; } int Q_atoi (const char *str) { int val; int sign; int c; if (*str == '-') { sign = -1; str++; } else sign = 1; val = 0; // // check for hex // if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) { str += 2; while (1) { c = *str++; if (c >= '0' && c <= '9') val = (val<<4) + c - '0'; else if (c >= 'a' && c <= 'f') val = (val<<4) + c - 'a' + 10; else if (c >= 'A' && c <= 'F') val = (val<<4) + c - 'A' + 10; else return val*sign; } } // // check for character // if (str[0] == '\'') { return sign * str[1]; } // // assume decimal // while (1) { c = *str++; if (c <'0' || c > '9') return val*sign; val = val*10 + c - '0'; } return 0; } float Q_atof (const char *str) { double val; int sign; int c; int decimal, total; if (*str == '-') { sign = -1; str++; } else sign = 1; val = 0; // // check for hex // if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) { str += 2; while (1) { c = *str++; if (c >= '0' && c <= '9') val = (val*16) + c - '0'; else if (c >= 'a' && c <= 'f') val = (val*16) + c - 'a' + 10; else if (c >= 'A' && c <= 'F') val = (val*16) + c - 'A' + 10; else return val*sign; } } // // check for character // if (str[0] == '\'') { return sign * str[1]; } // // assume decimal // decimal = -1; total = 0; while (1) { c = *str++; if (c == '.') { decimal = total; continue; } if (c <'0' || c > '9') break; val = val*10 + c - '0'; total++; } if (decimal == -1) return val*sign; while (total > decimal) { val /= 10; total--; } return val*sign; } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ qboolean host_bigendian; short (*BigShort) (short l); short (*LittleShort) (short l); int (*BigLong) (int l); int (*LittleLong) (int l); float (*BigFloat) (float l); float (*LittleFloat) (float l); short ShortSwap (short l) { byte b1, b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } short ShortNoSwap (short l) { return l; } int LongSwap (int l) { byte b1, b2, b3, b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } int LongNoSwap (int l) { return l; } float FloatSwap (float f) { union { float f; byte b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } float FloatNoSwap (float f) { return f; } /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ // // writing functions // void MSG_WriteChar (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < -128 || c > 127) Sys_Error ("MSG_WriteChar: range error"); #endif buf = (byte *) SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteByte (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < 0 || c > 255) Sys_Error ("MSG_WriteByte: range error"); #endif buf = (byte *) SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteShort (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < ((short)0x8000) || c > (short)0x7fff) Sys_Error ("MSG_WriteShort: range error"); #endif buf = (byte *) SZ_GetSpace (sb, 2); buf[0] = c&0xff; buf[1] = c>>8; } void MSG_WriteLong (sizebuf_t *sb, int c) { byte *buf; buf = (byte *) SZ_GetSpace (sb, 4); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; buf[2] = (c>>16)&0xff; buf[3] = c>>24; } void MSG_WriteFloat (sizebuf_t *sb, float f) { union { float f; int l; } dat; dat.f = f; dat.l = LittleLong (dat.l); SZ_Write (sb, &dat.l, 4); } void MSG_WriteString (sizebuf_t *sb, const char *s) { if (!s) SZ_Write (sb, "", 1); else SZ_Write (sb, s, Q_strlen(s)+1); } //johnfitz -- original behavior, 13.3 fixed point coords, max range +-4096 void MSG_WriteCoord16 (sizebuf_t *sb, float f) { MSG_WriteShort (sb, Q_rint(f*8)); } //johnfitz -- 16.8 fixed point coords, max range +-32768 void MSG_WriteCoord24 (sizebuf_t *sb, float f) { MSG_WriteShort (sb, f); MSG_WriteByte (sb, (int)(f*255)%255); } //johnfitz -- 32-bit float coords void MSG_WriteCoord32f (sizebuf_t *sb, float f) { MSG_WriteFloat (sb, f); } void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags) { if (flags & PRFL_FLOATCOORD) MSG_WriteFloat (sb, f); else if (flags & PRFL_INT32COORD) MSG_WriteLong (sb, Q_rint (f * 16)); else if (flags & PRFL_24BITCOORD) MSG_WriteCoord24 (sb, f); else MSG_WriteCoord16 (sb, f); } void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags) { if (flags & PRFL_FLOATANGLE) MSG_WriteFloat (sb, f); else if (flags & PRFL_SHORTANGLE) MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535); else MSG_WriteByte (sb, Q_rint(f * 256.0 / 360.0) & 255); //johnfitz -- use Q_rint instead of (int) } } //johnfitz -- for PROTOCOL_FITZQUAKE void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags) { if (flags & PRFL_FLOATANGLE) MSG_WriteFloat (sb, f); else MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535); } //johnfitz // // reading functions // int msg_readcount; qboolean msg_badread; void MSG_BeginReading (void) { msg_readcount = 0; msg_badread = false; } // returns -1 and sets msg_badread if no more characters are available int MSG_ReadChar (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (signed char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadByte (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (unsigned char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadShort (void) { int c; if (msg_readcount+2 > net_message.cursize) { msg_badread = true; return -1; } c = (short)(net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8)); msg_readcount += 2; return c; } int MSG_ReadLong (void) { int c; if (msg_readcount+4 > net_message.cursize) { msg_badread = true; return -1; } c = net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8) + (net_message.data[msg_readcount+2]<<16) + (net_message.data[msg_readcount+3]<<24); msg_readcount += 4; return c; } float MSG_ReadFloat (void) { union { byte b[4]; float f; int l; } dat; dat.b[0] = net_message.data[msg_readcount]; dat.b[1] = net_message.data[msg_readcount+1]; dat.b[2] = net_message.data[msg_readcount+2]; dat.b[3] = net_message.data[msg_readcount+3]; msg_readcount += 4; dat.l = LittleLong (dat.l); return dat.f; } const char *MSG_ReadString (void) { static char string[2048]; int c; size_t l; l = 0; do { c = MSG_ReadByte (); if (c == -1 || c == 0) break; string[l] = c; l++; } while (l < sizeof(string) - 1); string[l] = 0; return string; } //johnfitz -- original behavior, 13.3 fixed point coords, max range +-4096 float MSG_ReadCoord16 (void) { return MSG_ReadShort() * (1.0/8); } //johnfitz -- 16.8 fixed point coords, max range +-32768 float MSG_ReadCoord24 (void) { return MSG_ReadShort() + MSG_ReadByte() * (1.0/255); } //johnfitz -- 32-bit float coords float MSG_ReadCoord32f (void) { return MSG_ReadFloat(); } float MSG_ReadCoord (unsigned int flags) { if (flags & PRFL_FLOATCOORD) return MSG_ReadFloat (); else if (flags & PRFL_INT32COORD) return MSG_ReadLong () * (1.0 / 16.0); else if (flags & PRFL_24BITCOORD) return MSG_ReadCoord24 (); else return MSG_ReadCoord16 (); } float MSG_ReadAngle (unsigned int flags) { if (flags & PRFL_FLOATANGLE) return MSG_ReadFloat (); else if (flags & PRFL_SHORTANGLE) return MSG_ReadShort () * (360.0 / 65536); else return MSG_ReadChar () * (360.0 / 256); } //johnfitz -- for PROTOCOL_FITZQUAKE float MSG_ReadAngle16 (unsigned int flags) { if (flags & PRFL_FLOATANGLE) return MSG_ReadFloat (); // make sure else return MSG_ReadShort () * (360.0 / 65536); } //johnfitz //=========================================================================== void SZ_Alloc (sizebuf_t *buf, int startsize) { if (startsize < 256) startsize = 256; buf->data = (byte *) Hunk_AllocName (startsize, "sizebuf"); buf->maxsize = startsize; buf->cursize = 0; } void SZ_Free (sizebuf_t *buf) { // Z_Free (buf->data); // buf->data = NULL; // buf->maxsize = 0; buf->cursize = 0; } void SZ_Clear (sizebuf_t *buf) { buf->cursize = 0; } void *SZ_GetSpace (sizebuf_t *buf, int length) { void *data; if (buf->cursize + length > buf->maxsize) { if (!buf->allowoverflow) Host_Error ("SZ_GetSpace: overflow without allowoverflow set"); // ericw -- made Host_Error to be less annoying if (length > buf->maxsize) Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); buf->overflowed = true; Con_Printf ("SZ_GetSpace: overflow"); SZ_Clear (buf); } data = buf->data + buf->cursize; buf->cursize += length; return data; } void SZ_Write (sizebuf_t *buf, const void *data, int length) { Q_memcpy (SZ_GetSpace(buf,length),data,length); } void SZ_Print (sizebuf_t *buf, const char *data) { int len = Q_strlen(data) + 1; if (buf->data[buf->cursize-1]) { /* no trailing 0 */ Q_memcpy ((byte *)SZ_GetSpace(buf, len ) , data, len); } else { /* write over trailing 0 */ Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1, data, len); } } //============================================================================ /* ============ COM_SkipPath ============ */ const char *COM_SkipPath (const char *pathname) { const char *last; last = pathname; while (*pathname) { if (*pathname == '/') last = pathname + 1; pathname++; } return last; } /* ============ COM_StripExtension ============ */ void COM_StripExtension (const char *in, char *out, size_t outsize) { int length; if (!*in) { *out = '\0'; return; } if (in != out) /* copy when not in-place editing */ q_strlcpy (out, in, outsize); length = (int)strlen(out) - 1; while (length > 0 && out[length] != '.') { --length; if (out[length] == '/' || out[length] == '\\') return; /* no extension */ } if (length > 0) out[length] = '\0'; } /* ============ COM_FileGetExtension - doesn't return NULL ============ */ const char *COM_FileGetExtension (const char *in) { const char *src; size_t len; len = strlen(in); if (len < 2) /* nothing meaningful */ return ""; src = in + len - 1; while (src != in && src[-1] != '.') src--; if (src == in || strchr(src, '/') != NULL || strchr(src, '\\') != NULL) return ""; /* no extension, or parent directory has a dot */ return src; } /* ============ COM_ExtractExtension ============ */ void COM_ExtractExtension (const char *in, char *out, size_t outsize) { const char *ext = COM_FileGetExtension (in); if (! *ext) *out = '\0'; else q_strlcpy (out, ext, outsize); } /* ============ COM_FileBase take 'somedir/otherdir/filename.ext', write only 'filename' to the output ============ */ void COM_FileBase (const char *in, char *out, size_t outsize) { const char *dot, *slash, *s; s = in; slash = in; dot = NULL; while (*s) { if (*s == '/') slash = s + 1; if (*s == '.') dot = s; s++; } if (dot == NULL) dot = s; if (dot - slash < 2) q_strlcpy (out, "?model?", outsize); else { size_t len = dot - slash; if (len >= outsize) len = outsize - 1; memcpy (out, slash, len); out[len] = '\0'; } } /* ================== COM_DefaultExtension if path doesn't have a .EXT, append extension (extension should include the leading ".") ================== */ #if 0 /* can be dangerous */ void COM_DefaultExtension (char *path, const char *extension, size_t len) { char *src; if (!*path) return; src = path + strlen(path) - 1; while (*src != '/' && *src != '\\' && src != path) { if (*src == '.') return; // it has an extension src--; } q_strlcat(path, extension, len); } #endif /* ================== COM_AddExtension if path extension doesn't match .EXT, append it (extension should include the leading ".") ================== */ void COM_AddExtension (char *path, const char *extension, size_t len) { if (strcmp(COM_FileGetExtension(path), extension + 1) != 0) q_strlcat(path, extension, len); } /* ============== COM_Parse Parse a token out of a string ============== */ const char *COM_Parse (const char *data) { int c; int len; len = 0; com_token[0] = 0; if (!data) return NULL; // skip whitespace skipwhite: while ((c = *data) <= ' ') { if (c == 0) return NULL; // end of file data++; } // skip // comments if (c == '/' && data[1] == '/') { while (*data && *data != '\n') data++; goto skipwhite; } // skip /*..*/ comments if (c == '/' && data[1] == '*') { data += 2; while (*data && !(*data == '*' && data[1] == '/')) data++; if (*data) data += 2; goto skipwhite; } // handle quoted strings specially if (c == '\"') { data++; while (1) { if ((c = *data) != 0) ++data; if (c == '\"' || !c) { com_token[len] = 0; return data; } com_token[len] = c; len++; } } // parse single characters if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\'' || c == ':') { com_token[len] = c; len++; com_token[len] = 0; return data+1; } // parse a regular word do { com_token[len] = c; data++; len++; c = *data; /* commented out the check for ':' so that ip:port works */ if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\''/* || c == ':' */) break; } while (c > 32); com_token[len] = 0; return data; } /* ================ COM_CheckParm Returns the position (1 to argc-1) in the program's argument list where the given parameter apears, or 0 if not present ================ */ int COM_CheckParm (const char *parm) { int i; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; // NEXTSTEP sometimes clears appkit vars. if (!Q_strcmp (parm,com_argv[i])) return i; } return 0; } /* ================ COM_CheckRegistered Looks for the pop.txt file and verifies it. Sets the "registered" cvar. Immediately exits out if an alternate game was attempted to be started without being registered. ================ */ static void COM_CheckRegistered (void) { int h; unsigned short check[128]; int i; COM_OpenFile("gfx/pop.lmp", &h, NULL); if (h == -1) { Cvar_SetROM ("registered", "0"); Con_Printf ("Playing shareware version.\n"); if (com_modified) Sys_Error ("You must have the registered version to use modified games.\n\n" "Basedir is: %s\n\n" "Check that this has an " GAMENAME " subdirectory containing pak0.pak and pak1.pak, " "or use the -basedir command-line option to specify another directory.", com_basedir); return; } Sys_FileRead (h, check, sizeof(check)); COM_CloseFile (h); for (i = 0; i < 128; i++) { if (pop[i] != (unsigned short)BigShort (check[i])) Sys_Error ("Corrupted data file."); } for (i = 0; com_cmdline[i]; i++) { if (com_cmdline[i]!= ' ') break; } Cvar_SetROM ("cmdline", &com_cmdline[i]); Cvar_SetROM ("registered", "1"); Con_Printf ("Playing registered version.\n"); } /* ================ COM_InitArgv ================ */ void COM_InitArgv (int argc, char **argv) { int i, j, n; // reconstitute the command line for the cmdline externally visible cvar n = 0; for (j = 0; (j<MAX_NUM_ARGVS) && (j< argc); j++) { i = 0; while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i]) { com_cmdline[n++] = argv[j][i++]; } if (n < (CMDLINE_LENGTH - 1)) com_cmdline[n++] = ' '; else break; } if (n > 0 && com_cmdline[n-1] == ' ') com_cmdline[n-1] = 0; //johnfitz -- kill the trailing space Con_Printf("Command line: %s\n", com_cmdline); for (com_argc = 0; (com_argc < MAX_NUM_ARGVS) && (com_argc < argc); com_argc++) { largv[com_argc] = argv[com_argc]; if (!Q_strcmp ("-safe", argv[com_argc])) safemode = 1; } largv[com_argc] = argvdummy; com_argv = largv; if (COM_CheckParm ("-rogue")) { rogue = true; standard_quake = false; } if (COM_CheckParm ("-hipnotic") || COM_CheckParm ("-quoth")) //johnfitz -- "-quoth" support { hipnotic = true; standard_quake = false; } } /* ================ Test_f -- johnfitz ================ */ #ifdef _DEBUG static void FitzTest_f (void) { } #endif /* ================ COM_Init ================ */ void COM_Init (void) { int i = 0x12345678; /* U N I X */ /* BE_ORDER: 12 34 56 78 U N I X LE_ORDER: 78 56 34 12 X I N U PDP_ORDER: 34 12 78 56 N U X I */ if ( *(char *)&i == 0x12 ) host_bigendian = true; else if ( *(char *)&i == 0x78 ) host_bigendian = false; else /* if ( *(char *)&i == 0x34 ) */ Sys_Error ("Unsupported endianism."); if (host_bigendian) { BigShort = ShortNoSwap; LittleShort = ShortSwap; BigLong = LongNoSwap; LittleLong = LongSwap; BigFloat = FloatNoSwap; LittleFloat = FloatSwap; } else /* assumed LITTLE_ENDIAN. */ { BigShort = ShortSwap; LittleShort = ShortNoSwap; BigLong = LongSwap; LittleLong = LongNoSwap; BigFloat = FloatSwap; LittleFloat = FloatNoSwap; } if (COM_CheckParm("-fitz")) fitzmode = true; #ifdef _DEBUG Cmd_AddCommand ("fitztest", FitzTest_f); //johnfitz #endif } /* ============ va does a varargs printf into a temp buffer. cycles between 4 different static buffers. the number of buffers cycled is defined in VA_NUM_BUFFS. FIXME: make this buffer size safe someday ============ */ #define VA_NUM_BUFFS 4 #define VA_BUFFERLEN 1024 static char *get_va_buffer(void) { static char va_buffers[VA_NUM_BUFFS][VA_BUFFERLEN]; static int buffer_idx = 0; buffer_idx = (buffer_idx + 1) & (VA_NUM_BUFFS - 1); return va_buffers[buffer_idx]; } char *va (const char *format, ...) { va_list argptr; char *va_buf; va_buf = get_va_buffer (); va_start (argptr, format); q_vsnprintf (va_buf, VA_BUFFERLEN, format, argptr); va_end (argptr); return va_buf; } /* ============================================================================= QUAKE FILESYSTEM ============================================================================= */ int com_filesize; // // on-disk pakfile // typedef struct { char name[56]; int filepos, filelen; } dpackfile_t; typedef struct { char id[4]; int dirofs; int dirlen; } dpackheader_t; #define MAX_FILES_IN_PACK 2048 char com_gamedir[MAX_OSPATH]; char com_basedir[MAX_OSPATH]; int file_from_pak; // ZOID: global indicating that file came from a pak searchpath_t *com_searchpaths; searchpath_t *com_base_searchpaths; /* ============ COM_Path_f ============ */ static void COM_Path_f (void) { searchpath_t *s; Con_Printf ("Current search path:\n"); for (s = com_searchpaths; s; s = s->next) { if (s->pack) { Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); } else Con_Printf ("%s\n", s->filename); } } /* ============ COM_WriteFile The filename will be prefixed by the current game directory ============ */ void COM_WriteFile (const char *filename, const void *data, int len) { int handle; char name[MAX_OSPATH]; Sys_mkdir (com_gamedir); //johnfitz -- if we've switched to a nonexistant gamedir, create it now so we don't crash q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, filename); handle = Sys_FileOpenWrite (name); if (handle == -1) { Sys_Printf ("COM_WriteFile: failed on %s\n", name); return; } Sys_Printf ("COM_WriteFile: %s\n", name); Sys_FileWrite (handle, data, len); Sys_FileClose (handle); } /* ============ COM_CreatePath ============ */ void COM_CreatePath (char *path) { char *ofs; for (ofs = path + 1; *ofs; ofs++) { if (*ofs == '/') { // create the directory *ofs = 0; Sys_mkdir (path); *ofs = '/'; } } } /* ================ COM_filelength ================ */ long COM_filelength (FILE *f) { long pos, end; pos = ftell (f); fseek (f, 0, SEEK_END); end = ftell (f); fseek (f, pos, SEEK_SET); return end; } /* =========== COM_FindFile Finds the file in the search path. Sets com_filesize and one of handle or file If neither of file or handle is set, this can be used for detecting a file's presence. =========== */ static int COM_FindFile (const char *filename, int *handle, FILE **file, unsigned int *path_id) { searchpath_t *search; char netpath[MAX_OSPATH]; pack_t *pak; int i, findtime; if (file && handle) Sys_Error ("COM_FindFile: both handle and file set"); file_from_pak = 0; // // search through the path, one element at a time // for (search = com_searchpaths; search; search = search->next) { if (search->pack) /* look through all the pak file elements */ { pak = search->pack; for (i = 0; i < pak->numfiles; i++) { if (strcmp(pak->files[i].name, filename) != 0) continue; // found it! com_filesize = pak->files[i].filelen; file_from_pak = 1; if (path_id) *path_id = search->path_id; if (handle) { *handle = pak->handle; Sys_FileSeek (pak->handle, pak->files[i].filepos); return com_filesize; } else if (file) { /* open a new file on the pakfile */ *file = fopen (pak->filename, "rb"); if (*file) fseek (*file, pak->files[i].filepos, SEEK_SET); return com_filesize; } else /* for COM_FileExists() */ { return com_filesize; } } } else /* check a file in the directory tree */ { if (!registered.value) { /* if not a registered version, don't ever go beyond base */ if ( strchr (filename, '/') || strchr (filename,'\\')) continue; } q_snprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename); findtime = Sys_FileTime (netpath); if (findtime == -1) continue; if (path_id) *path_id = search->path_id; if (handle) { com_filesize = Sys_FileOpenRead (netpath, &i); *handle = i; return com_filesize; } else if (file) { *file = fopen (netpath, "rb"); com_filesize = (*file == NULL) ? -1 : COM_filelength (*file); return com_filesize; } else { return 0; /* dummy valid value for COM_FileExists() */ } } } if (strcmp(COM_FileGetExtension(filename), "pcx") != 0 && strcmp(COM_FileGetExtension(filename), "tga") != 0 && strcmp(COM_FileGetExtension(filename), "lit") != 0 && strcmp(COM_FileGetExtension(filename), "ent") != 0) Con_DPrintf ("FindFile: can't find %s\n", filename); else Con_DPrintf2("FindFile: can't find %s\n", filename); // Log pcx, tga, lit, ent misses only if (developer.value >= 2) if (handle) *handle = -1; if (file) *file = NULL; com_filesize = -1; return com_filesize; } /* =========== COM_FileExists Returns whether the file is found in the quake filesystem. =========== */ qboolean COM_FileExists (const char *filename, unsigned int *path_id) { int ret = COM_FindFile (filename, NULL, NULL, path_id); return (ret == -1) ? false : true; } /* =========== COM_OpenFile filename never has a leading slash, but may contain directory walks returns a handle and a length it may actually be inside a pak file =========== */ int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id) { return COM_FindFile (filename, handle, NULL, path_id); } /* =========== COM_FOpenFile If the requested file is inside a packfile, a new FILE * will be opened into the file. =========== */ int COM_FOpenFile (const char *filename, FILE **file, unsigned int *path_id) { return COM_FindFile (filename, NULL, file, path_id); } /* ============ COM_CloseFile If it is a pak file handle, don't really close it ============ */ void COM_CloseFile (int h) { searchpath_t *s; for (s = com_searchpaths; s; s = s->next) if (s->pack && s->pack->handle == h) return; Sys_FileClose (h); } /* ============ COM_LoadFile Filename are reletive to the quake directory. Allways appends a 0 byte. ============ */ #define LOADFILE_ZONE 0 #define LOADFILE_HUNK 1 #define LOADFILE_TEMPHUNK 2 #define LOADFILE_CACHE 3 #define LOADFILE_STACK 4 #define LOADFILE_MALLOC 5 static byte *loadbuf; static cache_user_t *loadcache; static int loadsize; byte *COM_LoadFile (const char *path, int usehunk, unsigned int *path_id) { int h; byte *buf; char base[32]; int len; buf = NULL; // quiet compiler warning // look for it in the filesystem or pack files len = COM_OpenFile (path, &h, path_id); if (h == -1) return NULL; // extract the filename base name for hunk tag COM_FileBase (path, base, sizeof(base)); switch (usehunk) { case LOADFILE_HUNK: buf = (byte *) Hunk_AllocName (len+1, base); break; case LOADFILE_TEMPHUNK: buf = (byte *) Hunk_TempAlloc (len+1); break; case LOADFILE_ZONE: buf = (byte *) Z_Malloc (len+1); break; case LOADFILE_CACHE: buf = (byte *) Cache_Alloc (loadcache, len+1, base); break; case LOADFILE_STACK: if (len < loadsize) buf = loadbuf; else buf = (byte *) Hunk_TempAlloc (len+1); break; case LOADFILE_MALLOC: buf = (byte *) malloc (len+1); break; default: Sys_Error ("COM_LoadFile: bad usehunk"); } if (!buf) Sys_Error ("COM_LoadFile: not enough space for %s", path); ((byte *)buf)[len] = 0; Sys_FileRead (h, buf, len); COM_CloseFile (h); return buf; } byte *COM_LoadHunkFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_HUNK, path_id); } byte *COM_LoadZoneFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_ZONE, path_id); } byte *COM_LoadTempFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_TEMPHUNK, path_id); } void COM_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id) { loadcache = cu; COM_LoadFile (path, LOADFILE_CACHE, path_id); } // uses temp hunk if larger than bufsize byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize, unsigned int *path_id) { byte *buf; loadbuf = (byte *)buffer; loadsize = bufsize; buf = COM_LoadFile (path, LOADFILE_STACK, path_id); return buf; } // returns malloc'd memory byte *COM_LoadMallocFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_MALLOC, path_id); } byte *COM_LoadMallocFile_TextMode_OSPath (const char *path, long *len_out) { FILE *f; byte *data; long len, actuallen; // ericw -- this is used by Host_Loadgame_f. Translate CRLF to LF on load games, // othewise multiline messages have a garbage character at the end of each line. // TODO: could handle in a way that allows loading CRLF savegames on mac/linux // without the junk characters appearing. f = fopen (path, "rt"); if (f == NULL) return NULL; len = COM_filelength (f); if (len < 0) return NULL; data = (byte *) malloc (len + 1); if (data == NULL) return NULL; // (actuallen < len) if CRLF to LF translation was performed actuallen = fread (data, 1, len, f); if (ferror(f)) { free (data); return NULL; } data[actuallen] = '\0'; if (len_out != NULL) *len_out = actuallen; return data; } const char *COM_ParseIntNewline(const char *buffer, int *value) { int consumed = 0; sscanf (buffer, "%i\n%n", value, &consumed); return buffer + consumed; } const char *COM_ParseFloatNewline(const char *buffer, float *value) { int consumed = 0; sscanf (buffer, "%f\n%n", value, &consumed); return buffer + consumed; } const char *COM_ParseStringNewline(const char *buffer) { int consumed = 0; com_token[0] = '\0'; sscanf (buffer, "%1023s\n%n", com_token, &consumed); return buffer + consumed; } /* ================= COM_LoadPackFile -- johnfitz -- modified based on topaz's tutorial Takes an explicit (not game tree related) path to a pak file. Loads the header and directory, adding the files at the beginning of the list so they override previous pack files. ================= */ static pack_t *COM_LoadPackFile (const char *packfile) { dpackheader_t header; int i; packfile_t *newfiles; int numpackfiles; pack_t *pack; int packhandle; dpackfile_t info[MAX_FILES_IN_PACK]; unsigned short crc; if (Sys_FileOpenRead (packfile, &packhandle) == -1) return NULL; Sys_FileRead (packhandle, (void *)&header, sizeof(header)); if (header.id[0] != 'P' || header.id[1] != 'A' || header.id[2] != 'C' || header.id[3] != 'K') Sys_Error ("%s is not a packfile", packfile); header.dirofs = LittleLong (header.dirofs); header.dirlen = LittleLong (header.dirlen); numpackfiles = header.dirlen / sizeof(dpackfile_t); if (header.dirlen < 0 || header.dirofs < 0) { Sys_Error ("Invalid packfile %s (dirlen: %i, dirofs: %i)", packfile, header.dirlen, header.dirofs); } if (!numpackfiles) { Sys_Printf ("WARNING: %s has no files, ignored\n", packfile); Sys_FileClose (packhandle); return NULL; } if (numpackfiles > MAX_FILES_IN_PACK) Sys_Error ("%s has %i files", packfile, numpackfiles); if (numpackfiles != PAK0_COUNT) com_modified = true; // not the original file newfiles = (packfile_t *) Z_Malloc(numpackfiles * sizeof(packfile_t)); Sys_FileSeek (packhandle, header.dirofs); Sys_FileRead (packhandle, (void *)info, header.dirlen); // crc the directory to check for modifications CRC_Init (&crc); for (i = 0; i < header.dirlen; i++) CRC_ProcessByte (&crc, ((byte *)info)[i]); if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100) com_modified = true; // parse the directory for (i = 0; i < numpackfiles; i++) { q_strlcpy (newfiles[i].name, info[i].name, sizeof(newfiles[i].name)); newfiles[i].filepos = LittleLong(info[i].filepos); newfiles[i].filelen = LittleLong(info[i].filelen); } pack = (pack_t *) Z_Malloc (sizeof (pack_t)); q_strlcpy (pack->filename, packfile, sizeof(pack->filename)); pack->handle = packhandle; pack->numfiles = numpackfiles; pack->files = newfiles; //Sys_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); return pack; } /* ================= COM_AddGameDirectory -- johnfitz -- modified based on topaz's tutorial ================= */ static void COM_AddGameDirectory (const char *base, const char *dir) { int i; unsigned int path_id; searchpath_t *search; pack_t *pak, *qspak; char pakfile[MAX_OSPATH]; qboolean been_here = false; q_strlcpy (com_gamedir, va("%s/%s", base, dir), sizeof(com_gamedir)); // assign a path_id to this game directory if (com_searchpaths) path_id = com_searchpaths->path_id << 1; else path_id = 1U; _add_path: // add the directory to the search path search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t)); search->path_id = path_id; q_strlcpy (search->filename, com_gamedir, sizeof(search->filename)); search->next = com_searchpaths; com_searchpaths = search; // add any pak files in the format pak0.pak pak1.pak, ... for (i = 0; ; i++) { q_snprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", com_gamedir, i); pak = COM_LoadPackFile (pakfile); if (i != 0 || path_id != 1 || fitzmode) qspak = NULL; else { qboolean old = com_modified; if (been_here) base = host_parms->userdir; q_snprintf (pakfile, sizeof(pakfile), "%s/quakespasm.pak", base); qspak = COM_LoadPackFile (pakfile); com_modified = old; } if (pak) { search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t)); search->path_id = path_id; search->pack = pak; search->next = com_searchpaths; com_searchpaths = search; } if (qspak) { search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t)); search->path_id = path_id; search->pack = qspak; search->next = com_searchpaths; com_searchpaths = search; } if (!pak) break; } if (!been_here && host_parms->userdir != host_parms->basedir) { been_here = true; q_strlcpy(com_gamedir, va("%s/%s", host_parms->userdir, dir), sizeof(com_gamedir)); Sys_mkdir(com_gamedir); goto _add_path; } } //============================================================================== //johnfitz -- dynamic gamedir stuff -- modified by QuakeSpasm team. //============================================================================== void ExtraMaps_NewGame (void); static void COM_Game_f (void) { if (Cmd_Argc() > 1) { const char *p = Cmd_Argv(1); const char *p2 = Cmd_Argv(2); searchpath_t *search; if (!registered.value) //disable shareware quake { Con_Printf("You must have the registered version to use modified games\n"); return; } if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":")) { Con_Printf ("gamedir should be a single directory name, not a path\n"); return; } if (*p2) { if (strcmp(p2,"-hipnotic") && strcmp(p2,"-rogue") && strcmp(p2,"-quoth")) { Con_Printf ("invalid mission pack argument to \"game\"\n"); return; } if (!q_strcasecmp(p, GAMENAME)) { Con_Printf ("no mission pack arguments to %s game\n", GAMENAME); return; } } if (!q_strcasecmp(p, COM_SkipPath(com_gamedir))) //no change { if (com_searchpaths->path_id > 1) { //current game not id1 if (*p2 && com_searchpaths->path_id == 2) { // rely on QuakeSpasm extension treating '-game missionpack' // as '-missionpack', otherwise would be a mess if (!q_strcasecmp(p, &p2[1])) goto _same; Con_Printf("reloading game \"%s\" with \"%s\" support\n", p, &p2[1]); } else if (!*p2 && com_searchpaths->path_id > 2) Con_Printf("reloading game \"%s\" without mission pack support\n", p); else goto _same; } else { _same: Con_Printf("\"game\" is already \"%s\"\n", COM_SkipPath(com_gamedir)); return; } } com_modified = true; //Kill the server CL_Disconnect (); Host_ShutdownServer(true); //Write config file Host_WriteConfiguration (); //Kill the extra game if it is loaded while (com_searchpaths != com_base_searchpaths) { if (com_searchpaths->pack) { Sys_FileClose (com_searchpaths->pack->handle); Z_Free (com_searchpaths->pack->files); Z_Free (com_searchpaths->pack); } search = com_searchpaths->next; Z_Free (com_searchpaths); com_searchpaths = search; } hipnotic = false; rogue = false; standard_quake = true; if (q_strcasecmp(p, GAMENAME)) //game is not id1 { if (*p2) { COM_AddGameDirectory (com_basedir, &p2[1]); standard_quake = false; if (!strcmp(p2,"-hipnotic") || !strcmp(p2,"-quoth")) hipnotic = true; else if (!strcmp(p2,"-rogue")) rogue = true; if (q_strcasecmp(p, &p2[1])) //don't load twice COM_AddGameDirectory (com_basedir, p); } else { COM_AddGameDirectory (com_basedir, p); // QuakeSpasm extension: treat '-game missionpack' as '-missionpack' if (!q_strcasecmp(p,"hipnotic") || !q_strcasecmp(p,"quoth")) { hipnotic = true; standard_quake = false; } else if (!q_strcasecmp(p,"rogue")) { rogue = true; standard_quake = false; } } } else // just update com_gamedir { q_snprintf (com_gamedir, sizeof(com_gamedir), "%s/%s", (host_parms->userdir != host_parms->basedir)? host_parms->userdir : com_basedir, GAMENAME); } //clear out and reload appropriate data Cache_Flush (); Mod_ResetAll(); if (!isDedicated) { TexMgr_NewGame (); Draw_NewGame (); R_NewGame (); } ExtraMaps_NewGame (); DemoList_Rebuild (); Con_Printf("\"game\" changed to \"%s\"\n", COM_SkipPath(com_gamedir)); VID_Lock (); Cbuf_AddText ("exec quake.rc\n"); Cbuf_AddText ("vid_unlock\n"); } else //Diplay the current gamedir Con_Printf("\"game\" is \"%s\"\n", COM_SkipPath(com_gamedir)); } /* ================= COM_InitFilesystem ================= */ void COM_InitFilesystem (void) //johnfitz -- modified based on topaz's tutorial { int i, j; Cvar_RegisterVariable (®istered); Cvar_RegisterVariable (&cmdline); Cmd_AddCommand ("path", COM_Path_f); Cmd_AddCommand ("game", COM_Game_f); //johnfitz i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) q_strlcpy (com_basedir, com_argv[i + 1], sizeof(com_basedir)); else q_strlcpy (com_basedir, host_parms->basedir, sizeof(com_basedir)); j = strlen (com_basedir); if (j < 1) Sys_Error("Bad argument to -basedir"); if ((com_basedir[j-1] == '\\') || (com_basedir[j-1] == '/')) com_basedir[j-1] = 0; // start up with GAMENAME by default (id1) COM_AddGameDirectory (com_basedir, GAMENAME); /* this is the end of our base searchpath: * any set gamedirs, such as those from -game command line * arguments or by the 'game' console command will be freed * up to here upon a new game command. */ com_base_searchpaths = com_searchpaths; // add mission pack requests (only one should be specified) if (COM_CheckParm ("-rogue")) COM_AddGameDirectory (com_basedir, "rogue"); if (COM_CheckParm ("-hipnotic")) COM_AddGameDirectory (com_basedir, "hipnotic"); if (COM_CheckParm ("-quoth")) COM_AddGameDirectory (com_basedir, "quoth"); i = COM_CheckParm ("-game"); if (i && i < com_argc-1) { const char *p = com_argv[i + 1]; if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":")) Sys_Error ("gamedir should be a single directory name, not a path\n"); com_modified = true; // don't load mission packs twice if (COM_CheckParm ("-rogue") && !q_strcasecmp(p, "rogue")) p = NULL; if (COM_CheckParm ("-hipnotic") && !q_strcasecmp(p, "hipnotic")) p = NULL; if (COM_CheckParm ("-quoth") && !q_strcasecmp(p, "quoth")) p = NULL; if (p != NULL) { COM_AddGameDirectory (com_basedir, p); // QuakeSpasm extension: treat '-game missionpack' as '-missionpack' if (!q_strcasecmp(p,"rogue")) { rogue = true; standard_quake = false; } if (!q_strcasecmp(p,"hipnotic") || !q_strcasecmp(p,"quoth")) { hipnotic = true; standard_quake = false; } } } COM_CheckRegistered (); } /* The following FS_*() stdio replacements are necessary if one is * to perform non-sequential reads on files reopened on pak files * because we need the bookkeeping about file start/end positions. * Allocating and filling in the fshandle_t structure is the users' * responsibility when the file is initially opened. */ size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh) { long byte_size; long bytes_read; size_t nmemb_read; if (!fh) { errno = EBADF; return 0; } if (!ptr) { errno = EFAULT; return 0; } if (!size || !nmemb) { /* no error, just zero bytes wanted */ errno = 0; return 0; } byte_size = nmemb * size; if (byte_size > fh->length - fh->pos) /* just read to end */ byte_size = fh->length - fh->pos; bytes_read = fread(ptr, 1, byte_size, fh->file); fh->pos += bytes_read; /* fread() must return the number of elements read, * not the total number of bytes. */ nmemb_read = bytes_read / size; /* even if the last member is only read partially * it is counted as a whole in the return value. */ if (bytes_read % size) nmemb_read++; return nmemb_read; } int FS_fseek(fshandle_t *fh, long offset, int whence) { /* I don't care about 64 bit off_t or fseeko() here. * the quake/hexen2 file system is 32 bits, anyway. */ int ret; if (!fh) { errno = EBADF; return -1; } /* the relative file position shouldn't be smaller * than zero or bigger than the filesize. */ switch (whence) { case SEEK_SET: break; case SEEK_CUR: offset += fh->pos; break; case SEEK_END: offset = fh->length + offset; break; default: errno = EINVAL; return -1; } if (offset < 0) { errno = EINVAL; return -1; } if (offset > fh->length) /* just seek to end */ offset = fh->length; ret = fseek(fh->file, fh->start + offset, SEEK_SET); if (ret < 0) return ret; fh->pos = offset; return 0; } int FS_fclose(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fclose(fh->file); } long FS_ftell(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fh->pos; } void FS_rewind(fshandle_t *fh) { if (!fh) return; clearerr(fh->file); fseek(fh->file, fh->start, SEEK_SET); fh->pos = 0; } int FS_feof(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } if (fh->pos >= fh->length) return -1; return 0; } int FS_ferror(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return ferror(fh->file); } int FS_fgetc(fshandle_t *fh) { if (!fh) { errno = EBADF; return EOF; } if (fh->pos >= fh->length) return EOF; fh->pos += 1; return fgetc(fh->file); } char *FS_fgets(char *s, int size, fshandle_t *fh) { char *ret; if (FS_feof(fh)) return NULL; if (size > (fh->length - fh->pos) + 1) size = (fh->length - fh->pos) + 1; ret = fgets(s, size, fh->file); fh->pos = ftell(fh->file) - fh->start; return ret; } long FS_filelength (fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fh->length; } �����������������������������������������������quakespasm-0.93.0/Quake/snd_xmp.h�������������������������������������������������������������������0000644�0000000�0000000�00000000325�13021740770�015343� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* module tracker decoding support using libxmp */ #if !defined(_SND_XMP_H_) #define _SND_XMP_H_ #if defined(USE_CODEC_XMP) extern snd_codec_t xmp_codec; #endif /* USE_CODEC_XMP */ #endif /* ! _SND_XMP_H_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/anorm_dots.h����������������������������������������������������������������0000644�0000000�0000000�00000063700�12407762022�016047� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * anorm_dots.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // 0 { 1.23, 1.30, 1.47, 1.35, 1.56, 1.71, 1.37, 1.38, 1.59, 1.60, 1.79, 1.97, 1.88, 1.92, 1.79, 1.02, 0.93, 1.07, 0.82, 0.87, 0.88, 0.94, 0.96, 1.14, 1.11, 0.82, 0.83, 0.89, 0.89, 0.86, 0.94, 0.91, 1.00, 1.21, 0.98, 1.48, 1.30, 1.57, 0.96, 1.07, 1.14, 1.60, 1.61, 1.40, 1.37, 1.72, 1.78, 1.79, 1.93, 1.99, 1.90, 1.68, 1.71, 1.86, 1.60, 1.68, 1.78, 1.86, 1.93, 1.99, 1.97, 1.44, 1.22, 1.49, 0.93, 0.99, 0.99, 1.23, 1.22, 1.44, 1.49, 0.89, 0.89, 0.97, 0.91, 0.98, 1.19, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.19, 0.98, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.87, 0.93, 0.94, 1.02, 1.30, 1.07, 1.35, 1.38, 1.11, 1.56, 1.92, 1.79, 1.79, 1.59, 1.60, 1.72, 1.90, 1.79, 0.80, 0.85, 0.79, 0.93, 0.80, 0.85, 0.77, 0.74, 0.72, 0.77, 0.74, 0.72, 0.70, 0.70, 0.71, 0.76, 0.73, 0.79, 0.79, 0.73, 0.76, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 1 { 1.26, 1.26, 1.48, 1.23, 1.50, 1.71, 1.14, 1.19, 1.38, 1.46, 1.64, 1.94, 1.87, 1.84, 1.71, 1.02, 0.92, 1.00, 0.79, 0.85, 0.84, 0.91, 0.90, 0.98, 0.99, 0.77, 0.77, 0.83, 0.82, 0.79, 0.86, 0.84, 0.92, 0.99, 0.91, 1.24, 1.03, 1.33, 0.88, 0.94, 0.97, 1.41, 1.39, 1.18, 1.11, 1.51, 1.61, 1.59, 1.80, 1.91, 1.76, 1.54, 1.65, 1.76, 1.70, 1.70, 1.85, 1.85, 1.97, 1.99, 1.93, 1.28, 1.09, 1.39, 0.92, 0.97, 0.99, 1.18, 1.26, 1.52, 1.48, 0.83, 0.85, 0.90, 0.88, 0.93, 1.00, 0.77, 0.73, 0.78, 0.72, 0.71, 0.74, 0.75, 0.79, 0.86, 0.81, 0.75, 0.81, 0.79, 0.96, 0.88, 0.94, 0.86, 0.93, 0.92, 0.85, 1.08, 1.33, 1.05, 1.55, 1.31, 1.01, 1.05, 1.27, 1.31, 1.60, 1.47, 1.70, 1.54, 1.76, 1.76, 1.57, 0.93, 0.90, 0.99, 0.88, 0.88, 0.95, 0.97, 1.11, 1.39, 1.20, 0.92, 0.97, 1.01, 1.10, 1.39, 1.22, 1.51, 1.58, 1.32, 1.64, 1.97, 1.85, 1.91, 1.77, 1.74, 1.88, 1.99, 1.91, 0.79, 0.86, 0.80, 0.94, 0.84, 0.88, 0.74, 0.74, 0.71, 0.82, 0.77, 0.76, 0.70, 0.73, 0.72, 0.73, 0.70, 0.74, 0.85, 0.77, 0.82, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 2 { 1.34, 1.27, 1.53, 1.17, 1.46, 1.71, 0.98, 1.05, 1.20, 1.34, 1.48, 1.86, 1.82, 1.71, 1.62, 1.09, 0.94, 0.99, 0.79, 0.85, 0.82, 0.90, 0.87, 0.93, 0.96, 0.76, 0.74, 0.79, 0.76, 0.74, 0.79, 0.78, 0.85, 0.92, 0.85, 1.00, 0.93, 1.06, 0.81, 0.86, 0.89, 1.16, 1.12, 0.97, 0.95, 1.28, 1.38, 1.35, 1.60, 1.77, 1.57, 1.33, 1.50, 1.58, 1.69, 1.63, 1.82, 1.74, 1.91, 1.92, 1.80, 1.04, 0.97, 1.21, 0.90, 0.93, 0.97, 1.05, 1.21, 1.48, 1.37, 0.77, 0.80, 0.84, 0.85, 0.88, 0.92, 0.73, 0.71, 0.74, 0.74, 0.71, 0.75, 0.73, 0.79, 0.84, 0.78, 0.79, 0.86, 0.81, 1.05, 0.94, 0.99, 0.90, 0.95, 0.92, 0.86, 1.24, 1.44, 1.14, 1.59, 1.34, 1.02, 1.27, 1.50, 1.49, 1.80, 1.69, 1.86, 1.72, 1.87, 1.80, 1.69, 1.00, 0.98, 1.23, 0.95, 0.96, 1.09, 1.16, 1.37, 1.63, 1.46, 0.99, 1.10, 1.25, 1.24, 1.51, 1.41, 1.67, 1.77, 1.55, 1.72, 1.95, 1.89, 1.98, 1.91, 1.86, 1.97, 1.99, 1.94, 0.81, 0.89, 0.85, 0.98, 0.90, 0.94, 0.75, 0.78, 0.73, 0.89, 0.83, 0.82, 0.72, 0.77, 0.76, 0.72, 0.70, 0.71, 0.91, 0.83, 0.89, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 3 { 1.46, 1.34, 1.60, 1.16, 1.46, 1.71, 0.94, 0.99, 1.05, 1.26, 1.33, 1.74, 1.76, 1.57, 1.54, 1.23, 0.98, 1.05, 0.83, 0.89, 0.84, 0.92, 0.87, 0.91, 0.96, 0.78, 0.74, 0.79, 0.72, 0.72, 0.75, 0.76, 0.80, 0.88, 0.83, 0.94, 0.87, 0.95, 0.76, 0.80, 0.82, 0.97, 0.96, 0.89, 0.88, 1.08, 1.11, 1.10, 1.37, 1.59, 1.37, 1.07, 1.27, 1.34, 1.57, 1.45, 1.69, 1.55, 1.77, 1.79, 1.60, 0.93, 0.90, 0.99, 0.86, 0.87, 0.93, 0.96, 1.07, 1.35, 1.18, 0.73, 0.76, 0.77, 0.81, 0.82, 0.85, 0.70, 0.71, 0.72, 0.78, 0.73, 0.77, 0.73, 0.79, 0.82, 0.76, 0.83, 0.90, 0.84, 1.18, 0.98, 1.03, 0.92, 0.95, 0.90, 0.86, 1.32, 1.45, 1.15, 1.53, 1.27, 0.99, 1.42, 1.65, 1.58, 1.93, 1.83, 1.94, 1.81, 1.88, 1.74, 1.70, 1.19, 1.17, 1.44, 1.11, 1.15, 1.36, 1.41, 1.61, 1.81, 1.67, 1.22, 1.34, 1.50, 1.42, 1.65, 1.61, 1.82, 1.91, 1.75, 1.80, 1.89, 1.89, 1.98, 1.99, 1.94, 1.98, 1.92, 1.87, 0.86, 0.95, 0.92, 1.14, 0.98, 1.03, 0.79, 0.84, 0.77, 0.97, 0.90, 0.89, 0.76, 0.82, 0.82, 0.74, 0.72, 0.71, 0.98, 0.89, 0.97, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 4 { 1.60, 1.44, 1.68, 1.22, 1.49, 1.71, 0.93, 0.99, 0.99, 1.23, 1.22, 1.60, 1.68, 1.44, 1.49, 1.40, 1.14, 1.19, 0.89, 0.96, 0.89, 0.97, 0.89, 0.91, 0.98, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.91, 0.83, 0.89, 0.72, 0.76, 0.76, 0.89, 0.89, 0.82, 0.82, 0.98, 0.96, 0.97, 1.14, 1.40, 1.19, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.70, 0.72, 0.73, 0.77, 0.76, 0.79, 0.70, 0.72, 0.71, 0.82, 0.77, 0.80, 0.74, 0.79, 0.80, 0.74, 0.87, 0.93, 0.85, 1.23, 1.02, 1.02, 0.93, 0.93, 0.87, 0.85, 1.30, 1.35, 1.07, 1.38, 1.11, 0.94, 1.47, 1.71, 1.56, 1.97, 1.88, 1.92, 1.79, 1.79, 1.59, 1.60, 1.30, 1.35, 1.56, 1.37, 1.38, 1.59, 1.60, 1.79, 1.92, 1.79, 1.48, 1.57, 1.72, 1.61, 1.78, 1.79, 1.93, 1.99, 1.90, 1.86, 1.78, 1.86, 1.93, 1.99, 1.97, 1.90, 1.79, 1.72, 0.94, 1.07, 1.00, 1.37, 1.21, 1.30, 0.86, 0.91, 0.83, 1.14, 0.98, 0.96, 0.82, 0.88, 0.89, 0.79, 0.76, 0.73, 1.07, 0.94, 1.11, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 5 { 1.74, 1.57, 1.76, 1.33, 1.54, 1.71, 0.94, 1.05, 0.99, 1.26, 1.16, 1.46, 1.60, 1.34, 1.46, 1.59, 1.37, 1.37, 0.97, 1.11, 0.96, 1.10, 0.95, 0.94, 1.08, 0.89, 0.82, 0.88, 0.72, 0.76, 0.75, 0.80, 0.80, 0.88, 0.87, 0.91, 0.83, 0.87, 0.72, 0.76, 0.74, 0.83, 0.84, 0.78, 0.79, 0.96, 0.89, 0.92, 0.98, 1.23, 1.05, 0.86, 0.92, 0.95, 1.11, 0.98, 1.22, 1.03, 1.34, 1.42, 1.14, 0.79, 0.77, 0.84, 0.78, 0.76, 0.82, 0.82, 0.89, 0.97, 0.90, 0.70, 0.71, 0.71, 0.73, 0.72, 0.74, 0.73, 0.76, 0.72, 0.86, 0.81, 0.82, 0.76, 0.79, 0.77, 0.73, 0.90, 0.95, 0.86, 1.18, 1.03, 0.98, 0.92, 0.90, 0.83, 0.84, 1.19, 1.17, 0.98, 1.15, 0.97, 0.89, 1.42, 1.65, 1.44, 1.93, 1.83, 1.81, 1.67, 1.61, 1.36, 1.41, 1.32, 1.45, 1.58, 1.57, 1.53, 1.74, 1.70, 1.88, 1.94, 1.81, 1.69, 1.77, 1.87, 1.79, 1.89, 1.92, 1.98, 1.99, 1.98, 1.89, 1.65, 1.80, 1.82, 1.91, 1.94, 1.75, 1.61, 1.50, 1.07, 1.34, 1.27, 1.60, 1.45, 1.55, 0.93, 0.99, 0.90, 1.35, 1.18, 1.07, 0.87, 0.93, 0.96, 0.85, 0.82, 0.77, 1.15, 0.99, 1.27, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 6 { 1.86, 1.71, 1.82, 1.48, 1.62, 1.71, 0.98, 1.20, 1.05, 1.34, 1.17, 1.34, 1.53, 1.27, 1.46, 1.77, 1.60, 1.57, 1.16, 1.38, 1.12, 1.35, 1.06, 1.00, 1.28, 0.97, 0.89, 0.95, 0.76, 0.81, 0.79, 0.86, 0.85, 0.92, 0.93, 0.93, 0.85, 0.87, 0.74, 0.78, 0.74, 0.79, 0.82, 0.76, 0.79, 0.96, 0.85, 0.90, 0.94, 1.09, 0.99, 0.81, 0.85, 0.89, 0.95, 0.90, 0.99, 0.94, 1.10, 1.24, 0.98, 0.75, 0.73, 0.78, 0.74, 0.72, 0.77, 0.76, 0.82, 0.89, 0.83, 0.73, 0.71, 0.71, 0.71, 0.70, 0.72, 0.77, 0.80, 0.74, 0.90, 0.85, 0.84, 0.78, 0.79, 0.75, 0.73, 0.92, 0.95, 0.86, 1.05, 0.99, 0.94, 0.90, 0.86, 0.79, 0.81, 1.00, 0.98, 0.91, 0.96, 0.89, 0.83, 1.27, 1.50, 1.23, 1.80, 1.69, 1.63, 1.46, 1.37, 1.09, 1.16, 1.24, 1.44, 1.49, 1.69, 1.59, 1.80, 1.69, 1.87, 1.86, 1.72, 1.82, 1.91, 1.94, 1.92, 1.95, 1.99, 1.98, 1.91, 1.97, 1.89, 1.51, 1.72, 1.67, 1.77, 1.86, 1.55, 1.41, 1.25, 1.33, 1.58, 1.50, 1.80, 1.63, 1.74, 1.04, 1.21, 0.97, 1.48, 1.37, 1.21, 0.93, 0.97, 1.05, 0.92, 0.88, 0.84, 1.14, 1.02, 1.34, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 7 { 1.94, 1.84, 1.87, 1.64, 1.71, 1.71, 1.14, 1.38, 1.19, 1.46, 1.23, 1.26, 1.48, 1.26, 1.50, 1.91, 1.80, 1.76, 1.41, 1.61, 1.39, 1.59, 1.33, 1.24, 1.51, 1.18, 0.97, 1.11, 0.82, 0.88, 0.86, 0.94, 0.92, 0.99, 1.03, 0.98, 0.91, 0.90, 0.79, 0.84, 0.77, 0.79, 0.84, 0.77, 0.83, 0.99, 0.85, 0.91, 0.92, 1.02, 1.00, 0.79, 0.80, 0.86, 0.88, 0.84, 0.92, 0.88, 0.97, 1.10, 0.94, 0.74, 0.71, 0.74, 0.72, 0.70, 0.73, 0.72, 0.76, 0.82, 0.77, 0.77, 0.73, 0.74, 0.71, 0.70, 0.73, 0.83, 0.85, 0.78, 0.92, 0.88, 0.86, 0.81, 0.79, 0.74, 0.75, 0.92, 0.93, 0.85, 0.96, 0.94, 0.88, 0.86, 0.81, 0.75, 0.79, 0.93, 0.90, 0.85, 0.88, 0.82, 0.77, 1.05, 1.27, 0.99, 1.60, 1.47, 1.39, 1.20, 1.11, 0.95, 0.97, 1.08, 1.33, 1.31, 1.70, 1.55, 1.76, 1.57, 1.76, 1.70, 1.54, 1.85, 1.97, 1.91, 1.99, 1.97, 1.99, 1.91, 1.77, 1.88, 1.85, 1.39, 1.64, 1.51, 1.58, 1.74, 1.32, 1.22, 1.01, 1.54, 1.76, 1.65, 1.93, 1.70, 1.85, 1.28, 1.39, 1.09, 1.52, 1.48, 1.26, 0.97, 0.99, 1.18, 1.00, 0.93, 0.90, 1.05, 1.01, 1.31, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 8 { 1.97, 1.92, 1.88, 1.79, 1.79, 1.71, 1.37, 1.59, 1.38, 1.60, 1.35, 1.23, 1.47, 1.30, 1.56, 1.99, 1.93, 1.90, 1.60, 1.78, 1.61, 1.79, 1.57, 1.48, 1.72, 1.40, 1.14, 1.37, 0.89, 0.96, 0.94, 1.07, 1.00, 1.21, 1.30, 1.14, 0.98, 0.96, 0.86, 0.91, 0.83, 0.82, 0.88, 0.82, 0.89, 1.11, 0.87, 0.94, 0.93, 1.02, 1.07, 0.80, 0.79, 0.85, 0.82, 0.80, 0.87, 0.85, 0.93, 1.02, 0.93, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.82, 0.76, 0.79, 0.72, 0.73, 0.76, 0.89, 0.89, 0.82, 0.93, 0.91, 0.86, 0.83, 0.79, 0.73, 0.76, 0.91, 0.89, 0.83, 0.89, 0.89, 0.82, 0.82, 0.76, 0.72, 0.76, 0.86, 0.83, 0.79, 0.82, 0.76, 0.73, 0.94, 1.00, 0.91, 1.37, 1.21, 1.14, 0.98, 0.96, 0.88, 0.89, 0.96, 1.14, 1.07, 1.60, 1.40, 1.61, 1.37, 1.57, 1.48, 1.30, 1.78, 1.93, 1.79, 1.99, 1.92, 1.90, 1.79, 1.59, 1.72, 1.79, 1.30, 1.56, 1.35, 1.38, 1.60, 1.11, 1.07, 0.94, 1.68, 1.86, 1.71, 1.97, 1.68, 1.86, 1.44, 1.49, 1.22, 1.44, 1.49, 1.22, 0.99, 0.99, 1.23, 1.19, 0.98, 0.97, 0.97, 0.98, 1.19, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 9 { 1.94, 1.97, 1.87, 1.91, 1.85, 1.71, 1.60, 1.77, 1.58, 1.74, 1.51, 1.26, 1.48, 1.39, 1.64, 1.99, 1.97, 1.99, 1.70, 1.85, 1.76, 1.91, 1.76, 1.70, 1.88, 1.55, 1.33, 1.57, 0.96, 1.08, 1.05, 1.31, 1.27, 1.47, 1.54, 1.39, 1.20, 1.11, 0.93, 0.99, 0.90, 0.88, 0.95, 0.88, 0.97, 1.32, 0.92, 1.01, 0.97, 1.10, 1.22, 0.84, 0.80, 0.88, 0.79, 0.79, 0.85, 0.86, 0.92, 1.02, 0.94, 0.82, 0.76, 0.77, 0.72, 0.73, 0.70, 0.72, 0.71, 0.74, 0.74, 0.88, 0.81, 0.85, 0.75, 0.77, 0.82, 0.94, 0.93, 0.86, 0.92, 0.92, 0.86, 0.85, 0.79, 0.74, 0.79, 0.88, 0.85, 0.81, 0.82, 0.83, 0.77, 0.78, 0.73, 0.71, 0.75, 0.79, 0.77, 0.74, 0.77, 0.73, 0.70, 0.86, 0.92, 0.84, 1.14, 0.99, 0.98, 0.91, 0.90, 0.84, 0.83, 0.88, 0.97, 0.94, 1.41, 1.18, 1.39, 1.11, 1.33, 1.24, 1.03, 1.61, 1.80, 1.59, 1.91, 1.84, 1.76, 1.64, 1.38, 1.51, 1.71, 1.26, 1.50, 1.23, 1.19, 1.46, 0.99, 1.00, 0.91, 1.70, 1.85, 1.65, 1.93, 1.54, 1.76, 1.52, 1.48, 1.26, 1.28, 1.39, 1.09, 0.99, 0.97, 1.18, 1.31, 1.01, 1.05, 0.90, 0.93, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 10 { 1.86, 1.95, 1.82, 1.98, 1.89, 1.71, 1.80, 1.91, 1.77, 1.86, 1.67, 1.34, 1.53, 1.51, 1.72, 1.92, 1.91, 1.99, 1.69, 1.82, 1.80, 1.94, 1.87, 1.86, 1.97, 1.59, 1.44, 1.69, 1.05, 1.24, 1.27, 1.49, 1.50, 1.69, 1.72, 1.63, 1.46, 1.37, 1.00, 1.23, 0.98, 0.95, 1.09, 0.96, 1.16, 1.55, 0.99, 1.25, 1.10, 1.24, 1.41, 0.90, 0.85, 0.94, 0.79, 0.81, 0.85, 0.89, 0.94, 1.09, 0.98, 0.89, 0.82, 0.83, 0.74, 0.77, 0.72, 0.76, 0.73, 0.75, 0.78, 0.94, 0.86, 0.91, 0.79, 0.83, 0.89, 0.99, 0.95, 0.90, 0.90, 0.92, 0.84, 0.86, 0.79, 0.75, 0.81, 0.85, 0.80, 0.78, 0.76, 0.77, 0.73, 0.74, 0.71, 0.71, 0.73, 0.74, 0.74, 0.71, 0.76, 0.72, 0.70, 0.79, 0.85, 0.78, 0.98, 0.92, 0.93, 0.85, 0.87, 0.82, 0.79, 0.81, 0.89, 0.86, 1.16, 0.97, 1.12, 0.95, 1.06, 1.00, 0.93, 1.38, 1.60, 1.35, 1.77, 1.71, 1.57, 1.48, 1.20, 1.28, 1.62, 1.27, 1.46, 1.17, 1.05, 1.34, 0.96, 0.99, 0.90, 1.63, 1.74, 1.50, 1.80, 1.33, 1.58, 1.48, 1.37, 1.21, 1.04, 1.21, 0.97, 0.97, 0.93, 1.05, 1.34, 1.02, 1.14, 0.84, 0.88, 0.92, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 11 { 1.74, 1.89, 1.76, 1.98, 1.89, 1.71, 1.93, 1.99, 1.91, 1.94, 1.82, 1.46, 1.60, 1.65, 1.80, 1.79, 1.77, 1.92, 1.57, 1.69, 1.74, 1.87, 1.88, 1.94, 1.98, 1.53, 1.45, 1.70, 1.18, 1.32, 1.42, 1.58, 1.65, 1.83, 1.81, 1.81, 1.67, 1.61, 1.19, 1.44, 1.17, 1.11, 1.36, 1.15, 1.41, 1.75, 1.22, 1.50, 1.34, 1.42, 1.61, 0.98, 0.92, 1.03, 0.83, 0.86, 0.89, 0.95, 0.98, 1.23, 1.14, 0.97, 0.89, 0.90, 0.78, 0.82, 0.76, 0.82, 0.77, 0.79, 0.84, 0.98, 0.90, 0.98, 0.83, 0.89, 0.97, 1.03, 0.95, 0.92, 0.86, 0.90, 0.82, 0.86, 0.79, 0.77, 0.84, 0.81, 0.76, 0.76, 0.72, 0.73, 0.70, 0.72, 0.71, 0.73, 0.73, 0.72, 0.74, 0.71, 0.78, 0.74, 0.72, 0.75, 0.80, 0.76, 0.94, 0.88, 0.91, 0.83, 0.87, 0.84, 0.79, 0.76, 0.82, 0.80, 0.97, 0.89, 0.96, 0.88, 0.95, 0.94, 0.87, 1.11, 1.37, 1.10, 1.59, 1.57, 1.37, 1.33, 1.05, 1.08, 1.54, 1.34, 1.46, 1.16, 0.99, 1.26, 0.96, 1.05, 0.92, 1.45, 1.55, 1.27, 1.60, 1.07, 1.34, 1.35, 1.18, 1.07, 0.93, 0.99, 0.90, 0.93, 0.87, 0.96, 1.27, 0.99, 1.15, 0.77, 0.82, 0.85, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 12 { 1.60, 1.78, 1.68, 1.93, 1.86, 1.71, 1.97, 1.99, 1.99, 1.97, 1.93, 1.60, 1.68, 1.78, 1.86, 1.61, 1.57, 1.79, 1.37, 1.48, 1.59, 1.72, 1.79, 1.92, 1.90, 1.38, 1.35, 1.60, 1.23, 1.30, 1.47, 1.56, 1.71, 1.88, 1.79, 1.92, 1.79, 1.79, 1.30, 1.56, 1.35, 1.37, 1.59, 1.38, 1.60, 1.90, 1.48, 1.72, 1.57, 1.61, 1.79, 1.21, 1.00, 1.30, 0.89, 0.94, 0.96, 1.07, 1.14, 1.40, 1.37, 1.14, 0.96, 0.98, 0.82, 0.88, 0.82, 0.89, 0.83, 0.86, 0.91, 1.02, 0.93, 1.07, 0.87, 0.94, 1.11, 1.02, 0.93, 0.93, 0.82, 0.87, 0.80, 0.85, 0.79, 0.80, 0.85, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.72, 0.76, 0.73, 0.82, 0.79, 0.76, 0.73, 0.79, 0.76, 0.93, 0.86, 0.91, 0.83, 0.89, 0.89, 0.82, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.44, 1.19, 1.22, 0.99, 0.98, 1.49, 1.44, 1.49, 1.22, 0.99, 1.23, 0.98, 1.19, 0.97, 1.21, 1.30, 1.00, 1.37, 0.94, 1.07, 1.14, 0.98, 0.96, 0.86, 0.91, 0.83, 0.88, 0.82, 0.89, 1.11, 0.94, 1.07, 0.73, 0.76, 0.79, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 13 { 1.46, 1.65, 1.60, 1.82, 1.80, 1.71, 1.93, 1.91, 1.99, 1.94, 1.98, 1.74, 1.76, 1.89, 1.89, 1.42, 1.34, 1.61, 1.11, 1.22, 1.36, 1.50, 1.61, 1.81, 1.75, 1.15, 1.17, 1.41, 1.18, 1.19, 1.42, 1.44, 1.65, 1.83, 1.67, 1.94, 1.81, 1.88, 1.32, 1.58, 1.45, 1.57, 1.74, 1.53, 1.70, 1.98, 1.69, 1.87, 1.77, 1.79, 1.92, 1.45, 1.27, 1.55, 0.97, 1.07, 1.11, 1.34, 1.37, 1.59, 1.60, 1.35, 1.07, 1.18, 0.86, 0.93, 0.87, 0.96, 0.90, 0.93, 0.99, 1.03, 0.95, 1.15, 0.90, 0.99, 1.27, 0.98, 0.90, 0.92, 0.78, 0.83, 0.77, 0.84, 0.79, 0.82, 0.86, 0.73, 0.71, 0.73, 0.72, 0.70, 0.73, 0.72, 0.76, 0.81, 0.76, 0.76, 0.82, 0.77, 0.89, 0.85, 0.82, 0.75, 0.80, 0.80, 0.94, 0.88, 0.94, 0.87, 0.95, 0.96, 0.88, 0.72, 0.74, 0.76, 0.83, 0.78, 0.84, 0.79, 0.87, 0.91, 0.83, 0.89, 0.98, 0.92, 1.23, 1.34, 1.05, 1.16, 0.99, 0.96, 1.46, 1.57, 1.54, 1.33, 1.05, 1.26, 1.08, 1.37, 1.10, 0.98, 1.03, 0.92, 1.14, 0.86, 0.95, 0.97, 0.90, 0.89, 0.79, 0.84, 0.77, 0.82, 0.76, 0.82, 0.97, 0.89, 0.98, 0.71, 0.72, 0.74, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 14 { 1.34, 1.51, 1.53, 1.67, 1.72, 1.71, 1.80, 1.77, 1.91, 1.86, 1.98, 1.86, 1.82, 1.95, 1.89, 1.24, 1.10, 1.41, 0.95, 0.99, 1.09, 1.25, 1.37, 1.63, 1.55, 0.96, 0.98, 1.16, 1.05, 1.00, 1.27, 1.23, 1.50, 1.69, 1.46, 1.86, 1.72, 1.87, 1.24, 1.49, 1.44, 1.69, 1.80, 1.59, 1.69, 1.97, 1.82, 1.94, 1.91, 1.92, 1.99, 1.63, 1.50, 1.74, 1.16, 1.33, 1.38, 1.58, 1.60, 1.77, 1.80, 1.48, 1.21, 1.37, 0.90, 0.97, 0.93, 1.05, 0.97, 1.04, 1.21, 0.99, 0.95, 1.14, 0.92, 1.02, 1.34, 0.94, 0.86, 0.90, 0.74, 0.79, 0.75, 0.81, 0.79, 0.84, 0.86, 0.71, 0.71, 0.73, 0.76, 0.73, 0.77, 0.74, 0.80, 0.85, 0.78, 0.81, 0.89, 0.84, 0.97, 0.92, 0.88, 0.79, 0.85, 0.86, 0.98, 0.92, 1.00, 0.93, 1.06, 1.12, 0.95, 0.74, 0.74, 0.78, 0.79, 0.76, 0.82, 0.79, 0.87, 0.93, 0.85, 0.85, 0.94, 0.90, 1.09, 1.27, 0.99, 1.17, 1.05, 0.96, 1.46, 1.71, 1.62, 1.48, 1.20, 1.34, 1.28, 1.57, 1.35, 0.90, 0.94, 0.85, 0.98, 0.81, 0.89, 0.89, 0.83, 0.82, 0.75, 0.78, 0.73, 0.77, 0.72, 0.76, 0.89, 0.83, 0.91, 0.71, 0.70, 0.72, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 15 { 1.26, 1.39, 1.48, 1.51, 1.64, 1.71, 1.60, 1.58, 1.77, 1.74, 1.91, 1.94, 1.87, 1.97, 1.85, 1.10, 0.97, 1.22, 0.88, 0.92, 0.95, 1.01, 1.11, 1.39, 1.32, 0.88, 0.90, 0.97, 0.96, 0.93, 1.05, 0.99, 1.27, 1.47, 1.20, 1.70, 1.54, 1.76, 1.08, 1.31, 1.33, 1.70, 1.76, 1.55, 1.57, 1.88, 1.85, 1.91, 1.97, 1.99, 1.99, 1.70, 1.65, 1.85, 1.41, 1.54, 1.61, 1.76, 1.80, 1.91, 1.93, 1.52, 1.26, 1.48, 0.92, 0.99, 0.97, 1.18, 1.09, 1.28, 1.39, 0.94, 0.93, 1.05, 0.92, 1.01, 1.31, 0.88, 0.81, 0.86, 0.72, 0.75, 0.74, 0.79, 0.79, 0.86, 0.85, 0.71, 0.73, 0.75, 0.82, 0.77, 0.83, 0.78, 0.85, 0.88, 0.81, 0.88, 0.97, 0.90, 1.18, 1.00, 0.93, 0.86, 0.92, 0.94, 1.14, 0.99, 1.24, 1.03, 1.33, 1.39, 1.11, 0.79, 0.77, 0.84, 0.79, 0.77, 0.84, 0.83, 0.90, 0.98, 0.91, 0.85, 0.92, 0.91, 1.02, 1.26, 1.00, 1.23, 1.19, 0.99, 1.50, 1.84, 1.71, 1.64, 1.38, 1.46, 1.51, 1.76, 1.59, 0.84, 0.88, 0.80, 0.94, 0.79, 0.86, 0.82, 0.77, 0.76, 0.74, 0.74, 0.71, 0.73, 0.70, 0.72, 0.82, 0.77, 0.85, 0.74, 0.70, 0.73, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 } ����������������������������������������������������������������quakespasm-0.93.0/Quake/arch_def.h������������������������������������������������������������������0000644�0000000�0000000�00000012043�13050561515�015426� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * arch_def.h * platform specific definitions * - standalone header * - doesn't and must not include any other headers * - shouldn't depend on compiler.h, q_stdinc.h, or * any other headers * * Copyright (C) 2007-2016 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ARCHDEFS_H #define ARCHDEFS_H #if defined(__DJGPP__) || defined(__MSDOS__) || defined(__DOS__) || defined(_MSDOS) # if !defined(PLATFORM_DOS) # define PLATFORM_DOS 1 # endif #elif defined(__OS2__) || defined(__EMX__) # if !defined(PLATFORM_OS2) # define PLATFORM_OS2 1 # endif #elif defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__NT__) || defined(_Windows) # if !defined(PLATFORM_WINDOWS) # define PLATFORM_WINDOWS 1 # endif #elif defined(__APPLE__) && defined(__MACH__) /* Mac OS X */ # if !defined(PLATFORM_OSX) # define PLATFORM_OSX 1 # endif #elif defined(macintosh) /* Mac OS classic */ # if !defined(PLATFORM_MAC) # define PLATFORM_MAC 1 # endif #elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) # if !defined(PLATFORM_AMIGA) # define PLATFORM_AMIGA 1 # endif #elif defined(__riscos__) # if !defined(PLATFORM_RISCOS) # define PLATFORM_RISCOS 1 # endif #else /* here goes the unix platforms */ #if defined(__unix) || defined(__unix__) || defined(unix) || \ defined(__linux__) || defined(__linux) || \ defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(__hpux) || defined(__hpux__) || defined(_hpux) || \ defined(__sun) || defined(sun) || \ defined(__sgi) || defined(sgi) || defined(__sgi__) || \ defined(__GNU__) /* GNU/Hurd */ || \ defined(__QNX__) || defined(__QNXNTO__) # if !defined(PLATFORM_UNIX) # define PLATFORM_UNIX 1 # endif #endif #endif /* PLATFORM_xxx */ #if defined (PLATFORM_OSX) /* OS X is unix-based */ # if !defined(PLATFORM_UNIX) # define PLATFORM_UNIX 2 # endif #endif /* OS X -> PLATFORM_UNIX */ #if defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \ defined(__OpenBSD__) || defined(__NetBSD__) # if !defined(PLATFORM_BSD) # define PLATFORM_BSD 1 # endif #endif /* PLATFORM_BSD (for convenience) */ #if defined(PLATFORM_AMIGA) && !defined(PLATFORM_AMIGAOS3) # if !defined(__MORPHOS__) && !defined(__AROS__) && !defined(__amigaos4__) # define PLATFORM_AMIGAOS3 1 # endif #endif /* PLATFORM_AMIGAOS3 (for convenience) */ #if defined(_WIN64) # define PLATFORM_STRING "Win64" #elif defined(PLATFORM_WINDOWS) # define PLATFORM_STRING "Windows" #elif defined(PLATFORM_DOS) # define PLATFORM_STRING "DOS" #elif defined(PLATFORM_OS2) # define PLATFORM_STRING "OS/2" #elif defined(__linux__) || defined(__linux) # define PLATFORM_STRING "Linux" #elif defined(__DragonFly__) # define PLATFORM_STRING "DragonFly" #elif defined(__FreeBSD__) # define PLATFORM_STRING "FreeBSD" #elif defined(__NetBSD__) # define PLATFORM_STRING "NetBSD" #elif defined(__OpenBSD__) # define PLATFORM_STRING "OpenBSD" #elif defined(__MORPHOS__) # define PLATFORM_STRING "MorphOS" #elif defined(__AROS__) # define PLATFORM_STRING "AROS" #elif defined(__amigaos4__) # define PLATFORM_STRING "AmigaOS4" #elif defined(PLATFORM_AMIGA) # define PLATFORM_STRING "AmigaOS" #elif defined(__QNX__) || defined(__QNXNTO__) # define PLATFORM_STRING "QNX" #elif defined(PLATFORM_OSX) # define PLATFORM_STRING "MacOSX" #elif defined(PLATFORM_MAC) # define PLATFORM_STRING "MacOS" #elif defined(__hpux) || defined(__hpux__) || defined(_hpux) # define PLATFORM_STRING "HP-UX" #elif (defined(__sun) || defined(sun)) && (defined(__svr4__) || defined(__SVR4)) # define PLATFORM_STRING "Solaris" #elif defined(__sun) || defined(sun) # define PLATFORM_STRING "SunOS" #elif defined(__sgi) || defined(sgi) || defined(__sgi__) # define PLATFORM_STRING "Irix" #elif defined(PLATFORM_RISCOS) # define PLATFORM_STRING "RiscOS" #elif defined(__GNU__) # define PLATFORM_STRING "GNU/Hurd" #elif defined(PLATFORM_UNIX) # define PLATFORM_STRING "Unix" #else # define PLATFORM_STRING "Unknown" # warning "Platform is UNKNOWN." #endif /* PLATFORM_STRING */ #endif /* ARCHDEFS_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sbar.h����������������������������������������������������������������������0000644�0000000�0000000�00000002634�12407762022�014630� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_SBAR_H #define _QUAKE_SBAR_H // the status bar is only redrawn if something has changed, but if anything // does, the entire thing will be redrawn for the next vid.numpages frames. extern int sb_lines; // scan lines to draw void Sbar_Init (void); void Sbar_LoadPics (void); void Sbar_Changed (void); // call whenever any of the client stats represented on the sbar changes void Sbar_Draw (void); // called every frame by screen void Sbar_IntermissionOverlay (void); // called each frame after the level has been completed void Sbar_FinaleOverlay (void); #endif /* _QUAKE_SBAR_H */ ����������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/zone.h����������������������������������������������������������������������0000644�0000000�0000000�00000006540�12407762022�014654� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __ZZONE_H #define __ZZONE_H /* memory allocation H_??? The hunk manages the entire memory block given to quake. It must be contiguous. Memory can be allocated from either the low or high end in a stack fashion. The only way memory is released is by resetting one of the pointers. Hunk allocations should be given a name, so the Hunk_Print () function can display usage. Hunk allocations are guaranteed to be 16 byte aligned. The video buffers are allocated high to avoid leaving a hole underneath server allocations when changing to a higher video mode. Z_??? Zone memory functions used for small, dynamic allocations like text strings from command input. There is only about 48K for it, allocated at the very bottom of the hunk. Cache_??? Cache memory is for objects that can be dynamically loaded and can usefully stay persistant between levels. The size of the cache fluctuates from level to level. To allocate a cachable object Temp_??? Temp memory is used for file loading and surface caching. The size of the cache memory is adjusted so that there is a minimum of 512k remaining for temp memory. ------ Top of Memory ------- high hunk allocations <--- high hunk reset point held by vid video buffer z buffer surface cache <--- high hunk used cachable memory <--- low hunk used client and server low hunk allocations <-- low hunk reset point held by host startup hunk allocations Zone block ----- Bottom of Memory ----- */ void Memory_Init (void *buf, int size); void Z_Free (void *ptr); void *Z_Malloc (int size); // returns 0 filled memory void *Z_Realloc (void *ptr, int size); char *Z_Strdup (const char *s); void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, const char *name); void *Hunk_HighAllocName (int size, const char *name); char *Hunk_Strdup (const char *s, const char *name); int Hunk_LowMark (void); void Hunk_FreeToLowMark (int mark); int Hunk_HighMark (void); void Hunk_FreeToHighMark (int mark); void *Hunk_TempAlloc (int size); void Hunk_Check (void); typedef struct cache_user_s { void *data; } cache_user_t; void Cache_Flush (void); void *Cache_Check (cache_user_t *c); // returns the cached data, and moves to the head of the LRU list // if present, otherwise returns NULL void Cache_Free (cache_user_t *c, qboolean freetextures); //johnfitz -- added second argument void *Cache_Alloc (cache_user_t *c, int size, const char *name); // Returns NULL if all purgable data was tossed and there still // wasn't enough room. void Cache_Report (void); #endif /* __ZZONE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/bspfile.h�������������������������������������������������������������������0000644�0000000�0000000�00000021657�13136015044�015326� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __BSPFILE_H #define __BSPFILE_H // upper design bounds #define MAX_MAP_HULLS 4 #define MAX_MAP_MODELS 256 #define MAX_MAP_BRUSHES 4096 #define MAX_MAP_ENTITIES 1024 #define MAX_MAP_ENTSTRING 65536 #define MAX_MAP_PLANES 32767 #define MAX_MAP_NODES 32767 // because negative shorts are contents #define MAX_MAP_CLIPNODES 32767 //#define MAX_MAP_LEAFS 80000 //johnfitz -- was 8192 #define MAX_MAP_VERTS 65535 #define MAX_MAP_FACES 65535 #define MAX_MAP_MARKSURFACES 65535 #define MAX_MAP_TEXINFO 4096 #define MAX_MAP_EDGES 256000 #define MAX_MAP_SURFEDGES 512000 #define MAX_MAP_TEXTURES 512 #define MAX_MAP_MIPTEX 0x200000 #define MAX_MAP_LIGHTING 0x100000 #define MAX_MAP_VISIBILITY 0x100000 #define MAX_MAP_PORTALS 65536 // key / value pair sizes #define MAX_KEY 32 #define MAX_VALUE 1024 //============================================================================= #define BSPVERSION 29 /* RMQ support (2PSB). 32bits instead of shorts for all but bbox sizes (which * still use shorts) */ #define BSP2VERSION_2PSB (('B' << 24) | ('S' << 16) | ('P' << 8) | '2') /* BSP2 support. 32bits instead of shorts for everything (bboxes use floats) */ #define BSP2VERSION_BSP2 (('B' << 0) | ('S' << 8) | ('P' << 16) | ('2'<<24)) #define TOOLVERSION 2 typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_PLANES 1 #define LUMP_TEXTURES 2 #define LUMP_VERTEXES 3 #define LUMP_VISIBILITY 4 #define LUMP_NODES 5 #define LUMP_TEXINFO 6 #define LUMP_FACES 7 #define LUMP_LIGHTING 8 #define LUMP_CLIPNODES 9 #define LUMP_LEAFS 10 #define LUMP_MARKSURFACES 11 #define LUMP_EDGES 12 #define LUMP_SURFEDGES 13 #define LUMP_MODELS 14 #define HEADER_LUMPS 15 typedef struct { float mins[3], maxs[3]; float origin[3]; int headnode[MAX_MAP_HULLS]; int visleafs; // not including the solid leaf 0 int firstface, numfaces; } dmodel_t; typedef struct { int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { int nummiptex; int dataofs[4]; // [nummiptex] } dmiptexlump_t; #define MIPLEVELS 4 typedef struct miptex_s { char name[16]; unsigned width, height; unsigned offsets[MIPLEVELS]; // four mip maps stored } miptex_t; typedef struct { float point[3]; } dvertex_t; // 0-2 are axial planes #define PLANE_X 0 #define PLANE_Y 1 #define PLANE_Z 2 // 3-5 are non-axial planes snapped to the nearest #define PLANE_ANYX 3 #define PLANE_ANYY 4 #define PLANE_ANYZ 5 typedef struct { float normal[3]; float dist; int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate } dplane_t; #define CONTENTS_EMPTY -1 #define CONTENTS_SOLID -2 #define CONTENTS_WATER -3 #define CONTENTS_SLIME -4 #define CONTENTS_LAVA -5 #define CONTENTS_SKY -6 #define CONTENTS_ORIGIN -7 // removed at csg time #define CONTENTS_CLIP -8 // changed to contents_solid #define CONTENTS_CURRENT_0 -9 #define CONTENTS_CURRENT_90 -10 #define CONTENTS_CURRENT_180 -11 #define CONTENTS_CURRENT_270 -12 #define CONTENTS_CURRENT_UP -13 #define CONTENTS_CURRENT_DOWN -14 // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { int planenum; short children[2]; // negative numbers are -(leafs+1), not nodes short mins[3]; // for sphere culling short maxs[3]; unsigned short firstface; unsigned short numfaces; // counting both sides } dsnode_t; typedef struct { int planenum; int children[2]; // negative numbers are -(leafs+1), not nodes short mins[3]; // for sphere culling short maxs[3]; unsigned int firstface; unsigned int numfaces; // counting both sides } dl1node_t; typedef struct { int planenum; int children[2]; // negative numbers are -(leafs+1), not nodes float mins[3]; // for sphere culling float maxs[3]; unsigned int firstface; unsigned int numfaces; // counting both sides } dl2node_t; typedef struct { int planenum; short children[2]; // negative numbers are contents } dsclipnode_t; typedef struct { int planenum; int children[2]; // negative numbers are contents } dlclipnode_t; typedef struct texinfo_s { float vecs[2][4]; // [s/t][xyz offset] int miptex; int flags; } texinfo_t; #define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision #define TEX_MISSING 2 // johnfitz -- this texinfo does not have a texture // note that edge 0 is never used, because negative edge nums are used for // counterclockwise use of the edge in a face typedef struct { unsigned short v[2]; // vertex numbers } dsedge_t; typedef struct { unsigned int v[2]; // vertex numbers } dledge_t; #define MAXLIGHTMAPS 4 typedef struct { short planenum; short side; int firstedge; // we must support > 64k edges short numedges; short texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dsface_t; typedef struct { int planenum; int side; int firstedge; // we must support > 64k edges int numedges; int texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dlface_t; #define AMBIENT_WATER 0 #define AMBIENT_SKY 1 #define AMBIENT_SLIME 2 #define AMBIENT_LAVA 3 #define NUM_AMBIENTS 4 // automatic ambient sounds // leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas // all other leafs need visibility info typedef struct { int contents; int visofs; // -1 = no visibility info short mins[3]; // for frustum culling short maxs[3]; unsigned short firstmarksurface; unsigned short nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dsleaf_t; typedef struct { int contents; int visofs; // -1 = no visibility info short mins[3]; // for frustum culling short maxs[3]; unsigned int firstmarksurface; unsigned int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dl1leaf_t; typedef struct { int contents; int visofs; // -1 = no visibility info float mins[3]; // for frustum culling float maxs[3]; unsigned int firstmarksurface; unsigned int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dl2leaf_t; //============================================================================ #ifndef QUAKE_GAME #define ANGLE_UP -1 #define ANGLE_DOWN -2 // the utilities get to be lazy and just use large static arrays extern int nummodels; extern dmodel_t dmodels[MAX_MAP_MODELS]; extern int visdatasize; extern byte dvisdata[MAX_MAP_VISIBILITY]; extern int lightdatasize; extern byte dlightdata[MAX_MAP_LIGHTING]; extern int texdatasize; extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) extern int entdatasize; extern char dentdata[MAX_MAP_ENTSTRING]; //extern int numleafs; //extern dleaf_t dleafs[MAX_MAP_LEAFS]; extern int numplanes; extern dplane_t dplanes[MAX_MAP_PLANES]; extern int numvertexes; extern dvertex_t dvertexes[MAX_MAP_VERTS]; extern int numnodes; extern dnode_t dnodes[MAX_MAP_NODES]; extern int numtexinfo; extern texinfo_t texinfo[MAX_MAP_TEXINFO]; extern int numfaces; extern dface_t dfaces[MAX_MAP_FACES]; extern int numclipnodes; extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; extern int numedges; extern dedge_t dedges[MAX_MAP_EDGES]; extern int nummarksurfaces; extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; extern int numsurfedges; extern int dsurfedges[MAX_MAP_SURFEDGES]; void DecompressVis (byte *in, byte *decompressed); int CompressVis (byte *vis, byte *dest); void LoadBSPFile (char *filename); void WriteBSPFile (char *filename); void PrintBSPFileSizes (void); //=============== typedef struct epair_s { struct epair_s *next; char *key; char *value; } epair_t; typedef struct { vec3_t origin; int firstbrush; int numbrushes; epair_t *epairs; } entity_t; extern int num_entities; extern entity_t entities[MAX_MAP_ENTITIES]; void ParseEntities (void); void UnparseEntities (void); void SetKeyValue (entity_t *ent, char *key, char *value); char *ValueForKey (entity_t *ent, char *key); // will return "" if not present vec_t FloatForKey (entity_t *ent, char *key); void GetVectorForKey (entity_t *ent, char *key, vec3_t vec); epair_t *ParseEpair (void); #endif /* QUAKE_GAME */ #endif /* __BSPFILE_H */ ���������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_dgrm.h������������������������������������������������������������������0000644�0000000�0000000�00000002634�12407762022�015500� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __NET_DATAGRAM_H #define __NET_DATAGRAM_H int Datagram_Init (void); void Datagram_Listen (qboolean state); void Datagram_SearchForHosts (qboolean xmit); qsocket_t *Datagram_Connect (const char *host); qsocket_t *Datagram_CheckNewConnections (void); int Datagram_GetMessage (qsocket_t *sock); int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data); int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); qboolean Datagram_CanSendMessage (qsocket_t *sock); qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock); void Datagram_Close (qsocket_t *sock); void Datagram_Shutdown (void); #endif /* __NET_DATAGRAM_H */ ����������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/draw.h����������������������������������������������������������������������0000644�0000000�0000000�00000003343�12407762022�014634� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_DRAW_H #define _QUAKE_DRAW_H // draw.h -- these are the only functions outside the refresh allowed // to touch the vid buffer extern qpic_t *draw_disc; // also used on sbar void Draw_Init (void); void Draw_Character (int x, int y, int num); void Draw_DebugChar (char num); void Draw_Pic (int x, int y, qpic_t *pic); void Draw_TransPicTranslate (int x, int y, qpic_t *pic, int top, int bottom); //johnfitz -- more parameters void Draw_ConsoleBackground (void); //johnfitz -- removed parameter int lines void Draw_TileClear (int x, int y, int w, int h); void Draw_Fill (int x, int y, int w, int h, int c, float alpha); //johnfitz -- added alpha void Draw_FadeScreen (void); void Draw_String (int x, int y, const char *str); qpic_t *Draw_PicFromWad (const char *name); qpic_t *Draw_CachePic (const char *path); void Draw_NewGame (void); void GL_SetCanvas (canvastype newcanvas); //johnfitz #endif /* _QUAKE_DRAW_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/wad.c�����������������������������������������������������������������������0000644�0000000�0000000�00000007667�13144221270�014454� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // wad.c #include "quakedef.h" int wad_numlumps; lumpinfo_t *wad_lumps; byte *wad_base = NULL; void SwapPic (qpic_t *pic); /* ================== W_CleanupName Lowercases name and pads with spaces and a terminating 0 to the length of lumpinfo_t->name. Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time Space padding is so names can be printed nicely in tables. Can safely be performed in place. ================== */ void W_CleanupName (const char *in, char *out) { int i; int c; for (i=0 ; i<16 ; i++ ) { c = in[i]; if (!c) break; if (c >= 'A' && c <= 'Z') c += ('a' - 'A'); out[i] = c; } for ( ; i< 16 ; i++ ) out[i] = 0; } /* ==================== W_LoadWadFile ==================== */ void W_LoadWadFile (void) //johnfitz -- filename is now hard-coded for honesty { lumpinfo_t *lump_p; wadinfo_t *header; int i; int infotableofs; const char *filename = WADFILENAME; //johnfitz -- modified to use malloc //TODO: use cache_alloc if (wad_base) free (wad_base); wad_base = COM_LoadMallocFile (filename, NULL); if (!wad_base) Sys_Error ("W_LoadWadFile: couldn't load %s\n\n" "Basedir is: %s\n\n" "Check that this has an " GAMENAME " subdirectory containing pak0.pak and pak1.pak, " "or use the -basedir command-line option to specify another directory.", filename, com_basedir); header = (wadinfo_t *)wad_base; if (header->identification[0] != 'W' || header->identification[1] != 'A' || header->identification[2] != 'D' || header->identification[3] != '2') Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename); wad_numlumps = LittleLong(header->numlumps); infotableofs = LittleLong(header->infotableofs); wad_lumps = (lumpinfo_t *)(wad_base + infotableofs); for (i=0, lump_p = wad_lumps ; i<wad_numlumps ; i++,lump_p++) { lump_p->filepos = LittleLong(lump_p->filepos); lump_p->size = LittleLong(lump_p->size); W_CleanupName (lump_p->name, lump_p->name); // CAUTION: in-place editing!!! if (lump_p->type == TYP_QPIC) SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); } } /* ============= W_GetLumpinfo ============= */ lumpinfo_t *W_GetLumpinfo (const char *name) { int i; lumpinfo_t *lump_p; char clean[16]; W_CleanupName (name, clean); for (lump_p=wad_lumps, i=0 ; i<wad_numlumps ; i++,lump_p++) { if (!strcmp(clean, lump_p->name)) return lump_p; } Con_SafePrintf ("W_GetLumpinfo: %s not found\n", name); //johnfitz -- was Sys_Error return NULL; } void *W_GetLumpName (const char *name) { lumpinfo_t *lump; lump = W_GetLumpinfo (name); if (!lump) return NULL; //johnfitz return (void *)(wad_base + lump->filepos); } void *W_GetLumpNum (int num) { lumpinfo_t *lump; if (num < 0 || num > wad_numlumps) Sys_Error ("W_GetLumpNum: bad number: %i", num); lump = wad_lumps + num; return (void *)(wad_base + lump->filepos); } /* ============================================================================= automatic byte swapping ============================================================================= */ void SwapPic (qpic_t *pic) { pic->width = LittleLong(pic->width); pic->height = LittleLong(pic->height); } �������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_vidsdl.c�����������������������������������������������������������������0000644�0000000�0000000�00000165635�13176243376�015670� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // gl_vidsdl.c -- SDL GL vid component #include "quakedef.h" #include "cfgfile.h" #include "bgmusic.h" #include "resource.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif //ericw -- for putting the driver into multithreaded mode #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #endif #define MAX_MODE_LIST 600 //johnfitz -- was 30 #define MAX_BPPS_LIST 5 #define MAX_RATES_LIST 20 #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAXWIDTH 10000 #define MAXHEIGHT 10000 #define DEFAULT_SDL_FLAGS SDL_OPENGL #define DEFAULT_REFRESHRATE 60 typedef struct { int width; int height; int refreshrate; int bpp; } vmode_t; static const char *gl_vendor; static const char *gl_renderer; static const char *gl_version; static int gl_version_major; static int gl_version_minor; static const char *gl_extensions; static char * gl_extensions_nice; static vmode_t modelist[MAX_MODE_LIST]; static int nummodes; static qboolean vid_initialized = false; #if defined(USE_SDL2) static SDL_Window *draw_context; static SDL_GLContext gl_context; #else static SDL_Surface *draw_context; #endif static qboolean vid_locked = false; //johnfitz static qboolean vid_changed = false; static void VID_Menu_Init (void); //johnfitz static void VID_Menu_f (void); //johnfitz static void VID_MenuDraw (void); static void VID_MenuKey (int key); static void ClearAllStates (void); static void GL_Init (void); static void GL_SetupState (void); //johnfitz viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; qboolean scr_skipupdate; qboolean gl_mtexable = false; qboolean gl_texture_env_combine = false; //johnfitz qboolean gl_texture_env_add = false; //johnfitz qboolean gl_swap_control = false; //johnfitz qboolean gl_anisotropy_able = false; //johnfitz float gl_max_anisotropy; //johnfitz qboolean gl_texture_NPOT = false; //ericw qboolean gl_vbo_able = false; //ericw qboolean gl_glsl_able = false; //ericw GLint gl_max_texture_units = 0; //ericw qboolean gl_glsl_gamma_able = false; //ericw qboolean gl_glsl_alias_able = false; //ericw int gl_stencilbits; PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc = NULL; //johnfitz PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc = NULL; //johnfitz PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc = NULL; //ericw PFNGLBINDBUFFERARBPROC GL_BindBufferFunc = NULL; //ericw PFNGLBUFFERDATAARBPROC GL_BufferDataFunc = NULL; //ericw PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc = NULL; //ericw PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc = NULL; //ericw PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc = NULL; //ericw QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc = NULL; //ericw QS_PFNGLDELETESHADERPROC GL_DeleteShaderFunc = NULL; //ericw QS_PFNGLDELETEPROGRAMPROC GL_DeleteProgramFunc = NULL; //ericw QS_PFNGLSHADERSOURCEPROC GL_ShaderSourceFunc = NULL; //ericw QS_PFNGLCOMPILESHADERPROC GL_CompileShaderFunc = NULL; //ericw QS_PFNGLGETSHADERIVPROC GL_GetShaderivFunc = NULL; //ericw QS_PFNGLGETSHADERINFOLOGPROC GL_GetShaderInfoLogFunc = NULL; //ericw QS_PFNGLGETPROGRAMIVPROC GL_GetProgramivFunc = NULL; //ericw QS_PFNGLGETPROGRAMINFOLOGPROC GL_GetProgramInfoLogFunc = NULL; //ericw QS_PFNGLCREATEPROGRAMPROC GL_CreateProgramFunc = NULL; //ericw QS_PFNGLATTACHSHADERPROC GL_AttachShaderFunc = NULL; //ericw QS_PFNGLLINKPROGRAMPROC GL_LinkProgramFunc = NULL; //ericw QS_PFNGLBINDATTRIBLOCATIONFUNC GL_BindAttribLocationFunc = NULL; //ericw QS_PFNGLUSEPROGRAMPROC GL_UseProgramFunc = NULL; //ericw QS_PFNGLGETATTRIBLOCATIONPROC GL_GetAttribLocationFunc = NULL; //ericw QS_PFNGLVERTEXATTRIBPOINTERPROC GL_VertexAttribPointerFunc = NULL; //ericw QS_PFNGLENABLEVERTEXATTRIBARRAYPROC GL_EnableVertexAttribArrayFunc = NULL; //ericw QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC GL_DisableVertexAttribArrayFunc = NULL; //ericw QS_PFNGLGETUNIFORMLOCATIONPROC GL_GetUniformLocationFunc = NULL; //ericw QS_PFNGLUNIFORM1IPROC GL_Uniform1iFunc = NULL; //ericw QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc = NULL; //ericw QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc = NULL; //ericw QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc = NULL; //ericw //==================================== //johnfitz -- new cvars static cvar_t vid_fullscreen = {"vid_fullscreen", "0", CVAR_ARCHIVE}; // QuakeSpasm, was "1" static cvar_t vid_width = {"vid_width", "800", CVAR_ARCHIVE}; // QuakeSpasm, was 640 static cvar_t vid_height = {"vid_height", "600", CVAR_ARCHIVE}; // QuakeSpasm, was 480 static cvar_t vid_bpp = {"vid_bpp", "16", CVAR_ARCHIVE}; static cvar_t vid_refreshrate = {"vid_refreshrate", "60", CVAR_ARCHIVE}; static cvar_t vid_vsync = {"vid_vsync", "0", CVAR_ARCHIVE}; static cvar_t vid_fsaa = {"vid_fsaa", "0", CVAR_ARCHIVE}; // QuakeSpasm static cvar_t vid_desktopfullscreen = {"vid_desktopfullscreen", "0", CVAR_ARCHIVE}; // QuakeSpasm static cvar_t vid_borderless = {"vid_borderless", "0", CVAR_ARCHIVE}; // QuakeSpasm //johnfitz cvar_t vid_gamma = {"gamma", "1", CVAR_ARCHIVE}; //johnfitz -- moved here from view.c cvar_t vid_contrast = {"contrast", "1", CVAR_ARCHIVE}; //QuakeSpasm, MarkV //========================================================================== // // HARDWARE GAMMA -- johnfitz // //========================================================================== #define USE_GAMMA_RAMPS 0 #if USE_GAMMA_RAMPS static unsigned short vid_gamma_red[256]; static unsigned short vid_gamma_green[256]; static unsigned short vid_gamma_blue[256]; static unsigned short vid_sysgamma_red[256]; static unsigned short vid_sysgamma_green[256]; static unsigned short vid_sysgamma_blue[256]; #endif static qboolean gammaworks = false; // whether hw-gamma works static int fsaa; /* ================ VID_Gamma_SetGamma -- apply gamma correction ================ */ static void VID_Gamma_SetGamma (void) { if (gl_glsl_gamma_able) return; if (draw_context && gammaworks) { float value; if (vid_gamma.value > (1.0f / GAMMA_MAX)) value = 1.0f / vid_gamma.value; else value = GAMMA_MAX; #if defined(USE_SDL2) # if USE_GAMMA_RAMPS if (SDL_SetWindowGammaRamp(draw_context, vid_gamma_red, vid_gamma_green, vid_gamma_blue) != 0) Con_Printf ("VID_Gamma_SetGamma: failed on SDL_SetWindowGammaRamp\n"); # else if (SDL_SetWindowBrightness(draw_context, value) != 0) Con_Printf ("VID_Gamma_SetGamma: failed on SDL_SetWindowBrightness\n"); # endif #else /* USE_SDL2 */ # if USE_GAMMA_RAMPS if (SDL_SetGammaRamp(vid_gamma_red, vid_gamma_green, vid_gamma_blue) == -1) Con_Printf ("VID_Gamma_SetGamma: failed on SDL_SetGammaRamp\n"); # else if (SDL_SetGamma(value,value,value) == -1) Con_Printf ("VID_Gamma_SetGamma: failed on SDL_SetGamma\n"); # endif #endif /* USE_SDL2 */ } } /* ================ VID_Gamma_Restore -- restore system gamma ================ */ static void VID_Gamma_Restore (void) { if (gl_glsl_gamma_able) return; if (draw_context && gammaworks) { #if defined(USE_SDL2) # if USE_GAMMA_RAMPS if (SDL_SetWindowGammaRamp(draw_context, vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) != 0) Con_Printf ("VID_Gamma_Restore: failed on SDL_SetWindowGammaRamp\n"); # else if (SDL_SetWindowBrightness(draw_context, 1) != 0) Con_Printf ("VID_Gamma_Restore: failed on SDL_SetWindowBrightness\n"); # endif #else /* USE_SDL2 */ # if USE_GAMMA_RAMPS if (SDL_SetGammaRamp(vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) == -1) Con_Printf ("VID_Gamma_Restore: failed on SDL_SetGammaRamp\n"); # else if (SDL_SetGamma(1, 1, 1) == -1) Con_Printf ("VID_Gamma_Restore: failed on SDL_SetGamma\n"); # endif #endif /* USE_SDL2 */ } } /* ================ VID_Gamma_Shutdown -- called on exit ================ */ static void VID_Gamma_Shutdown (void) { VID_Gamma_Restore (); } /* ================ VID_Gamma_f -- callback when the cvar changes ================ */ static void VID_Gamma_f (cvar_t *var) { if (gl_glsl_gamma_able) return; #if USE_GAMMA_RAMPS int i; for (i = 0; i < 256; i++) { vid_gamma_red[i] = CLAMP(0, (int) ((255 * pow((i + 0.5)/255.5, vid_gamma.value) + 0.5) * vid_contrast.value), 255) << 8; vid_gamma_green[i] = vid_gamma_red[i]; vid_gamma_blue[i] = vid_gamma_red[i]; } #endif VID_Gamma_SetGamma (); } /* ================ VID_Gamma_Init -- call on init ================ */ static void VID_Gamma_Init (void) { Cvar_RegisterVariable (&vid_gamma); Cvar_RegisterVariable (&vid_contrast); Cvar_SetCallback (&vid_gamma, VID_Gamma_f); Cvar_SetCallback (&vid_contrast, VID_Gamma_f); if (gl_glsl_gamma_able) return; #if defined(USE_SDL2) # if USE_GAMMA_RAMPS gammaworks = (SDL_GetWindowGammaRamp(draw_context, vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) == 0); if (gammaworks) gammaworks = (SDL_SetWindowGammaRamp(draw_context, vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) == 0); # else gammaworks = (SDL_SetWindowBrightness(draw_context, 1) == 0); # endif #else /* USE_SDL2 */ # if USE_GAMMA_RAMPS gammaworks = (SDL_GetGammaRamp(vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) == 0); if (gammaworks) gammaworks = (SDL_SetGammaRamp(vid_sysgamma_red, vid_sysgamma_green, vid_sysgamma_blue) == 0); # else gammaworks = (SDL_SetGamma(1, 1, 1) == 0); # endif #endif /* USE_SDL2 */ if (!gammaworks) Con_SafePrintf("gamma adjustment not available\n"); } /* ====================== VID_GetCurrentWidth ====================== */ static int VID_GetCurrentWidth (void) { #if defined(USE_SDL2) int w = 0, h = 0; SDL_GetWindowSize(draw_context, &w, &h); return w; #else return draw_context->w; #endif } /* ======================= VID_GetCurrentHeight ======================= */ static int VID_GetCurrentHeight (void) { #if defined(USE_SDL2) int w = 0, h = 0; SDL_GetWindowSize(draw_context, &w, &h); return h; #else return draw_context->h; #endif } /* ==================== VID_GetCurrentRefreshRate ==================== */ static int VID_GetCurrentRefreshRate (void) { #if defined(USE_SDL2) SDL_DisplayMode mode; int current_display; current_display = SDL_GetWindowDisplayIndex(draw_context); if (0 != SDL_GetCurrentDisplayMode(current_display, &mode)) return DEFAULT_REFRESHRATE; return mode.refresh_rate; #else // SDL1.2 doesn't support refresh rates return DEFAULT_REFRESHRATE; #endif } /* ==================== VID_GetCurrentBPP ==================== */ static int VID_GetCurrentBPP (void) { #if defined(USE_SDL2) const Uint32 pixelFormat = SDL_GetWindowPixelFormat(draw_context); return SDL_BITSPERPIXEL(pixelFormat); #else return draw_context->format->BitsPerPixel; #endif } /* ==================== VID_GetFullscreen returns true if we are in regular fullscreen or "desktop fullscren" ==================== */ static qboolean VID_GetFullscreen (void) { #if defined(USE_SDL2) return (SDL_GetWindowFlags(draw_context) & SDL_WINDOW_FULLSCREEN) != 0; #else return (draw_context->flags & SDL_FULLSCREEN) != 0; #endif } /* ==================== VID_GetDesktopFullscreen returns true if we are specifically in "desktop fullscreen" mode ==================== */ static qboolean VID_GetDesktopFullscreen (void) { #if defined(USE_SDL2) return (SDL_GetWindowFlags(draw_context) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP; #else return false; #endif } /* ==================== VID_GetVSync ==================== */ static qboolean VID_GetVSync (void) { #if defined(USE_SDL2) return SDL_GL_GetSwapInterval() == 1; #else int swap_control; if (SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &swap_control) == 0) return swap_control > 0; return false; #endif } /* ==================== VID_GetWindow used by pl_win.c ==================== */ void *VID_GetWindow (void) { #if defined(USE_SDL2) return draw_context; #else return NULL; #endif } /* ==================== VID_HasMouseOrInputFocus ==================== */ qboolean VID_HasMouseOrInputFocus (void) { #if defined(USE_SDL2) return (SDL_GetWindowFlags(draw_context) & (SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS)) != 0; #else return (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0; #endif } /* ==================== VID_IsMinimized ==================== */ qboolean VID_IsMinimized (void) { #if defined(USE_SDL2) return !(SDL_GetWindowFlags(draw_context) & SDL_WINDOW_SHOWN); #else /* SDL_APPACTIVE in SDL 1.x means "not minimized" */ return !(SDL_GetAppState() & SDL_APPACTIVE); #endif } #if defined(USE_SDL2) /* ================ VID_SDL2_GetDisplayMode Returns a pointer to a statically allocated SDL_DisplayMode structure if there is one with the requested params on the default display. Otherwise returns NULL. This is passed to SDL_SetWindowDisplayMode to specify a pixel format with the requested bpp. If we didn't care about bpp we could just pass NULL. ================ */ static SDL_DisplayMode *VID_SDL2_GetDisplayMode(int width, int height, int refreshrate, int bpp) { static SDL_DisplayMode mode; const int sdlmodes = SDL_GetNumDisplayModes(0); int i; for (i = 0; i < sdlmodes; i++) { if (SDL_GetDisplayMode(0, i, &mode) != 0) continue; if (mode.w == width && mode.h == height && SDL_BITSPERPIXEL(mode.format) == bpp && mode.refresh_rate == refreshrate) { return &mode; } } return NULL; } #endif /* USE_SDL2 */ /* ================ VID_ValidMode ================ */ static qboolean VID_ValidMode (int width, int height, int refreshrate, int bpp, qboolean fullscreen) { // ignore width / height / bpp if vid_desktopfullscreen is enabled if (fullscreen && vid_desktopfullscreen.value) return true; if (width < 320) return false; if (height < 200) return false; #if defined(USE_SDL2) if (fullscreen && VID_SDL2_GetDisplayMode(width, height, refreshrate, bpp) == NULL) bpp = 0; #else { Uint32 flags = DEFAULT_SDL_FLAGS; if (fullscreen) flags |= SDL_FULLSCREEN; bpp = SDL_VideoModeOK(width, height, bpp, flags); } #endif switch (bpp) { case 16: case 24: case 32: break; default: return false; } return true; } /* ================ VID_SetMode ================ */ static qboolean VID_SetMode (int width, int height, int refreshrate, int bpp, qboolean fullscreen) { int temp; Uint32 flags; char caption[50]; int depthbits, stencilbits; int fsaa_obtained; #if defined(USE_SDL2) int previous_display; #endif // so Con_Printfs don't mess us up by forcing vid and snd updates temp = scr_disabled_for_loading; scr_disabled_for_loading = true; CDAudio_Pause (); BGM_Pause (); /* z-buffer depth */ if (bpp == 16) { depthbits = 16; stencilbits = 0; } else { depthbits = 24; stencilbits = 8; } SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthbits); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilbits); /* fsaa */ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, fsaa > 0 ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fsaa); q_snprintf(caption, sizeof(caption), "QuakeSpasm " QUAKESPASM_VER_STRING); #if defined(USE_SDL2) /* Create the window if needed, hidden */ if (!draw_context) { flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; if (vid_borderless.value) flags |= SDL_WINDOW_BORDERLESS; draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); if (!draw_context) { // scale back fsaa SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); } if (!draw_context) { // scale back SDL_GL_DEPTH_SIZE SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); } if (!draw_context) { // scale back SDL_GL_STENCIL_SIZE SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); } if (!draw_context) Sys_Error ("Couldn't create window"); previous_display = -1; } else { previous_display = SDL_GetWindowDisplayIndex(draw_context); } /* Ensure the window is not fullscreen */ if (VID_GetFullscreen ()) { if (SDL_SetWindowFullscreen (draw_context, 0) != 0) Sys_Error("Couldn't set fullscreen state mode"); } /* Set window size and display mode */ SDL_SetWindowSize (draw_context, width, height); if (previous_display >= 0) SDL_SetWindowPosition (draw_context, SDL_WINDOWPOS_CENTERED_DISPLAY(previous_display), SDL_WINDOWPOS_CENTERED_DISPLAY(previous_display)); else SDL_SetWindowPosition(draw_context, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); SDL_SetWindowDisplayMode (draw_context, VID_SDL2_GetDisplayMode(width, height, refreshrate, bpp)); SDL_SetWindowBordered (draw_context, vid_borderless.value ? SDL_FALSE : SDL_TRUE); /* Make window fullscreen if needed, and show the window */ if (fullscreen) { Uint32 flags = vid_desktopfullscreen.value ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN; if (SDL_SetWindowFullscreen (draw_context, flags) != 0) Sys_Error ("Couldn't set fullscreen state mode"); } SDL_ShowWindow (draw_context); /* Create GL context if needed */ if (!gl_context) { gl_context = SDL_GL_CreateContext(draw_context); if (!gl_context) Sys_Error("Couldn't create GL context"); } gl_swap_control = true; if (SDL_GL_SetSwapInterval ((vid_vsync.value) ? 1 : 0) == -1) gl_swap_control = false; #else /* !defined(USE_SDL2) */ flags = DEFAULT_SDL_FLAGS; if (fullscreen) flags |= SDL_FULLSCREEN; if (vid_borderless.value) flags |= SDL_NOFRAME; gl_swap_control = true; if (SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, (vid_vsync.value) ? 1 : 0) == -1) gl_swap_control = false; bpp = SDL_VideoModeOK(width, height, bpp, flags); draw_context = SDL_SetVideoMode(width, height, bpp, flags); if (!draw_context) { // scale back fsaa SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); draw_context = SDL_SetVideoMode(width, height, bpp, flags); } if (!draw_context) { // scale back SDL_GL_DEPTH_SIZE SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); draw_context = SDL_SetVideoMode(width, height, bpp, flags); } if (!draw_context) { // scale back SDL_GL_STENCIL_SIZE SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); draw_context = SDL_SetVideoMode(width, height, bpp, flags); if (!draw_context) Sys_Error ("Couldn't set video mode"); } SDL_WM_SetCaption(caption, caption); #endif /* !defined(USE_SDL2) */ vid.width = VID_GetCurrentWidth(); vid.height = VID_GetCurrentHeight(); vid.conwidth = vid.width & 0xFFFFFFF8; vid.conheight = vid.conwidth * vid.height / vid.width; vid.numpages = 2; // read the obtained z-buffer depth if (SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthbits) == -1) depthbits = 0; // read obtained fsaa samples if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fsaa_obtained) == -1) fsaa_obtained = 0; // read stencil bits if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &gl_stencilbits) == -1) gl_stencilbits = 0; modestate = VID_GetFullscreen() ? MS_FULLSCREEN : MS_WINDOWED; CDAudio_Resume (); BGM_Resume (); scr_disabled_for_loading = temp; // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); Con_SafePrintf ("Video mode %dx%dx%d %dHz (%d-bit z-buffer, %dx FSAA) initialized\n", VID_GetCurrentWidth(), VID_GetCurrentHeight(), VID_GetCurrentBPP(), VID_GetCurrentRefreshRate(), depthbits, fsaa_obtained); vid.recalc_refdef = 1; // no pending changes vid_changed = false; return true; } /* =================== VID_Changed_f -- kristian -- notify us that a value has changed that requires a vid_restart =================== */ static void VID_Changed_f (cvar_t *var) { vid_changed = true; } /* =================== VID_Restart -- johnfitz -- change video modes on the fly =================== */ static void VID_Restart (void) { int width, height, refreshrate, bpp; qboolean fullscreen; if (vid_locked || !vid_changed) return; width = (int)vid_width.value; height = (int)vid_height.value; refreshrate = (int)vid_refreshrate.value; bpp = (int)vid_bpp.value; fullscreen = vid_fullscreen.value ? true : false; // // validate new mode // if (!VID_ValidMode (width, height, refreshrate, bpp, fullscreen)) { Con_Printf ("%dx%dx%d %dHz %s is not a valid mode\n", width, height, bpp, refreshrate, fullscreen? "fullscreen" : "windowed"); return; } // ericw -- OS X, SDL1: textures, VBO's invalid after mode change // OS X, SDL2: still valid after mode change // To handle both cases, delete all GL objects (textures, VBO, GLSL) now. // We must not interleave deleting the old objects with creating new ones, because // one of the new objects could be given the same ID as an invalid handle // which is later deleted. TexMgr_DeleteTextureObjects (); GLSLGamma_DeleteTexture (); R_ScaleView_DeleteTexture (); R_DeleteShaders (); GL_DeleteBModelVertexBuffer (); GLMesh_DeleteVertexBuffers (); // // set new mode // VID_SetMode (width, height, refreshrate, bpp, fullscreen); GL_Init (); TexMgr_ReloadImages (); GL_BuildBModelVertexBuffer (); GLMesh_LoadVertexBuffers (); GL_SetupState (); Fog_SetupState (); //warpimages needs to be recalculated TexMgr_RecalcWarpImageSize (); //conwidth and conheight need to be recalculated vid.conwidth = (scr_conwidth.value > 0) ? (int)scr_conwidth.value : (scr_conscale.value > 0) ? (int)(vid.width/scr_conscale.value) : vid.width; vid.conwidth = CLAMP (320, vid.conwidth, vid.width); vid.conwidth &= 0xFFFFFFF8; vid.conheight = vid.conwidth * vid.height / vid.width; // // keep cvars in line with actual mode // VID_SyncCvars(); // // update mouse grab // if (key_dest == key_console || key_dest == key_menu) { if (modestate == MS_WINDOWED) IN_Deactivate(true); else if (modestate == MS_FULLSCREEN) IN_Activate(); } } /* ================ VID_Test -- johnfitz -- like vid_restart, but asks for confirmation after switching modes ================ */ static void VID_Test (void) { int old_width, old_height, old_refreshrate, old_bpp, old_fullscreen; if (vid_locked || !vid_changed) return; // // now try the switch // old_width = VID_GetCurrentWidth(); old_height = VID_GetCurrentHeight(); old_refreshrate = VID_GetCurrentRefreshRate(); old_bpp = VID_GetCurrentBPP(); old_fullscreen = VID_GetFullscreen() ? true : false; VID_Restart (); //pop up confirmation dialoge if (!SCR_ModalMessage("Would you like to keep this\nvideo mode? (y/n)\n", 5.0f)) { //revert cvars and mode Cvar_SetValueQuick (&vid_width, old_width); Cvar_SetValueQuick (&vid_height, old_height); Cvar_SetValueQuick (&vid_refreshrate, old_refreshrate); Cvar_SetValueQuick (&vid_bpp, old_bpp); Cvar_SetQuick (&vid_fullscreen, old_fullscreen ? "1" : "0"); VID_Restart (); } } /* ================ VID_Unlock -- johnfitz ================ */ static void VID_Unlock (void) { vid_locked = false; VID_SyncCvars(); } /* ================ VID_Lock -- ericw Subsequent changes to vid_* mode settings, and vid_restart commands, will be ignored until the "vid_unlock" command is run. Used when changing gamedirs so the current settings override what was saved in the config.cfg. ================ */ void VID_Lock (void) { vid_locked = true; } //============================================================================== // // OPENGL STUFF // //============================================================================== /* =============== GL_MakeNiceExtensionsList -- johnfitz =============== */ static char *GL_MakeNiceExtensionsList (const char *in) { char *copy, *token, *out; int i, count; if (!in) return Z_Strdup("(none)"); //each space will be replaced by 4 chars, so count the spaces before we malloc for (i = 0, count = 1; i < (int) strlen(in); i++) { if (in[i] == ' ') count++; } out = (char *) Z_Malloc (strlen(in) + count*3 + 1); //usually about 1-2k out[0] = 0; copy = (char *) Z_Strdup(in); for (token = strtok(copy, " "); token; token = strtok(NULL, " ")) { strcat(out, "\n "); strcat(out, token); } Z_Free (copy); return out; } /* =============== GL_Info_f -- johnfitz =============== */ static void GL_Info_f (void) { Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions_nice); } /* =============== GL_CheckExtensions =============== */ static qboolean GL_ParseExtensionList (const char *list, const char *name) { const char *start; const char *where, *terminator; if (!list || !name || !*name) return false; if (strchr(name, ' ') != NULL) return false; // extension names must not have spaces start = list; while (1) { where = strstr (start, name); if (!where) break; terminator = where + strlen (name); if (where == start || where[-1] == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } static void GL_CheckExtensions (void) { int swap_control; // ARB_vertex_buffer_object // if (COM_CheckParm("-novbo")) Con_Warning ("Vertex buffer objects disabled at command line\n"); else if (gl_version_major < 1 || (gl_version_major == 1 && gl_version_minor < 5)) Con_Warning ("OpenGL version < 1.5, skipping ARB_vertex_buffer_object check\n"); else { GL_BindBufferFunc = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB"); GL_BufferDataFunc = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB"); GL_BufferSubDataFunc = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB"); GL_DeleteBuffersFunc = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); GL_GenBuffersFunc = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); if (GL_BindBufferFunc && GL_BufferDataFunc && GL_BufferSubDataFunc && GL_DeleteBuffersFunc && GL_GenBuffersFunc) { Con_Printf("FOUND: ARB_vertex_buffer_object\n"); gl_vbo_able = true; } else { Con_Warning ("ARB_vertex_buffer_object not available\n"); } } // multitexture // if (COM_CheckParm("-nomtex")) Con_Warning ("Mutitexture disabled at command line\n"); else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_multitexture")) { GL_MTexCoord2fFunc = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB"); GL_SelectTextureFunc = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB"); GL_ClientActiveTextureFunc = (PFNGLCLIENTACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glClientActiveTextureARB"); if (GL_MTexCoord2fFunc && GL_SelectTextureFunc && GL_ClientActiveTextureFunc) { Con_Printf("FOUND: ARB_multitexture\n"); gl_mtexable = true; glGetIntegerv(GL_MAX_TEXTURE_UNITS, &gl_max_texture_units); Con_Printf("GL_MAX_TEXTURE_UNITS: %d\n", (int)gl_max_texture_units); } else { Con_Warning ("Couldn't link to multitexture functions\n"); } } else { Con_Warning ("multitexture not supported (extension not found)\n"); } // texture_env_combine // if (COM_CheckParm("-nocombine")) Con_Warning ("texture_env_combine disabled at command line\n"); else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_env_combine")) { Con_Printf("FOUND: ARB_texture_env_combine\n"); gl_texture_env_combine = true; } else if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_env_combine")) { Con_Printf("FOUND: EXT_texture_env_combine\n"); gl_texture_env_combine = true; } else { Con_Warning ("texture_env_combine not supported\n"); } // texture_env_add // if (COM_CheckParm("-noadd")) Con_Warning ("texture_env_add disabled at command line\n"); else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_env_add")) { Con_Printf("FOUND: ARB_texture_env_add\n"); gl_texture_env_add = true; } else if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_env_add")) { Con_Printf("FOUND: EXT_texture_env_add\n"); gl_texture_env_add = true; } else { Con_Warning ("texture_env_add not supported\n"); } // swap control // if (!gl_swap_control) { #if defined(USE_SDL2) Con_Warning ("vertical sync not supported (SDL_GL_SetSwapInterval failed)\n"); #else Con_Warning ("vertical sync not supported (SDL_GL_SetAttribute failed)\n"); #endif } #if defined(USE_SDL2) else if ((swap_control = SDL_GL_GetSwapInterval()) == -1) #else else if (SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &swap_control) == -1) #endif { gl_swap_control = false; #if defined(USE_SDL2) Con_Warning ("vertical sync not supported (SDL_GL_GetSwapInterval failed)\n"); #else Con_Warning ("vertical sync not supported (SDL_GL_GetAttribute failed)\n"); #endif } else if ((vid_vsync.value && swap_control != 1) || (!vid_vsync.value && swap_control != 0)) { gl_swap_control = false; Con_Warning ("vertical sync not supported (swap_control doesn't match vid_vsync)\n"); } else { #if defined(USE_SDL2) Con_Printf("FOUND: SDL_GL_SetSwapInterval\n"); #else Con_Printf("FOUND: SDL_GL_SWAP_CONTROL\n"); #endif } // anisotropic filtering // if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_filter_anisotropic")) { float test1,test2; GLuint tex; // test to make sure we really have control over it // 1.0 and 2.0 should always be legal values glGenTextures(1, &tex); glBindTexture (GL_TEXTURE_2D, tex); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glGetTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f); glGetTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test2); glDeleteTextures(1, &tex); if (test1 == 1 && test2 == 2) { Con_Printf("FOUND: EXT_texture_filter_anisotropic\n"); gl_anisotropy_able = true; } else { Con_Warning ("anisotropic filtering locked by driver. Current driver setting is %f\n", test1); } //get max value either way, so the menu and stuff know it glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_anisotropy); if (gl_max_anisotropy < 2) { gl_anisotropy_able = false; gl_max_anisotropy = 1; Con_Warning ("anisotropic filtering broken: disabled\n"); } } else { gl_max_anisotropy = 1; Con_Warning ("texture_filter_anisotropic not supported\n"); } // texture_non_power_of_two // if (COM_CheckParm("-notexturenpot")) Con_Warning ("texture_non_power_of_two disabled at command line\n"); else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_non_power_of_two")) { Con_Printf("FOUND: ARB_texture_non_power_of_two\n"); gl_texture_NPOT = true; } else { Con_Warning ("texture_non_power_of_two not supported\n"); } // GLSL // if (COM_CheckParm("-noglsl")) Con_Warning ("GLSL disabled at command line\n"); else if (gl_version_major >= 2) { GL_CreateShaderFunc = (QS_PFNGLCREATESHADERPROC) SDL_GL_GetProcAddress("glCreateShader"); GL_DeleteShaderFunc = (QS_PFNGLDELETESHADERPROC) SDL_GL_GetProcAddress("glDeleteShader"); GL_DeleteProgramFunc = (QS_PFNGLDELETEPROGRAMPROC) SDL_GL_GetProcAddress("glDeleteProgram"); GL_ShaderSourceFunc = (QS_PFNGLSHADERSOURCEPROC) SDL_GL_GetProcAddress("glShaderSource"); GL_CompileShaderFunc = (QS_PFNGLCOMPILESHADERPROC) SDL_GL_GetProcAddress("glCompileShader"); GL_GetShaderivFunc = (QS_PFNGLGETSHADERIVPROC) SDL_GL_GetProcAddress("glGetShaderiv"); GL_GetShaderInfoLogFunc = (QS_PFNGLGETSHADERINFOLOGPROC) SDL_GL_GetProcAddress("glGetShaderInfoLog"); GL_GetProgramivFunc = (QS_PFNGLGETPROGRAMIVPROC) SDL_GL_GetProcAddress("glGetProgramiv"); GL_GetProgramInfoLogFunc = (QS_PFNGLGETPROGRAMINFOLOGPROC) SDL_GL_GetProcAddress("glGetProgramInfoLog"); GL_CreateProgramFunc = (QS_PFNGLCREATEPROGRAMPROC) SDL_GL_GetProcAddress("glCreateProgram"); GL_AttachShaderFunc = (QS_PFNGLATTACHSHADERPROC) SDL_GL_GetProcAddress("glAttachShader"); GL_LinkProgramFunc = (QS_PFNGLLINKPROGRAMPROC) SDL_GL_GetProcAddress("glLinkProgram"); GL_BindAttribLocationFunc = (QS_PFNGLBINDATTRIBLOCATIONFUNC) SDL_GL_GetProcAddress("glBindAttribLocation"); GL_UseProgramFunc = (QS_PFNGLUSEPROGRAMPROC) SDL_GL_GetProcAddress("glUseProgram"); GL_GetAttribLocationFunc = (QS_PFNGLGETATTRIBLOCATIONPROC) SDL_GL_GetProcAddress("glGetAttribLocation"); GL_VertexAttribPointerFunc = (QS_PFNGLVERTEXATTRIBPOINTERPROC) SDL_GL_GetProcAddress("glVertexAttribPointer"); GL_EnableVertexAttribArrayFunc = (QS_PFNGLENABLEVERTEXATTRIBARRAYPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArray"); GL_DisableVertexAttribArrayFunc = (QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArray"); GL_GetUniformLocationFunc = (QS_PFNGLGETUNIFORMLOCATIONPROC) SDL_GL_GetProcAddress("glGetUniformLocation"); GL_Uniform1iFunc = (QS_PFNGLUNIFORM1IPROC) SDL_GL_GetProcAddress("glUniform1i"); GL_Uniform1fFunc = (QS_PFNGLUNIFORM1FPROC) SDL_GL_GetProcAddress("glUniform1f"); GL_Uniform3fFunc = (QS_PFNGLUNIFORM3FPROC) SDL_GL_GetProcAddress("glUniform3f"); GL_Uniform4fFunc = (QS_PFNGLUNIFORM4FPROC) SDL_GL_GetProcAddress("glUniform4f"); if (GL_CreateShaderFunc && GL_DeleteShaderFunc && GL_DeleteProgramFunc && GL_ShaderSourceFunc && GL_CompileShaderFunc && GL_GetShaderivFunc && GL_GetShaderInfoLogFunc && GL_GetProgramivFunc && GL_GetProgramInfoLogFunc && GL_CreateProgramFunc && GL_AttachShaderFunc && GL_LinkProgramFunc && GL_BindAttribLocationFunc && GL_UseProgramFunc && GL_GetAttribLocationFunc && GL_VertexAttribPointerFunc && GL_EnableVertexAttribArrayFunc && GL_DisableVertexAttribArrayFunc && GL_GetUniformLocationFunc && GL_Uniform1iFunc && GL_Uniform1fFunc && GL_Uniform3fFunc && GL_Uniform4fFunc) { Con_Printf("FOUND: GLSL\n"); gl_glsl_able = true; } else { Con_Warning ("GLSL not available\n"); } } else { Con_Warning ("OpenGL version < 2, GLSL not available\n"); } // GLSL gamma // if (COM_CheckParm("-noglslgamma")) Con_Warning ("GLSL gamma disabled at command line\n"); else if (gl_glsl_able) { gl_glsl_gamma_able = true; } else { Con_Warning ("GLSL gamma not available, using hardware gamma\n"); } // GLSL alias model rendering // if (COM_CheckParm("-noglslalias")) Con_Warning ("GLSL alias model rendering disabled at command line\n"); else if (gl_glsl_able && gl_vbo_able && gl_max_texture_units >= 3) { gl_glsl_alias_able = true; } else { Con_Warning ("GLSL alias model rendering not available, using Fitz renderer\n"); } } /* =============== GL_SetupState -- johnfitz does all the stuff from GL_Init that needs to be done every time a new GL render context is created =============== */ static void GL_SetupState (void) { glClearColor (0.15,0.15,0.15,0); //johnfitz -- originally 1,0,0,0 glCullFace(GL_BACK); //johnfitz -- glquake used CCW with backwards culling -- let's do it right glFrontFace(GL_CW); //johnfitz -- glquake used CCW with backwards culling -- let's do it right glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.666); glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); glShadeModel (GL_FLAT); glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //johnfitz glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glDepthRange (0, 1); //johnfitz -- moved here becuase gl_ztrick is gone. glDepthFunc (GL_LEQUAL); //johnfitz -- moved here becuase gl_ztrick is gone. } /* =============== GL_Init =============== */ static void GL_Init (void) { gl_vendor = (const char *) glGetString (GL_VENDOR); gl_renderer = (const char *) glGetString (GL_RENDERER); gl_version = (const char *) glGetString (GL_VERSION); gl_extensions = (const char *) glGetString (GL_EXTENSIONS); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); if (gl_version == NULL || sscanf(gl_version, "%d.%d", &gl_version_major, &gl_version_minor) < 2) { gl_version_major = 0; gl_version_minor = 0; } if (gl_extensions_nice != NULL) Z_Free (gl_extensions_nice); gl_extensions_nice = GL_MakeNiceExtensionsList (gl_extensions); GL_CheckExtensions (); //johnfitz #ifdef __APPLE__ // ericw -- enable multi-threaded OpenGL, gives a decent FPS boost. // https://developer.apple.com/library/mac/technotes/tn2085/ if (host_parms->numcpus > 1 && kCGLNoError != CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine)) { Con_Warning ("Couldn't enable multi-threaded OpenGL"); } #endif //johnfitz -- intel video workarounds from Baker if (!strcmp(gl_vendor, "Intel")) { Con_Printf ("Intel Display Adapter detected, enabling gl_clear\n"); Cbuf_AddText ("gl_clear 1"); } //johnfitz GLAlias_CreateShaders (); GLWorld_CreateShaders (); GL_ClearBufferBindings (); } /* ================= GL_BeginRendering -- sets values of glx, gly, glwidth, glheight ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = vid.width; *height = vid.height; } /* ================= GL_EndRendering ================= */ void GL_EndRendering (void) { if (!scr_skipupdate) { #if defined(USE_SDL2) SDL_GL_SwapWindow(draw_context); #else SDL_GL_SwapBuffers(); #endif } } void VID_Shutdown (void) { if (vid_initialized) { VID_Gamma_Shutdown (); //johnfitz SDL_QuitSubSystem(SDL_INIT_VIDEO); draw_context = NULL; #if defined(USE_SDL2) gl_context = NULL; #endif PL_VID_Shutdown(); } } /* =================================================================== MAIN WINDOW =================================================================== */ /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } //========================================================================== // // COMMANDS // //========================================================================== /* ================= VID_DescribeCurrentMode_f ================= */ static void VID_DescribeCurrentMode_f (void) { if (draw_context) Con_Printf("%dx%dx%d %dHz %s\n", VID_GetCurrentWidth(), VID_GetCurrentHeight(), VID_GetCurrentBPP(), VID_GetCurrentRefreshRate(), VID_GetFullscreen() ? "fullscreen" : "windowed"); } /* ================= VID_DescribeModes_f -- johnfitz -- changed formatting, and added refresh rates after each mode. ================= */ static void VID_DescribeModes_f (void) { int i; int lastwidth, lastheight, lastbpp, count; lastwidth = lastheight = lastbpp = count = 0; for (i = 0; i < nummodes; i++) { if (lastwidth != modelist[i].width || lastheight != modelist[i].height || lastbpp != modelist[i].bpp) { if (count > 0) Con_SafePrintf ("\n"); Con_SafePrintf (" %4i x %4i x %i : %i", modelist[i].width, modelist[i].height, modelist[i].bpp, modelist[i].refreshrate); lastwidth = modelist[i].width; lastheight = modelist[i].height; lastbpp = modelist[i].bpp; count++; } } Con_Printf ("\n%i modes\n", count); } /* =================== VID_FSAA_f -- ericw -- warn that vid_fsaa requires engine restart =================== */ static void VID_FSAA_f (cvar_t *var) { // don't print the warning if vid_fsaa is set during startup if (vid_initialized) Con_Printf("%s %d requires engine restart to take effect\n", var->name, (int)var->value); } //========================================================================== // // INIT // //========================================================================== /* ================= VID_InitModelist ================= */ static void VID_InitModelist (void) { #if defined(USE_SDL2) const int sdlmodes = SDL_GetNumDisplayModes(0); int i; nummodes = 0; for (i = 0; i < sdlmodes; i++) { SDL_DisplayMode mode; if (nummodes >= MAX_MODE_LIST) break; if (SDL_GetDisplayMode(0, i, &mode) == 0) { modelist[nummodes].width = mode.w; modelist[nummodes].height = mode.h; modelist[nummodes].bpp = SDL_BITSPERPIXEL(mode.format); modelist[nummodes].refreshrate = mode.refresh_rate; nummodes++; } } #else /* !defined(USE_SDL2) */ SDL_PixelFormat format; SDL_Rect **modes; Uint32 flags; int i, j, k, originalnummodes, existingmode; int bpps[] = {16, 24, 32}; // enumerate >8 bpp modes originalnummodes = nummodes = 0; format.palette = NULL; // enumerate fullscreen modes flags = DEFAULT_SDL_FLAGS | SDL_FULLSCREEN; for (i = 0; i < (int)(sizeof(bpps)/sizeof(bpps[0])); i++) { if (nummodes >= MAX_MODE_LIST) break; format.BitsPerPixel = bpps[i]; modes = SDL_ListModes(&format, flags); if (modes == (SDL_Rect **)0 || modes == (SDL_Rect **)-1) continue; for (j = 0; modes[j]; j++) { if (modes[j]->w > MAXWIDTH || modes[j]->h > MAXHEIGHT || nummodes >= MAX_MODE_LIST) continue; modelist[nummodes].width = modes[j]->w; modelist[nummodes].height = modes[j]->h; modelist[nummodes].bpp = bpps[i]; modelist[nummodes].refreshrate = DEFAULT_REFRESHRATE; for (k=originalnummodes, existingmode = 0 ; k < nummodes ; k++) { if ((modelist[nummodes].width == modelist[k].width) && (modelist[nummodes].height == modelist[k].height) && (modelist[nummodes].bpp == modelist[k].bpp)) { existingmode = 1; break; } } if (!existingmode) { nummodes++; } } } if (nummodes == originalnummodes) Con_SafePrintf ("No fullscreen DIB modes found\n"); #endif /* !defined(USE_SDL2) */ } /* =================== VID_Init =================== */ void VID_Init (void) { static char vid_center[] = "SDL_VIDEO_CENTERED=center"; int p, width, height, refreshrate, bpp; int display_width, display_height, display_refreshrate, display_bpp; qboolean fullscreen; const char *read_vars[] = { "vid_fullscreen", "vid_width", "vid_height", "vid_refreshrate", "vid_bpp", "vid_vsync", "vid_fsaa", "vid_desktopfullscreen", "vid_borderless"}; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_fullscreen); //johnfitz Cvar_RegisterVariable (&vid_width); //johnfitz Cvar_RegisterVariable (&vid_height); //johnfitz Cvar_RegisterVariable (&vid_refreshrate); //johnfitz Cvar_RegisterVariable (&vid_bpp); //johnfitz Cvar_RegisterVariable (&vid_vsync); //johnfitz Cvar_RegisterVariable (&vid_fsaa); //QuakeSpasm Cvar_RegisterVariable (&vid_desktopfullscreen); //QuakeSpasm Cvar_RegisterVariable (&vid_borderless); //QuakeSpasm Cvar_SetCallback (&vid_fullscreen, VID_Changed_f); Cvar_SetCallback (&vid_width, VID_Changed_f); Cvar_SetCallback (&vid_height, VID_Changed_f); Cvar_SetCallback (&vid_refreshrate, VID_Changed_f); Cvar_SetCallback (&vid_bpp, VID_Changed_f); Cvar_SetCallback (&vid_vsync, VID_Changed_f); Cvar_SetCallback (&vid_fsaa, VID_FSAA_f); Cvar_SetCallback (&vid_desktopfullscreen, VID_Changed_f); Cvar_SetCallback (&vid_borderless, VID_Changed_f); Cmd_AddCommand ("vid_unlock", VID_Unlock); //johnfitz Cmd_AddCommand ("vid_restart", VID_Restart); //johnfitz Cmd_AddCommand ("vid_test", VID_Test); //johnfitz Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); putenv (vid_center); /* SDL_putenv is problematic in versions <= 1.2.9 */ if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) Sys_Error("Couldn't init SDL video: %s", SDL_GetError()); #if defined(USE_SDL2) { SDL_DisplayMode mode; if (SDL_GetDesktopDisplayMode(0, &mode) != 0) Sys_Error("Could not get desktop display mode"); display_width = mode.w; display_height = mode.h; display_refreshrate = mode.refresh_rate; display_bpp = SDL_BITSPERPIXEL(mode.format); } #else { const SDL_VideoInfo *info = SDL_GetVideoInfo(); display_width = info->current_w; display_height = info->current_h; display_refreshrate = DEFAULT_REFRESHRATE; display_bpp = info->vfmt->BitsPerPixel; } #endif Cvar_SetValueQuick (&vid_bpp, (float)display_bpp); if (CFG_OpenConfig("config.cfg") == 0) { CFG_ReadCvars(read_vars, num_readvars); CFG_CloseConfig(); } CFG_ReadCvarOverrides(read_vars, num_readvars); VID_InitModelist(); width = (int)vid_width.value; height = (int)vid_height.value; refreshrate = (int)vid_refreshrate.value; bpp = (int)vid_bpp.value; fullscreen = (int)vid_fullscreen.value; fsaa = (int)vid_fsaa.value; if (COM_CheckParm("-current")) { width = display_width; height = display_height; refreshrate = display_refreshrate; bpp = display_bpp; fullscreen = true; } else { p = COM_CheckParm("-width"); if (p && p < com_argc-1) { width = Q_atoi(com_argv[p+1]); if(!COM_CheckParm("-height")) height = width * 3 / 4; } p = COM_CheckParm("-height"); if (p && p < com_argc-1) { height = Q_atoi(com_argv[p+1]); if(!COM_CheckParm("-width")) width = height * 4 / 3; } p = COM_CheckParm("-refreshrate"); if (p && p < com_argc-1) refreshrate = Q_atoi(com_argv[p+1]); p = COM_CheckParm("-bpp"); if (p && p < com_argc-1) bpp = Q_atoi(com_argv[p+1]); if (COM_CheckParm("-window") || COM_CheckParm("-w")) fullscreen = false; else if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) fullscreen = true; } p = COM_CheckParm ("-fsaa"); if (p && p < com_argc-1) fsaa = atoi(com_argv[p+1]); if (!VID_ValidMode(width, height, refreshrate, bpp, fullscreen)) { width = (int)vid_width.value; height = (int)vid_height.value; refreshrate = (int)vid_refreshrate.value; bpp = (int)vid_bpp.value; fullscreen = (int)vid_fullscreen.value; } if (!VID_ValidMode(width, height, refreshrate, bpp, fullscreen)) { width = 640; height = 480; refreshrate = display_refreshrate; bpp = display_bpp; fullscreen = false; } vid_initialized = true; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); // set window icon PL_SetWindowIcon(); VID_SetMode (width, height, refreshrate, bpp, fullscreen); GL_Init (); GL_SetupState (); Cmd_AddCommand ("gl_info", GL_Info_f); //johnfitz //johnfitz -- removed code creating "glquake" subdirectory vid_menucmdfn = VID_Menu_f; //johnfitz vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; VID_Gamma_Init(); //johnfitz VID_Menu_Init(); //johnfitz //QuakeSpasm: current vid settings should override config file settings. //so we have to lock the vid mode from now until after all config files are read. vid_locked = true; } // new proc by S.A., called by alt-return key binding. void VID_Toggle (void) { // disabling the fast path completely because SDL_SetWindowFullscreen was changing // the window size on SDL2/WinXP and we weren't set up to handle it. --ericw // // TODO: Clear out the dead code, reinstate the fast path using SDL_SetWindowFullscreen // inside VID_SetMode, check window size to fix WinXP issue. This will // keep all the mode changing code in one place. static qboolean vid_toggle_works = false; qboolean toggleWorked; #if defined(USE_SDL2) Uint32 flags = 0; #endif S_ClearBuffer (); if (!vid_toggle_works) goto vrestart; else if (gl_vbo_able) { // disabling the fast path because with SDL 1.2 it invalidates VBOs (using them // causes a crash, sugesting that the fullscreen toggle created a new GL context, // although texture objects remain valid for some reason). // // SDL2 does promise window resizes / fullscreen changes preserve the GL context, // so we could use the fast path with SDL2. --ericw vid_toggle_works = false; goto vrestart; } #if defined(USE_SDL2) if (!VID_GetFullscreen()) { flags = vid_desktopfullscreen.value ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN; } toggleWorked = SDL_SetWindowFullscreen(draw_context, flags) == 0; #else toggleWorked = SDL_WM_ToggleFullScreen(draw_context) == 1; #endif if (toggleWorked) { Sbar_Changed (); // Sbar seems to need refreshing modestate = VID_GetFullscreen() ? MS_FULLSCREEN : MS_WINDOWED; VID_SyncCvars(); // update mouse grab if (key_dest == key_console || key_dest == key_menu) { if (modestate == MS_WINDOWED) IN_Deactivate(true); else if (modestate == MS_FULLSCREEN) IN_Activate(); } } else { vid_toggle_works = false; Con_DPrintf ("SDL_WM_ToggleFullScreen failed, attempting VID_Restart\n"); vrestart: Cvar_SetQuick (&vid_fullscreen, VID_GetFullscreen() ? "0" : "1"); Cbuf_AddText ("vid_restart\n"); } } /* ================ VID_SyncCvars -- johnfitz -- set vid cvars to match current video mode ================ */ void VID_SyncCvars (void) { if (draw_context) { if (!VID_GetDesktopFullscreen()) { Cvar_SetValueQuick (&vid_width, VID_GetCurrentWidth()); Cvar_SetValueQuick (&vid_height, VID_GetCurrentHeight()); } Cvar_SetValueQuick (&vid_refreshrate, VID_GetCurrentRefreshRate()); Cvar_SetValueQuick (&vid_bpp, VID_GetCurrentBPP()); Cvar_SetQuick (&vid_fullscreen, VID_GetFullscreen() ? "1" : "0"); // don't sync vid_desktopfullscreen, it's a user preference that // should persist even if we are in windowed mode. Cvar_SetQuick (&vid_vsync, VID_GetVSync() ? "1" : "0"); } vid_changed = false; } //========================================================================== // // NEW VIDEO MENU -- johnfitz // //========================================================================== enum { VID_OPT_MODE, VID_OPT_BPP, VID_OPT_REFRESHRATE, VID_OPT_FULLSCREEN, VID_OPT_VSYNC, VID_OPT_TEST, VID_OPT_APPLY, VIDEO_OPTIONS_ITEMS }; static int video_options_cursor = 0; typedef struct { int width,height; } vid_menu_mode; //TODO: replace these fixed-length arrays with hunk_allocated buffers static vid_menu_mode vid_menu_modes[MAX_MODE_LIST]; static int vid_menu_nummodes = 0; static int vid_menu_bpps[MAX_BPPS_LIST]; static int vid_menu_numbpps = 0; static int vid_menu_rates[MAX_RATES_LIST]; static int vid_menu_numrates=0; /* ================ VID_Menu_Init ================ */ static void VID_Menu_Init (void) { int i, j, h, w; for (i = 0; i < nummodes; i++) { w = modelist[i].width; h = modelist[i].height; for (j = 0; j < vid_menu_nummodes; j++) { if (vid_menu_modes[j].width == w && vid_menu_modes[j].height == h) break; } if (j == vid_menu_nummodes) { vid_menu_modes[j].width = w; vid_menu_modes[j].height = h; vid_menu_nummodes++; } } } /* ================ VID_Menu_RebuildBppList regenerates bpp list based on current vid_width and vid_height ================ */ static void VID_Menu_RebuildBppList (void) { int i, j, b; vid_menu_numbpps = 0; for (i = 0; i < nummodes; i++) { if (vid_menu_numbpps >= MAX_BPPS_LIST) break; //bpp list is limited to bpps available with current width/height if (modelist[i].width != vid_width.value || modelist[i].height != vid_height.value) continue; b = modelist[i].bpp; for (j = 0; j < vid_menu_numbpps; j++) { if (vid_menu_bpps[j] == b) break; } if (j == vid_menu_numbpps) { vid_menu_bpps[j] = b; vid_menu_numbpps++; } } //if there are no valid fullscreen bpps for this width/height, just pick one if (vid_menu_numbpps == 0) { Cvar_SetValueQuick (&vid_bpp, (float)modelist[0].bpp); return; } //if vid_bpp is not in the new list, change vid_bpp for (i = 0; i < vid_menu_numbpps; i++) if (vid_menu_bpps[i] == (int)(vid_bpp.value)) break; if (i == vid_menu_numbpps) Cvar_SetValueQuick (&vid_bpp, (float)vid_menu_bpps[0]); } /* ================ VID_Menu_RebuildRateList regenerates rate list based on current vid_width, vid_height and vid_bpp ================ */ static void VID_Menu_RebuildRateList (void) { int i,j,r; vid_menu_numrates=0; for (i=0;i<nummodes;i++) { //rate list is limited to rates available with current width/height/bpp if (modelist[i].width != vid_width.value || modelist[i].height != vid_height.value || modelist[i].bpp != vid_bpp.value) continue; r = modelist[i].refreshrate; for (j=0;j<vid_menu_numrates;j++) { if (vid_menu_rates[j] == r) break; } if (j==vid_menu_numrates) { vid_menu_rates[j] = r; vid_menu_numrates++; } } //if there are no valid fullscreen refreshrates for this width/height, just pick one if (vid_menu_numrates == 0) { Cvar_SetValue ("vid_refreshrate",(float)modelist[0].refreshrate); return; } //if vid_refreshrate is not in the new list, change vid_refreshrate for (i=0;i<vid_menu_numrates;i++) if (vid_menu_rates[i] == (int)(vid_refreshrate.value)) break; if (i==vid_menu_numrates) Cvar_SetValue ("vid_refreshrate",(float)vid_menu_rates[0]); } /* ================ VID_Menu_ChooseNextMode chooses next resolution in order, then updates vid_width and vid_height cvars, then updates bpp and refreshrate lists ================ */ static void VID_Menu_ChooseNextMode (int dir) { int i; if (vid_menu_nummodes) { for (i = 0; i < vid_menu_nummodes; i++) { if (vid_menu_modes[i].width == vid_width.value && vid_menu_modes[i].height == vid_height.value) break; } if (i == vid_menu_nummodes) //can't find it in list, so it must be a custom windowed res { i = 0; } else { i += dir; if (i >= vid_menu_nummodes) i = 0; else if (i < 0) i = vid_menu_nummodes-1; } Cvar_SetValueQuick (&vid_width, (float)vid_menu_modes[i].width); Cvar_SetValueQuick (&vid_height, (float)vid_menu_modes[i].height); VID_Menu_RebuildBppList (); VID_Menu_RebuildRateList (); } } /* ================ VID_Menu_ChooseNextBpp chooses next bpp in order, then updates vid_bpp cvar ================ */ static void VID_Menu_ChooseNextBpp (int dir) { int i; if (vid_menu_numbpps) { for (i = 0; i < vid_menu_numbpps; i++) { if (vid_menu_bpps[i] == vid_bpp.value) break; } if (i == vid_menu_numbpps) //can't find it in list { i = 0; } else { i += dir; if (i >= vid_menu_numbpps) i = 0; else if (i < 0) i = vid_menu_numbpps-1; } Cvar_SetValueQuick (&vid_bpp, (float)vid_menu_bpps[i]); } } /* ================ VID_Menu_ChooseNextRate chooses next refresh rate in order, then updates vid_refreshrate cvar ================ */ static void VID_Menu_ChooseNextRate (int dir) { int i; for (i=0;i<vid_menu_numrates;i++) { if (vid_menu_rates[i] == vid_refreshrate.value) break; } if (i==vid_menu_numrates) //can't find it in list { i = 0; } else { i+=dir; if (i>=vid_menu_numrates) i = 0; else if (i<0) i = vid_menu_numrates-1; } Cvar_SetValue ("vid_refreshrate",(float)vid_menu_rates[i]); } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { switch (key) { case K_ESCAPE: case K_BBUTTON: VID_SyncCvars (); //sync cvars before leaving menu. FIXME: there are other ways to leave menu S_LocalSound ("misc/menu1.wav"); M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); video_options_cursor--; if (video_options_cursor < 0) video_options_cursor = VIDEO_OPTIONS_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); video_options_cursor++; if (video_options_cursor >= VIDEO_OPTIONS_ITEMS) video_options_cursor = 0; break; case K_LEFTARROW: S_LocalSound ("misc/menu3.wav"); switch (video_options_cursor) { case VID_OPT_MODE: VID_Menu_ChooseNextMode (1); break; case VID_OPT_BPP: VID_Menu_ChooseNextBpp (1); break; case VID_OPT_REFRESHRATE: VID_Menu_ChooseNextRate (1); break; case VID_OPT_FULLSCREEN: Cbuf_AddText ("toggle vid_fullscreen\n"); break; case VID_OPT_VSYNC: Cbuf_AddText ("toggle vid_vsync\n"); // kristian break; default: break; } break; case K_RIGHTARROW: S_LocalSound ("misc/menu3.wav"); switch (video_options_cursor) { case VID_OPT_MODE: VID_Menu_ChooseNextMode (-1); break; case VID_OPT_BPP: VID_Menu_ChooseNextBpp (-1); break; case VID_OPT_REFRESHRATE: VID_Menu_ChooseNextRate (-1); break; case VID_OPT_FULLSCREEN: Cbuf_AddText ("toggle vid_fullscreen\n"); break; case VID_OPT_VSYNC: Cbuf_AddText ("toggle vid_vsync\n"); break; default: break; } break; case K_ENTER: case K_KP_ENTER: case K_ABUTTON: m_entersound = true; switch (video_options_cursor) { case VID_OPT_MODE: VID_Menu_ChooseNextMode (1); break; case VID_OPT_BPP: VID_Menu_ChooseNextBpp (1); break; case VID_OPT_REFRESHRATE: VID_Menu_ChooseNextRate (1); break; case VID_OPT_FULLSCREEN: Cbuf_AddText ("toggle vid_fullscreen\n"); break; case VID_OPT_VSYNC: Cbuf_AddText ("toggle vid_vsync\n"); break; case VID_OPT_TEST: Cbuf_AddText ("vid_test\n"); break; case VID_OPT_APPLY: Cbuf_AddText ("vid_restart\n"); key_dest = key_game; m_state = m_none; IN_Activate(); break; default: break; } break; default: break; } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { int i, y; qpic_t *p; const char *title; y = 4; // plaque p = Draw_CachePic ("gfx/qplaque.lmp"); M_DrawTransPic (16, y, p); //p = Draw_CachePic ("gfx/vidmodes.lmp"); p = Draw_CachePic ("gfx/p_option.lmp"); M_DrawPic ( (320-p->width)/2, y, p); y += 28; // title title = "Video Options"; M_PrintWhite ((320-8*strlen(title))/2, y, title); y += 16; // options for (i = 0; i < VIDEO_OPTIONS_ITEMS; i++) { switch (i) { case VID_OPT_MODE: M_Print (16, y, " Video mode"); M_Print (184, y, va("%ix%i", (int)vid_width.value, (int)vid_height.value)); break; case VID_OPT_BPP: M_Print (16, y, " Color depth"); M_Print (184, y, va("%i", (int)vid_bpp.value)); break; case VID_OPT_REFRESHRATE: M_Print (16, y, " Refresh rate"); M_Print (184, y, va("%i", (int)vid_refreshrate.value)); break; case VID_OPT_FULLSCREEN: M_Print (16, y, " Fullscreen"); M_DrawCheckbox (184, y, (int)vid_fullscreen.value); break; case VID_OPT_VSYNC: M_Print (16, y, " Vertical sync"); if (gl_swap_control) M_DrawCheckbox (184, y, (int)vid_vsync.value); else M_Print (184, y, "N/A"); break; case VID_OPT_TEST: y += 8; //separate the test and apply items M_Print (16, y, " Test changes"); break; case VID_OPT_APPLY: M_Print (16, y, " Apply changes"); break; } if (video_options_cursor == i) M_DrawCharacter (168, y, 12+((int)(realtime*4)&1)); y += 8; } } /* ================ VID_Menu_f ================ */ static void VID_Menu_f (void) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_video; m_entersound = true; //set all the cvars to match the current mode when entering the menu VID_SyncCvars (); //set up bpp and rate lists based on current cvars VID_Menu_RebuildBppList (); VID_Menu_RebuildRateList (); } ���������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cmd.c�����������������������������������������������������������������������0000644�0000000�0000000�00000037570�13133771234�014450� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cmd.c -- Quake script command processing module #include "quakedef.h" void Cmd_ForwardToServer (void); #define MAX_ALIAS_NAME 32 #define CMDLINE_LENGTH 256 //johnfitz -- mirrored in common.c typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; } cmdalias_t; cmdalias_t *cmd_alias; qboolean cmd_wait; //============================================================================= /* ============ Cmd_Wait_f Causes execution of the remainder of the command buffer to be delayed until next frame. This allows commands like: bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" ============ */ void Cmd_Wait_f (void) { cmd_wait = true; } /* ============================================================================= COMMAND BUFFER ============================================================================= */ sizebuf_t cmd_text; /* ============ Cbuf_Init ============ */ void Cbuf_Init (void) { SZ_Alloc (&cmd_text, 1<<18); // space for commands and script files. spike -- was 8192, but modern configs can be _HUGE_, at least if they contain lots of comments/docs for things. } /* ============ Cbuf_AddText Adds command text at the end of the buffer ============ */ void Cbuf_AddText (const char *text) { int l; l = Q_strlen (text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Con_Printf ("Cbuf_AddText: overflow\n"); return; } SZ_Write (&cmd_text, text, Q_strlen (text)); } /* ============ Cbuf_InsertText Adds command text immediately after the current command Adds a \n to the text FIXME: actually change the command buffer to do less copying ============ */ void Cbuf_InsertText (const char *text) { char *temp; int templen; // copy off any commands still remaining in the exec buffer templen = cmd_text.cursize; if (templen) { temp = (char *) Z_Malloc (templen); Q_memcpy (temp, cmd_text.data, templen); SZ_Clear (&cmd_text); } else temp = NULL; // shut up compiler // add the entire text of the file Cbuf_AddText (text); SZ_Write (&cmd_text, "\n", 1); // add the copied off data if (templen) { SZ_Write (&cmd_text, temp, templen); Z_Free (temp); } } /* ============ Cbuf_Execute ============ */ void Cbuf_Execute (void) { int i; char *text; char line[1024]; int quotes; while (cmd_text.cursize) { // find a \n or ; line break text = (char *)cmd_text.data; quotes = 0; for (i=0 ; i< cmd_text.cursize ; i++) { if (text[i] == '"') quotes++; if ( !(quotes&1) && text[i] == ';') break; // don't break if inside a quoted string if (text[i] == '\n') break; } if (i > (int)sizeof(line) - 1) { memcpy (line, text, sizeof(line) - 1); line[sizeof(line) - 1] = 0; } else { memcpy (line, text, i); line[i] = 0; } // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec, alias) can insert data at the // beginning of the text buffer if (i == cmd_text.cursize) cmd_text.cursize = 0; else { i++; cmd_text.cursize -= i; memmove (text, text + i, cmd_text.cursize); } // execute the command line Cmd_ExecuteString (line, src_command); if (cmd_wait) { // skip out while text still remains in buffer, leaving it // for next frame cmd_wait = false; break; } } } /* ============================================================================== SCRIPT COMMANDS ============================================================================== */ /* =============== Cmd_StuffCmds_f -- johnfitz -- rewritten to read the "cmdline" cvar, for use with dynamic mod loading Adds command line parameters as script statements Commands lead with a +, and continue until a - or another + quake +prog jctest.qp +cmd amlev1 quake -nosound +cmd amlev1 =============== */ void Cmd_StuffCmds_f (void) { extern cvar_t cmdline; char cmds[CMDLINE_LENGTH]; int i, j, plus; plus = false; // On Unix, argv[0] is command name for (i = 0, j = 0; cmdline.string[i]; i++) { if (cmdline.string[i] == '+') { plus = true; if (j > 0) { cmds[j-1] = ';'; cmds[j++] = ' '; } } else if (cmdline.string[i] == '-' && (i==0 || cmdline.string[i-1] == ' ')) //johnfitz -- allow hypenated map names with +map plus = false; else if (plus) cmds[j++] = cmdline.string[i]; } cmds[j] = 0; Cbuf_InsertText (cmds); } /* =============== Cmd_Exec_f =============== */ void Cmd_Exec_f (void) { char *f; int mark; if (Cmd_Argc () != 2) { Con_Printf ("exec <filename> : execute a script file\n"); return; } mark = Hunk_LowMark (); f = (char *)COM_LoadHunkFile (Cmd_Argv(1), NULL); if (!f) { Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); return; } Con_Printf ("execing %s\n",Cmd_Argv(1)); Cbuf_InsertText (f); Hunk_FreeToLowMark (mark); } /* =============== Cmd_Echo_f Just prints the rest of the line to the console =============== */ void Cmd_Echo_f (void) { int i; for (i=1 ; i<Cmd_Argc() ; i++) Con_Printf ("%s ",Cmd_Argv(i)); Con_Printf ("\n"); } /* =============== Cmd_Alias_f -- johnfitz -- rewritten Creates a new command that executes a command string (possibly ; seperated) =============== */ void Cmd_Alias_f (void) { cmdalias_t *a; char cmd[1024]; int i, c; const char *s; switch (Cmd_Argc()) { case 1: //list all aliases for (a = cmd_alias, i = 0; a; a=a->next, i++) Con_SafePrintf (" %s: %s", a->name, a->value); if (i) Con_SafePrintf ("%i alias command(s)\n", i); else Con_SafePrintf ("no alias commands found\n"); break; case 2: //output current alias string for (a = cmd_alias ; a ; a=a->next) if (!strcmp(Cmd_Argv(1), a->name)) Con_Printf (" %s: %s", a->name, a->value); break; default: //set alias string s = Cmd_Argv(1); if (strlen(s) >= MAX_ALIAS_NAME) { Con_Printf ("Alias name is too long\n"); return; } // if the alias allready exists, reuse it for (a = cmd_alias ; a ; a=a->next) { if (!strcmp(s, a->name)) { Z_Free (a->value); break; } } if (!a) { a = (cmdalias_t *) Z_Malloc (sizeof(cmdalias_t)); a->next = cmd_alias; cmd_alias = a; } strcpy (a->name, s); // copy the rest of the command line cmd[0] = 0; // start out with a null string c = Cmd_Argc(); for (i = 2; i < c; i++) { q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd)); if (i != c - 1) q_strlcat (cmd, " ", sizeof(cmd)); } if (q_strlcat(cmd, "\n", sizeof(cmd)) >= sizeof(cmd)) { Con_Printf("alias value too long!\n"); cmd[0] = '\n'; // nullify the string cmd[1] = 0; } a->value = Z_Strdup (cmd); break; } } /* =============== Cmd_Unalias_f -- johnfitz =============== */ void Cmd_Unalias_f (void) { cmdalias_t *a, *prev; switch (Cmd_Argc()) { default: case 1: Con_Printf("unalias <name> : delete alias\n"); break; case 2: prev = NULL; for (a = cmd_alias; a; a = a->next) { if (!strcmp(Cmd_Argv(1), a->name)) { if (prev) prev->next = a->next; else cmd_alias = a->next; Z_Free (a->value); Z_Free (a); return; } prev = a; } Con_Printf ("No alias named %s\n", Cmd_Argv(1)); break; } } /* =============== Cmd_Unaliasall_f -- johnfitz =============== */ void Cmd_Unaliasall_f (void) { cmdalias_t *blah; while (cmd_alias) { blah = cmd_alias->next; Z_Free(cmd_alias->value); Z_Free(cmd_alias); cmd_alias = blah; } } /* ============================================================================= COMMAND EXECUTION ============================================================================= */ typedef struct cmd_function_s { struct cmd_function_s *next; const char *name; xcommand_t function; } cmd_function_t; #define MAX_ARGS 80 static int cmd_argc; static char *cmd_argv[MAX_ARGS]; static char cmd_null_string[] = ""; static const char *cmd_args = NULL; cmd_source_t cmd_source; //johnfitz -- better tab completion //static cmd_function_t *cmd_functions; // possible commands to execute cmd_function_t *cmd_functions; // possible commands to execute //johnfitz /* ============ Cmd_List_f -- johnfitz ============ */ void Cmd_List_f (void) { cmd_function_t *cmd; const char *partial; int len, count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = Q_strlen(partial); } else { partial = NULL; len = 0; } count=0; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (partial && Q_strncmp (partial,cmd->name, len)) { continue; } Con_SafePrintf (" %s\n", cmd->name); count++; } Con_SafePrintf ("%i commands", count); if (partial) { Con_SafePrintf (" beginning with \"%s\"", partial); } Con_SafePrintf ("\n"); } static char *Cmd_TintSubstring(const char *in, const char *substr, char *out, size_t outsize) { int l; char *m; q_strlcpy(out, in, outsize); while ((m = q_strcasestr(out, substr))) { l = strlen(substr); while (l-->0) if (*m >= ' ' && *m < 127) *m++ |= 0x80; } return out; } /* ============ Cmd_Apropos_f scans through each command and cvar names+descriptions for the given substring we don't support descriptions, so this isn't really all that useful, but even without the sake of consistency it still combines cvars+commands under a single command. ============ */ void Cmd_Apropos_f(void) { char tmpbuf[256]; int hits = 0; cmd_function_t *cmd; cvar_t *var; const char *substr = Cmd_Argv (1); if (!*substr) { Con_SafePrintf ("%s <substring> : search through commands and cvars for the given substring\n", Cmd_Argv(0)); return; } for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (q_strcasestr(cmd->name, substr)) { hits++; Con_SafePrintf ("%s\n", Cmd_TintSubstring(cmd->name, substr, tmpbuf, sizeof(tmpbuf))); } } for (var=Cvar_FindVarAfter("", 0) ; var ; var=var->next) { if (q_strcasestr(var->name, substr)) { hits++; Con_SafePrintf ("%s (current value: \"%s\")\n", Cmd_TintSubstring(var->name, substr, tmpbuf, sizeof(tmpbuf)), var->string); } } if (!hits) Con_SafePrintf ("no cvars nor commands contain that substring\n"); } /* ============ Cmd_Init ============ */ void Cmd_Init (void) { Cmd_AddCommand ("cmdlist", Cmd_List_f); //johnfitz Cmd_AddCommand ("unalias", Cmd_Unalias_f); //johnfitz Cmd_AddCommand ("unaliasall", Cmd_Unaliasall_f); //johnfitz Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); Cmd_AddCommand ("exec",Cmd_Exec_f); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("alias",Cmd_Alias_f); Cmd_AddCommand ("cmd", Cmd_ForwardToServer); Cmd_AddCommand ("wait", Cmd_Wait_f); Cmd_AddCommand ("apropos", Cmd_Apropos_f); Cmd_AddCommand ("find", Cmd_Apropos_f); } /* ============ Cmd_Argc ============ */ int Cmd_Argc (void) { return cmd_argc; } /* ============ Cmd_Argv ============ */ const char *Cmd_Argv (int arg) { if (arg < 0 || arg >= cmd_argc) return cmd_null_string; return cmd_argv[arg]; } /* ============ Cmd_Args ============ */ const char *Cmd_Args (void) { return cmd_args; } /* ============ Cmd_TokenizeString Parses the given string into command line tokens. ============ */ void Cmd_TokenizeString (const char *text) { int i; // clear the args from the last string for (i=0 ; i<cmd_argc ; i++) Z_Free (cmd_argv[i]); cmd_argc = 0; cmd_args = NULL; while (1) { // skip whitespace up to a /n while (*text && *text <= ' ' && *text != '\n') { text++; } if (*text == '\n') { // a newline seperates commands in the buffer text++; break; } if (!*text) return; if (cmd_argc == 1) cmd_args = text; text = COM_Parse (text); if (!text) return; if (cmd_argc < MAX_ARGS) { cmd_argv[cmd_argc] = Z_Strdup (com_token); cmd_argc++; } } } /* ============ Cmd_AddCommand ============ */ void Cmd_AddCommand (const char *cmd_name, xcommand_t function) { cmd_function_t *cmd; cmd_function_t *cursor,*prev; //johnfitz -- sorted list insert if (host_initialized) // because hunk allocation would get stomped Sys_Error ("Cmd_AddCommand after host_initialized"); // fail if the command is a variable name if (Cvar_VariableString(cmd_name)[0]) { Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name); return; } // fail if the command already exists for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!Q_strcmp (cmd_name, cmd->name)) { Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); return; } } cmd = (cmd_function_t *) Hunk_Alloc (sizeof(cmd_function_t)); cmd->name = cmd_name; cmd->function = function; //johnfitz -- insert each entry in alphabetical order if (cmd_functions == NULL || strcmp(cmd->name, cmd_functions->name) < 0) //insert at front { cmd->next = cmd_functions; cmd_functions = cmd; } else //insert later { prev = cmd_functions; cursor = cmd_functions->next; while ((cursor != NULL) && (strcmp(cmd->name, cursor->name) > 0)) { prev = cursor; cursor = cursor->next; } cmd->next = prev->next; prev->next = cmd; } //johnfitz } /* ============ Cmd_Exists ============ */ qboolean Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!Q_strcmp (cmd_name,cmd->name)) return true; } return false; } /* ============ Cmd_CompleteCommand ============ */ const char *Cmd_CompleteCommand (const char *partial) { cmd_function_t *cmd; int len; len = Q_strlen(partial); if (!len) return NULL; // check functions for (cmd=cmd_functions ; cmd ; cmd=cmd->next) if (!Q_strncmp (partial,cmd->name, len)) return cmd->name; return NULL; } /* ============ Cmd_ExecuteString A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ void Cmd_ExecuteString (const char *text, cmd_source_t src) { cmd_function_t *cmd; cmdalias_t *a; cmd_source = src; Cmd_TokenizeString (text); // execute the command line if (!Cmd_Argc()) return; // no tokens // check functions for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!q_strcasecmp (cmd_argv[0],cmd->name)) { cmd->function (); return; } } // check alias for (a=cmd_alias ; a ; a=a->next) { if (!q_strcasecmp (cmd_argv[0], a->name)) { Cbuf_InsertText (a->value); return; } } // check cvars if (!Cvar_Command ()) Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); } /* =================== Cmd_ForwardToServer Sends the entire command line over to the server =================== */ void Cmd_ForwardToServer (void) { if (cls.state != ca_connected) { Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected MSG_WriteByte (&cls.message, clc_stringcmd); if (q_strcasecmp(Cmd_Argv(0), "cmd") != 0) { SZ_Print (&cls.message, Cmd_Argv(0)); SZ_Print (&cls.message, " "); } if (Cmd_Argc() > 1) SZ_Print (&cls.message, Cmd_Args()); else SZ_Print (&cls.message, "\n"); } /* ================ Cmd_CheckParm Returns the position (1 to argc-1) in the command's argument list where the given parameter apears, or 0 if not present ================ */ int Cmd_CheckParm (const char *parm) { int i; if (!parm) Sys_Error ("Cmd_CheckParm: NULL"); for (i = 1; i < Cmd_Argc (); i++) if (! q_strcasecmp (parm, Cmd_Argv (i))) return i; return 0; } ����������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/mathlib.h�������������������������������������������������������������������0000644�0000000�0000000�00000007437�12653757413�015342� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __MATHLIB_H #define __MATHLIB_H // mathlib.h #include <math.h> #ifndef M_PI #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #endif #define M_PI_DIV_180 (M_PI / 180.0) //johnfitz struct mplane_s; extern vec3_t vec3_origin; #define nanmask (255 << 23) /* 7F800000 */ #if 0 /* macro is violating strict aliasing rules */ #define IS_NAN(x) (((*(int *) (char *) &x) & nanmask) == nanmask) #else static inline int IS_NAN (float x) { union { float f; int i; } num; num.f = x; return ((num.i & nanmask) == nanmask); } #endif #define Q_rint(x) ((x) > 0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) //johnfitz -- from joequake #define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) #define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2]) #define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} #define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} #define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} //johnfitz -- courtesy of lordhavoc // QuakeSpasm: To avoid strict aliasing violations, use a float/int union instead of type punning. #define VectorNormalizeFast(_v)\ {\ union { float f; int i; } _y, _number;\ _number.f = DotProduct(_v, _v);\ if (_number.f != 0.0)\ {\ _y.i = 0x5f3759df - (_number.i >> 1);\ _y.f = _y.f * (1.5f - (_number.f * 0.5f * _y.f * _y.f));\ VectorScale(_v, _y.f, _v);\ }\ } void TurnVector (vec3_t out, const vec3_t forward, const vec3_t side, float angle); //johnfitz void VectorAngles (const vec3_t forward, vec3_t angles); //johnfitz void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); vec_t _DotProduct (vec3_t v1, vec3_t v2); void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); void _VectorCopy (vec3_t in, vec3_t out); int VectorCompare (vec3_t v1, vec3_t v2); vec_t VectorLength (vec3_t v); void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); float VectorNormalize (vec3_t v); // returns vector length void VectorInverse (vec3_t v); void VectorScale (vec3_t in, vec_t scale, vec3_t out); int Q_log2(int val); void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); void FloorDivMod (double numer, double denom, int *quotient, int *rem); fixed16_t Invert24To16(fixed16_t val); int GreatestCommonDivisor (int i1, int i2); void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); float anglemod(float a); #define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ (((p)->type < 3)? \ ( \ ((p)->dist <= (emins)[(p)->type])? \ 1 \ : \ ( \ ((p)->dist >= (emaxs)[(p)->type])?\ 2 \ : \ 3 \ ) \ ) \ : \ BoxOnPlaneSide( (emins), (emaxs), (p))) #endif /* __MATHLIB_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/wad.h�����������������������������������������������������������������������0000644�0000000�0000000�00000003766�12407762022�014463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_WAD_H #define _QUAKE_WAD_H //=============== // TYPES //=============== #define CMP_NONE 0 #define CMP_LZSS 1 #define TYP_NONE 0 #define TYP_LABEL 1 #define TYP_LUMPY 64 // 64 + grab command number #define TYP_PALETTE 64 #define TYP_QTEX 65 #define TYP_QPIC 66 #define TYP_SOUND 67 #define TYP_MIPTEX 68 #define WADFILENAME "gfx.wad" //johnfitz -- filename is now hard-coded for honesty typedef struct { int width, height; byte data[4]; // variably sized } qpic_t; typedef struct { char identification[4]; // should be WAD2 or 2DAW int numlumps; int infotableofs; } wadinfo_t; typedef struct { int filepos; int disksize; int size; // uncompressed char type; char compression; char pad1, pad2; char name[16]; // must be null terminated } lumpinfo_t; extern int wad_numlumps; extern lumpinfo_t *wad_lumps; extern byte *wad_base; void W_LoadWadFile (void); //johnfitz -- filename is now hard-coded for honesty void W_CleanupName (const char *in, char *out); lumpinfo_t *W_GetLumpinfo (const char *name); void *W_GetLumpName (const char *name); void *W_GetLumpNum (int num); void SwapPic (qpic_t *pic); #endif /* _QUAKE_WAD_H */ ����������quakespasm-0.93.0/Quake/snd_codec.c�����������������������������������������������������������������0000644�0000000�0000000�00000014340�13136360611�015610� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com> * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_codec.h" #include "snd_codeci.h" /* headers for individual codecs */ #include "snd_mikmod.h" #include "snd_xmp.h" #include "snd_umx.h" #include "snd_wave.h" #include "snd_flac.h" #include "snd_mp3.h" #include "snd_vorbis.h" #include "snd_opus.h" static snd_codec_t *codecs; /* ================= S_CodecRegister ================= */ static void S_CodecRegister(snd_codec_t *codec) { codec->next = codecs; codecs = codec; } /* ================= S_CodecInit ================= */ void S_CodecInit (void) { snd_codec_t *codec; codecs = NULL; /* Register in the inverse order * of codec choice preference: */ #ifdef USE_CODEC_UMX S_CodecRegister(&umx_codec); #endif #ifdef USE_CODEC_MIKMOD S_CodecRegister(&mikmod_codec); #endif #ifdef USE_CODEC_XMP S_CodecRegister(&xmp_codec); #endif #ifdef USE_CODEC_WAVE S_CodecRegister(&wav_codec); #endif #ifdef USE_CODEC_FLAC S_CodecRegister(&flac_codec); #endif #ifdef USE_CODEC_MP3 S_CodecRegister(&mp3_codec); #endif #ifdef USE_CODEC_VORBIS S_CodecRegister(&vorbis_codec); #endif #ifdef USE_CODEC_OPUS S_CodecRegister(&opus_codec); #endif codec = codecs; while (codec) { codec->initialize(); codec = codec->next; } } /* ================= S_CodecShutdown ================= */ void S_CodecShutdown (void) { snd_codec_t *codec = codecs; while (codec) { codec->shutdown(); codec = codec->next; } codecs = NULL; } /* ================= S_CodecOpenStream ================= */ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type) { snd_codec_t *codec; snd_stream_t *stream; if (type == CODECTYPE_NONE) { Con_Printf("Bad type for %s\n", filename); return NULL; } codec = codecs; while (codec) { if (type == codec->type) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown type for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } snd_stream_t *S_CodecOpenStreamExt (const char *filename) { snd_codec_t *codec; snd_stream_t *stream; const char *ext; ext = COM_FileGetExtension(filename); if (! *ext) { Con_Printf("No extension for %s\n", filename); return NULL; } codec = codecs; while (codec) { if (!q_strcasecmp(ext, codec->ext)) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown extension for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } snd_stream_t *S_CodecOpenStreamAny (const char *filename) { snd_codec_t *codec; snd_stream_t *stream; const char *ext; ext = COM_FileGetExtension(filename); if (! *ext) /* try all available */ { char tmp[MAX_QPATH]; codec = codecs; while (codec) { q_snprintf(tmp, sizeof(tmp), "%s.%s", filename, codec->ext); stream = S_CodecUtilOpen(tmp, codec); if (stream) { if (codec->codec_open(stream)) { stream->status = STREAM_PLAY; return stream; } S_CodecUtilClose(&stream); } codec = codec->next; } return NULL; } else /* use the name as is */ { codec = codecs; while (codec) { if (!q_strcasecmp(ext, codec->ext)) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown extension for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } } qboolean S_CodecForwardStream (snd_stream_t *stream, unsigned int type) { snd_codec_t *codec = codecs; while (codec) { if (type == codec->type) break; codec = codec->next; } if (!codec) return false; stream->codec = codec; return codec->codec_open(stream); } void S_CodecCloseStream (snd_stream_t *stream) { stream->status = STREAM_NONE; stream->codec->codec_close(stream); } int S_CodecRewindStream (snd_stream_t *stream) { return stream->codec->codec_rewind(stream); } int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { return stream->codec->codec_read(stream, bytes, buffer); } /* Util functions (used by codecs) */ snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec) { snd_stream_t *stream; FILE *handle; qboolean pak; long length; /* Try to open the file */ length = (long) COM_FOpenFile(filename, &handle, NULL); pak = file_from_pak; if (length == -1) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } /* Allocate a stream, Z_Malloc zeroes its content */ stream = (snd_stream_t *) Z_Malloc(sizeof(snd_stream_t)); stream->codec = codec; stream->fh.file = handle; stream->fh.start = ftell(handle); stream->fh.pos = 0; stream->fh.length = length; stream->fh.pak = stream->pak = pak; q_strlcpy(stream->name, filename, MAX_QPATH); return stream; } void S_CodecUtilClose(snd_stream_t **stream) { fclose((*stream)->fh.file); Z_Free(*stream); *stream = NULL; } int S_CodecIsAvailable (unsigned int type) { snd_codec_t *codec = codecs; while (codec) { if (type == codec->type) return codec->initialized; codec = codec->next; } return -1; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_screen.c�����������������������������������������������������������������0000644�0000000�0000000�00000060635�13142243454�015642� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // screen.c -- master for refresh, status bar, console, chat, notify, etc #include "quakedef.h" /* background clear rendering turtle/net/ram icons sbar centerprint / slow centerprint notify lines intermission / finale overlay loading plaque console menu required background clears required update regions syncronous draw mode or async One off screen buffer, with updates either copied or xblited Need to double buffer? async draw will require the refresh area to be cleared, because it will be xblited, but sync draw can just ignore it. sync draw CenterPrint () SlowPrint () Screen_Update (); Con_Printf (); net turn off messages option the refresh is allways rendered, unless the console is full screen console is: notify lines half full */ int glx, gly, glwidth, glheight; float scr_con_current; float scr_conlines; // lines of console to display //johnfitz -- new cvars cvar_t scr_menuscale = {"scr_menuscale", "1", CVAR_ARCHIVE}; cvar_t scr_sbarscale = {"scr_sbarscale", "1", CVAR_ARCHIVE}; cvar_t scr_sbaralpha = {"scr_sbaralpha", "0.75", CVAR_ARCHIVE}; cvar_t scr_conwidth = {"scr_conwidth", "0", CVAR_ARCHIVE}; cvar_t scr_conscale = {"scr_conscale", "1", CVAR_ARCHIVE}; cvar_t scr_crosshairscale = {"scr_crosshairscale", "1", CVAR_ARCHIVE}; cvar_t scr_showfps = {"scr_showfps", "0", CVAR_NONE}; cvar_t scr_clock = {"scr_clock", "0", CVAR_NONE}; //johnfitz cvar_t scr_viewsize = {"viewsize","100", CVAR_ARCHIVE}; cvar_t scr_fov = {"fov","90",CVAR_NONE}; // 10 - 170 cvar_t scr_fov_adapt = {"fov_adapt","1",CVAR_ARCHIVE}; cvar_t scr_conspeed = {"scr_conspeed","500",CVAR_ARCHIVE}; cvar_t scr_centertime = {"scr_centertime","2",CVAR_NONE}; cvar_t scr_showram = {"showram","1",CVAR_NONE}; cvar_t scr_showturtle = {"showturtle","0",CVAR_NONE}; cvar_t scr_showpause = {"showpause","1",CVAR_NONE}; cvar_t scr_printspeed = {"scr_printspeed","8",CVAR_NONE}; cvar_t gl_triplebuffer = {"gl_triplebuffer", "1", CVAR_ARCHIVE}; extern cvar_t crosshair; qboolean scr_initialized; // ready to draw qpic_t *scr_ram; qpic_t *scr_net; qpic_t *scr_turtle; int clearconsole; int clearnotify; vrect_t scr_vrect; qboolean scr_disabled_for_loading; qboolean scr_drawloading; float scr_disabled_time; int scr_tileclear_updates = 0; //johnfitz void SCR_ScreenShot_f (void); /* =============================================================================== CENTER PRINTING =============================================================================== */ char scr_centerstring[1024]; float scr_centertime_start; // for slow victory printing float scr_centertime_off; int scr_center_lines; int scr_erase_lines; int scr_erase_center; /* ============== SCR_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void SCR_CenterPrint (const char *str) //update centerprint data { strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); scr_centertime_off = scr_centertime.value; scr_centertime_start = cl.time; // count the number of lines for centering scr_center_lines = 1; str = scr_centerstring; while (*str) { if (*str == '\n') scr_center_lines++; str++; } } void SCR_DrawCenterString (void) //actually do the drawing { char *start; int l; int j; int x, y; int remaining; GL_SetCanvas (CANVAS_MENU); //johnfitz // the finale prints the characters one at a time if (cl.intermission) remaining = scr_printspeed.value * (cl.time - scr_centertime_start); else remaining = 9999; scr_erase_center = 0; start = scr_centerstring; if (scr_center_lines <= 4) y = 200*0.35; //johnfitz -- 320x200 coordinate system else y = 48; if (crosshair.value) y -= 8; do { // scan the width of the line for (l=0 ; l<40 ; l++) if (start[l] == '\n' || !start[l]) break; x = (320 - l*8)/2; //johnfitz -- 320x200 coordinate system for (j=0 ; j<l ; j++, x+=8) { Draw_Character (x, y, start[j]); //johnfitz -- stretch overlays if (!remaining--) return; } y += 8; while (*start && *start != '\n') start++; if (!*start) break; start++; // skip the \n } while (1); } void SCR_CheckDrawCenterString (void) { if (scr_center_lines > scr_erase_lines) scr_erase_lines = scr_center_lines; scr_centertime_off -= host_frametime; if (scr_centertime_off <= 0 && !cl.intermission) return; if (key_dest != key_game) return; if (cl.paused) //johnfitz -- don't show centerprint during a pause return; SCR_DrawCenterString (); } //============================================================================= /* ==================== AdaptFovx Adapt a 4:3 horizontal FOV to the current screen size using the "Hor+" scaling: 2.0 * atan(width / height * 3.0 / 4.0 * tan(fov_x / 2.0)) ==================== */ float AdaptFovx (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); if (!scr_fov_adapt.value) return fov_x; if ((x = height / width) == 0.75) return fov_x; a = atan(0.75 / x * tan(fov_x / 360 * M_PI)); a = a * 360 / M_PI; return a; } /* ==================== CalcFovy ==================== */ float CalcFovy (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); x = width / tan(fov_x / 360 * M_PI); a = atan(height / x); a = a * 360 / M_PI; return a; } /* ================= SCR_CalcRefdef Must be called whenever vid changes Internal use only ================= */ static void SCR_CalcRefdef (void) { float size, scale; //johnfitz -- scale // force the status bar to redraw Sbar_Changed (); scr_tileclear_updates = 0; //johnfitz // bound viewsize if (scr_viewsize.value < 30) Cvar_SetQuick (&scr_viewsize, "30"); if (scr_viewsize.value > 120) Cvar_SetQuick (&scr_viewsize, "120"); // bound fov if (scr_fov.value < 10) Cvar_SetQuick (&scr_fov, "10"); if (scr_fov.value > 170) Cvar_SetQuick (&scr_fov, "170"); vid.recalc_refdef = 0; //johnfitz -- rewrote this section size = scr_viewsize.value; scale = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); if (size >= 120 || cl.intermission || scr_sbaralpha.value < 1) //johnfitz -- scr_sbaralpha.value sb_lines = 0; else if (size >= 110) sb_lines = 24 * scale; else sb_lines = 48 * scale; size = q_min(scr_viewsize.value, 100) / 100; //johnfitz //johnfitz -- rewrote this section r_refdef.vrect.width = q_max(glwidth * size, 96); //no smaller than 96, for icons r_refdef.vrect.height = q_min(glheight * size, glheight - sb_lines); //make room for sbar r_refdef.vrect.x = (glwidth - r_refdef.vrect.width)/2; r_refdef.vrect.y = (glheight - sb_lines - r_refdef.vrect.height)/2; //johnfitz r_refdef.fov_x = AdaptFovx(scr_fov.value, vid.width, vid.height); r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); scr_vrect = r_refdef.vrect; } /* ================= SCR_SizeUp_f Keybinding command ================= */ void SCR_SizeUp_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.value+10); } /* ================= SCR_SizeDown_f Keybinding command ================= */ void SCR_SizeDown_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.value-10); } static void SCR_Callback_refdef (cvar_t *var) { vid.recalc_refdef = 1; } /* ================== SCR_Conwidth_f -- johnfitz -- called when scr_conwidth or scr_conscale changes ================== */ void SCR_Conwidth_f (cvar_t *var) { vid.recalc_refdef = 1; vid.conwidth = (scr_conwidth.value > 0) ? (int)scr_conwidth.value : (scr_conscale.value > 0) ? (int)(vid.width/scr_conscale.value) : vid.width; vid.conwidth = CLAMP (320, vid.conwidth, vid.width); vid.conwidth &= 0xFFFFFFF8; vid.conheight = vid.conwidth * vid.height / vid.width; } //============================================================================ /* ================== SCR_LoadPics -- johnfitz ================== */ void SCR_LoadPics (void) { scr_ram = Draw_PicFromWad ("ram"); scr_net = Draw_PicFromWad ("net"); scr_turtle = Draw_PicFromWad ("turtle"); } /* ================== SCR_Init ================== */ void SCR_Init (void) { //johnfitz -- new cvars Cvar_RegisterVariable (&scr_menuscale); Cvar_RegisterVariable (&scr_sbarscale); Cvar_SetCallback (&scr_sbaralpha, SCR_Callback_refdef); Cvar_RegisterVariable (&scr_sbaralpha); Cvar_SetCallback (&scr_conwidth, &SCR_Conwidth_f); Cvar_SetCallback (&scr_conscale, &SCR_Conwidth_f); Cvar_RegisterVariable (&scr_conwidth); Cvar_RegisterVariable (&scr_conscale); Cvar_RegisterVariable (&scr_crosshairscale); Cvar_RegisterVariable (&scr_showfps); Cvar_RegisterVariable (&scr_clock); //johnfitz Cvar_SetCallback (&scr_fov, SCR_Callback_refdef); Cvar_SetCallback (&scr_fov_adapt, SCR_Callback_refdef); Cvar_SetCallback (&scr_viewsize, SCR_Callback_refdef); Cvar_RegisterVariable (&scr_fov); Cvar_RegisterVariable (&scr_fov_adapt); Cvar_RegisterVariable (&scr_viewsize); Cvar_RegisterVariable (&scr_conspeed); Cvar_RegisterVariable (&scr_showram); Cvar_RegisterVariable (&scr_showturtle); Cvar_RegisterVariable (&scr_showpause); Cvar_RegisterVariable (&scr_centertime); Cvar_RegisterVariable (&scr_printspeed); Cvar_RegisterVariable (&gl_triplebuffer); Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); Cmd_AddCommand ("sizeup",SCR_SizeUp_f); Cmd_AddCommand ("sizedown",SCR_SizeDown_f); SCR_LoadPics (); //johnfitz scr_initialized = true; } //============================================================================ /* ============== SCR_DrawFPS -- johnfitz ============== */ void SCR_DrawFPS (void) { static double oldtime = 0; static double lastfps = 0; static int oldframecount = 0; double elapsed_time; int frames; elapsed_time = realtime - oldtime; frames = r_framecount - oldframecount; if (elapsed_time < 0 || frames < 0) { oldtime = realtime; oldframecount = r_framecount; return; } // update value every 3/4 second if (elapsed_time > 0.75) { lastfps = frames / elapsed_time; oldtime = realtime; oldframecount = r_framecount; } if (scr_showfps.value) { char st[16]; int x, y; sprintf (st, "%4.0f fps", lastfps); x = 320 - (strlen(st)<<3); y = 200 - 8; if (scr_clock.value) y -= 8; //make room for clock GL_SetCanvas (CANVAS_BOTTOMRIGHT); Draw_String (x, y, st); scr_tileclear_updates = 0; } } /* ============== SCR_DrawClock -- johnfitz ============== */ void SCR_DrawClock (void) { char str[12]; if (scr_clock.value == 1) { int minutes, seconds; minutes = cl.time / 60; seconds = ((int)cl.time)%60; sprintf (str,"%i:%i%i", minutes, seconds/10, seconds%10); } else return; //draw it GL_SetCanvas (CANVAS_BOTTOMRIGHT); Draw_String (320 - (strlen(str)<<3), 200 - 8, str); scr_tileclear_updates = 0; } /* ============== SCR_DrawDevStats ============== */ void SCR_DrawDevStats (void) { char str[40]; int y = 25-9; //9=number of lines to print int x = 0; //margin if (!devstats.value) return; GL_SetCanvas (CANVAS_BOTTOMLEFT); Draw_Fill (x, y*8, 19*8, 9*8, 0, 0.5); //dark rectangle sprintf (str, "devstats |Curr Peak"); Draw_String (x, (y++)*8-x, str); sprintf (str, "---------+---------"); Draw_String (x, (y++)*8-x, str); sprintf (str, "Edicts |%4i %4i", dev_stats.edicts, dev_peakstats.edicts); Draw_String (x, (y++)*8-x, str); sprintf (str, "Packet |%4i %4i", dev_stats.packetsize, dev_peakstats.packetsize); Draw_String (x, (y++)*8-x, str); sprintf (str, "Visedicts|%4i %4i", dev_stats.visedicts, dev_peakstats.visedicts); Draw_String (x, (y++)*8-x, str); sprintf (str, "Efrags |%4i %4i", dev_stats.efrags, dev_peakstats.efrags); Draw_String (x, (y++)*8-x, str); sprintf (str, "Dlights |%4i %4i", dev_stats.dlights, dev_peakstats.dlights); Draw_String (x, (y++)*8-x, str); sprintf (str, "Beams |%4i %4i", dev_stats.beams, dev_peakstats.beams); Draw_String (x, (y++)*8-x, str); sprintf (str, "Tempents |%4i %4i", dev_stats.tempents, dev_peakstats.tempents); Draw_String (x, (y++)*8-x, str); } /* ============== SCR_DrawRam ============== */ void SCR_DrawRam (void) { if (!scr_showram.value) return; if (!r_cache_thrash) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); } /* ============== SCR_DrawTurtle ============== */ void SCR_DrawTurtle (void) { static int count; if (!scr_showturtle.value) return; if (host_frametime < 0.1) { count = 0; return; } count++; if (count < 3) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); } /* ============== SCR_DrawNet ============== */ void SCR_DrawNet (void) { if (realtime - cl.last_received_message < 0.3) return; if (cls.demoplayback) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); } /* ============== DrawPause ============== */ void SCR_DrawPause (void) { qpic_t *pic; if (!cl.paused) return; if (!scr_showpause.value) // turn off for screenshots return; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/pause.lmp"); Draw_Pic ( (320 - pic->width)/2, (240 - 48 - pic->height)/2, pic); //johnfitz -- stretched menus scr_tileclear_updates = 0; //johnfitz } /* ============== SCR_DrawLoading ============== */ void SCR_DrawLoading (void) { qpic_t *pic; if (!scr_drawloading) return; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/loading.lmp"); Draw_Pic ( (320 - pic->width)/2, (240 - 48 - pic->height)/2, pic); //johnfitz -- stretched menus scr_tileclear_updates = 0; //johnfitz } /* ============== SCR_DrawCrosshair -- johnfitz ============== */ void SCR_DrawCrosshair (void) { if (!crosshair.value) return; GL_SetCanvas (CANVAS_CROSSHAIR); Draw_Character (-4, -4, '+'); //0,0 is center of viewport } //============================================================================= /* ================== SCR_SetUpToDrawConsole ================== */ void SCR_SetUpToDrawConsole (void) { //johnfitz -- let's hack away the problem of slow console when host_timescale is <0 extern cvar_t host_timescale; float timescale; //johnfitz Con_CheckResize (); if (scr_drawloading) return; // never a console with loading plaque // decide on the height of the console con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; if (con_forcedup) { scr_conlines = glheight; //full screen //johnfitz -- glheight instead of vid.height scr_con_current = scr_conlines; } else if (key_dest == key_console) scr_conlines = glheight/2; //half screen //johnfitz -- glheight instead of vid.height else scr_conlines = 0; //none visible timescale = (host_timescale.value > 0) ? host_timescale.value : 1; //johnfitz -- timescale if (scr_conlines < scr_con_current) { // ericw -- (glheight/600.0) factor makes conspeed resolution independent, using 800x600 as a baseline scr_con_current -= scr_conspeed.value*(glheight/600.0)*host_frametime/timescale; //johnfitz -- timescale if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { // ericw -- (glheight/600.0) scr_con_current += scr_conspeed.value*(glheight/600.0)*host_frametime/timescale; //johnfitz -- timescale if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } if (clearconsole++ < vid.numpages) Sbar_Changed (); if (!con_forcedup && scr_con_current) scr_tileclear_updates = 0; //johnfitz } /* ================== SCR_DrawConsole ================== */ void SCR_DrawConsole (void) { if (scr_con_current) { Con_DrawConsole (scr_con_current, true); clearconsole = 0; } else { if (key_dest == key_game || key_dest == key_message) Con_DrawNotify (); // only draw notify in game } } /* ============================================================================== SCREEN SHOTS ============================================================================== */ static void SCR_ScreenShot_Usage (void) { Con_Printf ("usage: screenshot <format> <quality>\n"); Con_Printf (" format must be \"png\" or \"tga\" or \"jpg\"\n"); Con_Printf (" quality must be 1-100\n"); return; } /* ================== SCR_ScreenShot_f -- johnfitz -- rewritten to use Image_WriteTGA ================== */ void SCR_ScreenShot_f (void) { byte *buffer; char ext[4]; char imagename[16]; //johnfitz -- was [80] char checkname[MAX_OSPATH]; int i, quality; qboolean ok; Q_strncpy (ext, "png", sizeof(ext)); if (Cmd_Argc () >= 2) { const char *requested_ext = Cmd_Argv (1); if (!q_strcasecmp ("png", requested_ext) || !q_strcasecmp ("tga", requested_ext) || !q_strcasecmp ("jpg", requested_ext)) Q_strncpy (ext, requested_ext, sizeof(ext)); else { SCR_ScreenShot_Usage (); return; } } // read quality as the 3rd param (only used for JPG) quality = 90; if (Cmd_Argc () >= 3) quality = Q_atoi (Cmd_Argv(2)); if (quality < 1 || quality > 100) { SCR_ScreenShot_Usage (); return; } // find a file name to save it to for (i=0; i<10000; i++) { q_snprintf (imagename, sizeof(imagename), "spasm%04i.%s", i, ext); // "fitz%04i.tga" q_snprintf (checkname, sizeof(checkname), "%s/%s", com_gamedir, imagename); if (Sys_FileTime(checkname) == -1) break; // file doesn't exist } if (i == 10000) { Con_Printf ("SCR_ScreenShot_f: Couldn't find an unused filename\n"); return; } //get data if (!(buffer = (byte *) malloc(glwidth*glheight*3))) { Con_Printf ("SCR_ScreenShot_f: Couldn't allocate memory\n"); return; } glPixelStorei (GL_PACK_ALIGNMENT, 1);/* for widths that aren't a multiple of 4 */ glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer); // now write the file if (!q_strncasecmp (ext, "png", sizeof(ext))) ok = Image_WritePNG (imagename, buffer, glwidth, glheight, 24, false); else if (!q_strncasecmp (ext, "tga", sizeof(ext))) ok = Image_WriteTGA (imagename, buffer, glwidth, glheight, 24, false); else if (!q_strncasecmp (ext, "jpg", sizeof(ext))) ok = Image_WriteJPG (imagename, buffer, glwidth, glheight, 24, quality, false); else ok = false; if (ok) Con_Printf ("Wrote %s\n", imagename); else Con_Printf ("SCR_ScreenShot_f: Couldn't create %s\n", imagename); free (buffer); } //============================================================================= /* =============== SCR_BeginLoadingPlaque ================ */ void SCR_BeginLoadingPlaque (void) { S_StopAllSounds (true); if (cls.state != ca_connected) return; if (cls.signon != SIGNONS) return; // redraw with no console and the loading plaque Con_ClearNotify (); scr_centertime_off = 0; scr_con_current = 0; scr_drawloading = true; Sbar_Changed (); SCR_UpdateScreen (); scr_drawloading = false; scr_disabled_for_loading = true; scr_disabled_time = realtime; } /* =============== SCR_EndLoadingPlaque ================ */ void SCR_EndLoadingPlaque (void) { scr_disabled_for_loading = false; Con_ClearNotify (); } //============================================================================= const char *scr_notifystring; qboolean scr_drawdialog; void SCR_DrawNotifyString (void) { const char *start; int l; int j; int x, y; GL_SetCanvas (CANVAS_MENU); //johnfitz start = scr_notifystring; y = 200 * 0.35; //johnfitz -- stretched overlays do { // scan the width of the line for (l=0 ; l<40 ; l++) if (start[l] == '\n' || !start[l]) break; x = (320 - l*8)/2; //johnfitz -- stretched overlays for (j=0 ; j<l ; j++, x+=8) Draw_Character (x, y, start[j]); y += 8; while (*start && *start != '\n') start++; if (!*start) break; start++; // skip the \n } while (1); } /* ================== SCR_ModalMessage Displays a text string in the center of the screen and waits for a Y or N keypress. ================== */ int SCR_ModalMessage (const char *text, float timeout) //johnfitz -- timeout { double time1, time2; //johnfitz -- timeout int lastkey, lastchar; if (cls.state == ca_dedicated) return true; scr_notifystring = text; // draw a fresh screen scr_drawdialog = true; SCR_UpdateScreen (); scr_drawdialog = false; S_ClearBuffer (); // so dma doesn't loop current sound time1 = Sys_DoubleTime () + timeout; //johnfitz -- timeout time2 = 0.0f; //johnfitz -- timeout Key_BeginInputGrab (); do { Sys_SendKeyEvents (); Key_GetGrabbedInput (&lastkey, &lastchar); Sys_Sleep (16); if (timeout) time2 = Sys_DoubleTime (); //johnfitz -- zero timeout means wait forever. } while (lastchar != 'y' && lastchar != 'Y' && lastchar != 'n' && lastchar != 'N' && lastkey != K_ESCAPE && lastkey != K_ABUTTON && lastkey != K_BBUTTON && time2 <= time1); Key_EndInputGrab (); // SCR_UpdateScreen (); //johnfitz -- commented out //johnfitz -- timeout if (time2 > time1) return false; //johnfitz return (lastchar == 'y' || lastchar == 'Y' || lastkey == K_ABUTTON); } //============================================================================= //johnfitz -- deleted SCR_BringDownConsole /* ================== SCR_TileClear johnfitz -- modified to use glwidth/glheight instead of vid.width/vid.height also fixed the dimentions of right and top panels also added scr_tileclear_updates ================== */ void SCR_TileClear (void) { //ericw -- added check for glsl gamma. TODO: remove this ugly optimization? if (scr_tileclear_updates >= vid.numpages && !gl_clear.value && !(gl_glsl_gamma_able && vid_gamma.value != 1)) return; scr_tileclear_updates++; if (r_refdef.vrect.x > 0) { // left Draw_TileClear (0, 0, r_refdef.vrect.x, glheight - sb_lines); // right Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, glwidth - r_refdef.vrect.x - r_refdef.vrect.width, glheight - sb_lines); } if (r_refdef.vrect.y > 0) { // top Draw_TileClear (r_refdef.vrect.x, 0, r_refdef.vrect.width, r_refdef.vrect.y); // bottom Draw_TileClear (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height, r_refdef.vrect.width, glheight - r_refdef.vrect.y - r_refdef.vrect.height - sb_lines); } } /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. WARNING: be very careful calling this from elsewhere, because the refresh needs almost the entire 256k of stack space! ================== */ void SCR_UpdateScreen (void) { vid.numpages = (gl_triplebuffer.value) ? 3 : 2; if (scr_disabled_for_loading) { if (realtime - scr_disabled_time > 60) { scr_disabled_for_loading = false; Con_Printf ("load failed.\n"); } else return; } if (!scr_initialized || !con_initialized) return; // not initialized yet GL_BeginRendering (&glx, &gly, &glwidth, &glheight); // // determine size of refresh window // if (vid.recalc_refdef) SCR_CalcRefdef (); // // do 3D refresh drawing, and then update the screen // SCR_SetUpToDrawConsole (); V_RenderView (); GL_Set2D (); //FIXME: only call this when needed SCR_TileClear (); if (scr_drawdialog) //new game confirm { if (con_forcedup) Draw_ConsoleBackground (); else Sbar_Draw (); Draw_FadeScreen (); SCR_DrawNotifyString (); } else if (scr_drawloading) //loading { SCR_DrawLoading (); Sbar_Draw (); } else if (cl.intermission == 1 && key_dest == key_game) //end of level { Sbar_IntermissionOverlay (); } else if (cl.intermission == 2 && key_dest == key_game) //end of episode { Sbar_FinaleOverlay (); SCR_CheckDrawCenterString (); } else { SCR_DrawCrosshair (); //johnfitz SCR_DrawRam (); SCR_DrawNet (); SCR_DrawTurtle (); SCR_DrawPause (); SCR_CheckDrawCenterString (); Sbar_Draw (); SCR_DrawDevStats (); //johnfitz SCR_DrawFPS (); //johnfitz SCR_DrawClock (); //johnfitz SCR_DrawConsole (); M_Draw (); } V_UpdateBlend (); //johnfitz -- V_UpdatePalette cleaned up and renamed GLSLGamma_GammaCorrect (); GL_EndRendering (); } ���������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/filenames.h�����������������������������������������������������������������0000644�0000000�0000000�00000015334�13021721124�015634� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Macros for taking apart, interpreting and processing file names. * * These are here because some non-Posix (a.k.a. DOSish) systems have * drive letter brain-damage at the beginning of an absolute file name, * use forward- and back-slash in path names interchangeably, and * some of them have case-insensitive file names. * * This was based on filenames.h from BFD, the Binary File Descriptor * library, Copyright (C) 2000-2016 Free Software Foundation, Inc., * and changed by O. Sezer <sezero@users.sourceforge.net> for our needs. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef FILENAMES_H #define FILENAMES_H #include <string.h> /* ---------------------- Windows, DOS, OS2: ---------------------- */ #if defined(__MSDOS__) || defined(__DOS__) || defined(__DJGPP__) || \ defined(_MSDOS) || defined(__OS2__) || defined(__EMX__) || \ defined(_WIN32) || defined(_Windows) || defined(__WINDOWS__) || \ defined(__NT__) || defined(__CYGWIN__) #define HAVE_DOS_BASED_FILE_SYSTEM 1 #define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 #define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':')) #define STRIP_DRIVE_SPEC(f) ((f) + 2) #define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') /* both '/' and '\\' work as dir separator. djgpp likes changing * '\\' into '/', so I define DIR_SEPARATOR_CHAR as '/' for djgpp, * '\\' otherwise. */ #ifdef __DJGPP__ #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #else #define DIR_SEPARATOR_CHAR '\\' #define DIR_SEPARATOR_STR "\\" #endif /* Note that IS_ABSOLUTE_PATH accepts d:foo as well, although it is only semi-absolute. This is because the users of IS_ABSOLUTE_PATH want to know whether to prepend the current working directory to a file name, which should not be done with a name like d:foo. */ #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f))) #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strchr(_the_path, '/'); char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline char *FIND_LAST_DIRSEP (char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strrchr(_the_path, '/'); char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { /* FIXME: What about C:FOO ? */ const char *p1 = strchr(_the_path, '/'); const char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { /* FIXME: What about C:FOO ? */ const char *p1 = strrchr(_the_path, '/'); const char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strchr(_the_path, '/'); char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strrchr(_the_path, '/'); char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } #endif /* C++ */ /* ----------------- AmigaOS, MorphOS, AROS, etc: ----------------- */ #elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) #define HAS_DRIVE_SPEC(f) (0) /* */ #define STRIP_DRIVE_SPEC(f) (f) /* */ #define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == ':') #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || (strchr((f), ':'))) #define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (char *_the_path) { char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { const char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { const char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } #endif /* C++ */ /* ---------------------- assumed UNIX-ish : ---------------------- */ #else /* */ #define IS_DIR_SEPARATOR(c) ((c) == '/') #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) #define HAS_DRIVE_SPEC(f) (0) #define STRIP_DRIVE_SPEC(f) (f) #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (char *_the_path) { return strrchr(_the_path, '/'); } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { return strchr(_the_path, '/'); } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { return strrchr(_the_path, '/'); } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { return strrchr(_the_path, '/'); } #endif /* C++ */ #endif #endif /* FILENAMES_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_main.c������������������������������������������������������������������0000644�0000000�0000000�00000041450�12506735242�015471� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" qsocket_t *net_activeSockets = NULL; qsocket_t *net_freeSockets = NULL; int net_numsockets = 0; qboolean ipxAvailable = false; qboolean tcpipAvailable = false; int net_hostport; int DEFAULTnet_hostport = 26000; char my_ipx_address[NET_NAMELEN]; char my_tcpip_address[NET_NAMELEN]; static qboolean listening = false; qboolean slistInProgress = false; qboolean slistSilent = false; qboolean slistLocal = true; static double slistStartTime; static int slistLastShown; static void Slist_Send (void *); static void Slist_Poll (void *); static PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; static PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll}; sizebuf_t net_message; int net_activeconnections = 0; int messagesSent = 0; int messagesReceived = 0; int unreliableMessagesSent = 0; int unreliableMessagesReceived = 0; static cvar_t net_messagetimeout = {"net_messagetimeout","300",CVAR_NONE}; cvar_t hostname = {"hostname", "UNNAMED", CVAR_NONE}; // these two macros are to make the code more readable #define sfunc net_drivers[sock->driver] #define dfunc net_drivers[net_driverlevel] int net_driverlevel; double net_time; double SetNetTime (void) { net_time = Sys_DoubleTime(); return net_time; } /* =================== NET_NewQSocket Called by drivers when a new communications endpoint is required The sequence and buffer fields will be filled in properly =================== */ qsocket_t *NET_NewQSocket (void) { qsocket_t *sock; if (net_freeSockets == NULL) return NULL; if (net_activeconnections >= svs.maxclients) return NULL; // get one from free list sock = net_freeSockets; net_freeSockets = sock->next; // add it to active list sock->next = net_activeSockets; net_activeSockets = sock; sock->disconnected = false; sock->connecttime = net_time; Q_strcpy (sock->address,"UNSET ADDRESS"); sock->driver = net_driverlevel; sock->socket = 0; sock->driverdata = NULL; sock->canSend = true; sock->sendNext = false; sock->lastMessageTime = net_time; sock->ackSequence = 0; sock->sendSequence = 0; sock->unreliableSendSequence = 0; sock->sendMessageLength = 0; sock->receiveSequence = 0; sock->unreliableReceiveSequence = 0; sock->receiveMessageLength = 0; return sock; } void NET_FreeQSocket(qsocket_t *sock) { qsocket_t *s; // remove it from active list if (sock == net_activeSockets) net_activeSockets = net_activeSockets->next; else { for (s = net_activeSockets; s; s = s->next) { if (s->next == sock) { s->next = sock->next; break; } } if (!s) Sys_Error ("NET_FreeQSocket: not active"); } // add it to free list sock->next = net_freeSockets; net_freeSockets = sock; sock->disconnected = true; } double NET_QSocketGetTime (const qsocket_t *s) { return s->connecttime; } const char *NET_QSocketGetAddressString (const qsocket_t *s) { return s->address; } static void NET_Listen_f (void) { if (Cmd_Argc () != 2) { Con_Printf ("\"listen\" is \"%d\"\n", listening ? 1 : 0); return; } listening = Q_atoi(Cmd_Argv(1)) ? true : false; for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.Listen (listening); } } static void MaxPlayers_f (void) { int n; if (Cmd_Argc () != 2) { Con_Printf ("\"maxplayers\" is \"%d\"\n", svs.maxclients); return; } if (sv.active) { Con_Printf ("maxplayers can not be changed while a server is running.\n"); return; } n = Q_atoi(Cmd_Argv(1)); if (n < 1) n = 1; if (n > svs.maxclientslimit) { n = svs.maxclientslimit; Con_Printf ("\"maxplayers\" set to \"%d\"\n", n); } if ((n == 1) && listening) Cbuf_AddText ("listen 0\n"); if ((n > 1) && (!listening)) Cbuf_AddText ("listen 1\n"); svs.maxclients = n; if (n == 1) Cvar_Set ("deathmatch", "0"); else Cvar_Set ("deathmatch", "1"); } static void NET_Port_f (void) { int n; if (Cmd_Argc () != 2) { Con_Printf ("\"port\" is \"%d\"\n", net_hostport); return; } n = Q_atoi(Cmd_Argv(1)); if (n < 1 || n > 65534) { Con_Printf ("Bad value, must be between 1 and 65534\n"); return; } DEFAULTnet_hostport = n; net_hostport = n; if (listening) { // force a change to the new port Cbuf_AddText ("listen 0\n"); Cbuf_AddText ("listen 1\n"); } } static void PrintSlistHeader(void) { Con_Printf("Server Map Users\n"); Con_Printf("--------------- --------------- -----\n"); slistLastShown = 0; } static void PrintSlist(void) { int n; for (n = slistLastShown; n < hostCacheCount; n++) { if (hostcache[n].maxusers) Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); else Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map); } slistLastShown = n; } static void PrintSlistTrailer(void) { if (hostCacheCount) Con_Printf("== end list ==\n\n"); else Con_Printf("No Quake servers found.\n\n"); } void NET_Slist_f (void) { if (slistInProgress) return; if (! slistSilent) { Con_Printf("Looking for Quake servers...\n"); PrintSlistHeader(); } slistInProgress = true; slistStartTime = Sys_DoubleTime(); SchedulePollProcedure(&slistSendProcedure, 0.0); SchedulePollProcedure(&slistPollProcedure, 0.1); hostCacheCount = 0; } void NET_SlistSort (void) { if (hostCacheCount > 1) { int i, j; hostcache_t temp; for (i = 0; i < hostCacheCount; i++) { for (j = i + 1; j < hostCacheCount; j++) { if (strcmp(hostcache[j].name, hostcache[i].name) < 0) { memcpy(&temp, &hostcache[j], sizeof(hostcache_t)); memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t)); memcpy(&hostcache[i], &temp, sizeof(hostcache_t)); } } } } } const char *NET_SlistPrintServer (int idx) { static char string[64]; if (idx < 0 || idx >= hostCacheCount) return ""; if (hostcache[idx].maxusers) { q_snprintf(string, sizeof(string), "%-15.15s %-15.15s %2u/%2u\n", hostcache[idx].name, hostcache[idx].map, hostcache[idx].users, hostcache[idx].maxusers); } else { q_snprintf(string, sizeof(string), "%-15.15s %-15.15s\n", hostcache[idx].name, hostcache[idx].map); } return string; } const char *NET_SlistPrintServerName (int idx) { if (idx < 0 || idx >= hostCacheCount) return ""; return hostcache[idx].cname; } static void Slist_Send (void *unused) { for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel)) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (true); } if ((Sys_DoubleTime() - slistStartTime) < 0.5) SchedulePollProcedure(&slistSendProcedure, 0.75); } static void Slist_Poll (void *unused) { for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel)) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (false); } if (! slistSilent) PrintSlist(); if ((Sys_DoubleTime() - slistStartTime) < 1.5) { SchedulePollProcedure(&slistPollProcedure, 0.1); return; } if (! slistSilent) PrintSlistTrailer(); slistInProgress = false; slistSilent = false; slistLocal = true; } /* =================== NET_Connect =================== */ int hostCacheCount = 0; hostcache_t hostcache[HOSTCACHESIZE]; qsocket_t *NET_Connect (const char *host) { qsocket_t *ret; int n; int numdrivers = net_numdrivers; SetNetTime(); if (host && *host == 0) host = NULL; if (host) { if (q_strcasecmp (host, "local") == 0) { numdrivers = 1; goto JustDoIt; } if (hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (q_strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } if (n < hostCacheCount) goto JustDoIt; } } slistSilent = host ? true : false; NET_Slist_f (); while (slistInProgress) NET_Poll(); if (host == NULL) { if (hostCacheCount != 1) return NULL; host = hostcache[0].cname; Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host); } if (hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } } } JustDoIt: for (net_driverlevel = 0; net_driverlevel < numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; ret = dfunc.Connect (host); if (ret) return ret; } if (host) { Con_Printf("\n"); PrintSlistHeader(); PrintSlist(); PrintSlistTrailer(); } return NULL; } /* =================== NET_CheckNewConnections =================== */ qsocket_t *NET_CheckNewConnections (void) { qsocket_t *ret; SetNetTime(); for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; if (!IS_LOOP_DRIVER(net_driverlevel) && listening == false) continue; ret = dfunc.CheckNewConnections (); if (ret) { return ret; } } return NULL; } /* =================== NET_Close =================== */ void NET_Close (qsocket_t *sock) { if (!sock) return; if (sock->disconnected) return; SetNetTime(); // call the driver_Close function sfunc.Close (sock); NET_FreeQSocket(sock); } /* ================= NET_GetMessage If there is a complete message, return it in net_message returns 0 if no data is waiting returns 1 if a message was received returns -1 if connection is invalid ================= */ int NET_GetMessage (qsocket_t *sock) { int ret; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_GetMessage: disconnected socket\n"); return -1; } SetNetTime(); ret = sfunc.QGetMessage(sock); // see if this connection has timed out if (ret == 0 && !IS_LOOP_DRIVER(sock->driver)) { if (net_time - sock->lastMessageTime > net_messagetimeout.value) { NET_Close(sock); return -1; } } if (ret > 0) { if (!IS_LOOP_DRIVER(sock->driver)) { sock->lastMessageTime = net_time; if (ret == 1) messagesReceived++; else if (ret == 2) unreliableMessagesReceived++; } } return ret; } /* ================== NET_SendMessage Try to send a complete length+message unit over the reliable stream. returns 0 if the message cannot be delivered reliably, but the connection is still considered valid returns 1 if the message was sent properly returns -1 if the connection died ================== */ int NET_SendMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_SendMessage: disconnected socket\n"); return -1; } SetNetTime(); r = sfunc.QSendMessage(sock, data); if (r == 1 && !IS_LOOP_DRIVER(sock->driver)) messagesSent++; return r; } int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_SendMessage: disconnected socket\n"); return -1; } SetNetTime(); r = sfunc.SendUnreliableMessage(sock, data); if (r == 1 && !IS_LOOP_DRIVER(sock->driver)) unreliableMessagesSent++; return r; } /* ================== NET_CanSendMessage Returns true or false if the given qsocket can currently accept a message to be transmitted. ================== */ qboolean NET_CanSendMessage (qsocket_t *sock) { if (!sock) return false; if (sock->disconnected) return false; SetNetTime(); return sfunc.CanSendMessage(sock); } int NET_SendToAll (sizebuf_t *data, double blocktime) { double start; int i; int count = 0; qboolean msg_init[MAX_SCOREBOARD]; /* did we write the message to the client's connection */ qboolean msg_sent[MAX_SCOREBOARD]; /* did the msg arrive its destination (canSend state). */ for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { /* if (!host_client->netconnection) continue; if (host_client->active) */ if (host_client->netconnection && host_client->active) { if (IS_LOOP_DRIVER(host_client->netconnection->driver)) { NET_SendMessage(host_client->netconnection, data); msg_init[i] = true; msg_sent[i] = true; continue; } count++; msg_init[i] = false; msg_sent[i] = false; } else { msg_init[i] = true; msg_sent[i] = true; } } start = Sys_DoubleTime(); while (count) { count = 0; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (! msg_init[i]) { if (NET_CanSendMessage (host_client->netconnection)) { msg_init[i] = true; NET_SendMessage(host_client->netconnection, data); } else { NET_GetMessage (host_client->netconnection); } count++; continue; } if (! msg_sent[i]) { if (NET_CanSendMessage (host_client->netconnection)) { msg_sent[i] = true; } else { NET_GetMessage (host_client->netconnection); } count++; continue; } } if ((Sys_DoubleTime() - start) > blocktime) break; } return count; } //============================================================================= /* ==================== NET_Init ==================== */ void NET_Init (void) { int i; qsocket_t *s; i = COM_CheckParm ("-port"); if (!i) i = COM_CheckParm ("-udpport"); if (!i) i = COM_CheckParm ("-ipxport"); if (i) { if (i < com_argc-1) DEFAULTnet_hostport = Q_atoi (com_argv[i+1]); else Sys_Error ("NET_Init: you must specify a number after -port"); } net_hostport = DEFAULTnet_hostport; net_numsockets = svs.maxclientslimit; if (cls.state != ca_dedicated) net_numsockets++; if (COM_CheckParm("-listen") || cls.state == ca_dedicated) listening = true; SetNetTime(); for (i = 0; i < net_numsockets; i++) { s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket"); s->next = net_freeSockets; net_freeSockets = s; s->disconnected = true; } // allocate space for network message buffer SZ_Alloc (&net_message, NET_MAXMESSAGE); Cvar_RegisterVariable (&net_messagetimeout); Cvar_RegisterVariable (&hostname); Cmd_AddCommand ("slist", NET_Slist_f); Cmd_AddCommand ("listen", NET_Listen_f); Cmd_AddCommand ("maxplayers", MaxPlayers_f); Cmd_AddCommand ("port", NET_Port_f); // initialize all the drivers for (i = net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].Init() == -1) continue; i++; net_drivers[net_driverlevel].initialized = true; if (listening) net_drivers[net_driverlevel].Listen (true); } /* Loop_Init() returns -1 for dedicated server case, * therefore the i == 0 check is correct */ if (i == 0 && cls.state == ca_dedicated ) { Sys_Error("Network not available!"); } if (*my_ipx_address) { Con_DPrintf("IPX address %s\n", my_ipx_address); } if (*my_tcpip_address) { Con_DPrintf("TCP/IP address %s\n", my_tcpip_address); } } /* ==================== NET_Shutdown ==================== */ void NET_Shutdown (void) { qsocket_t *sock; SetNetTime(); for (sock = net_activeSockets; sock; sock = sock->next) NET_Close(sock); // // shutdown the drivers // for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == true) { net_drivers[net_driverlevel].Shutdown (); net_drivers[net_driverlevel].initialized = false; } } } static PollProcedure *pollProcedureList = NULL; void NET_Poll(void) { PollProcedure *pp; SetNetTime(); for (pp = pollProcedureList; pp; pp = pp->next) { if (pp->nextTime > net_time) break; pollProcedureList = pp->next; pp->procedure(pp->arg); } } void SchedulePollProcedure(PollProcedure *proc, double timeOffset) { PollProcedure *pp, *prev; proc->nextTime = Sys_DoubleTime() + timeOffset; for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) { if (pp->nextTime >= proc->nextTime) break; prev = pp; } if (prev == NULL) { proc->next = pollProcedureList; pollProcedureList = proc; return; } proc->next = pp; prev->next = proc; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_mesh.c�������������������������������������������������������������������0000644�0000000�0000000�00000035134�12577611311�015315� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // gl_mesh.c: triangle model functions #include "quakedef.h" /* ================================================================= ALIAS MODEL DISPLAY LIST GENERATION ================================================================= */ qmodel_t *aliasmodel; aliashdr_t *paliashdr; int used[8192]; // qboolean // the command list holds counts and s/t values that are valid for // every frame int commands[8192]; int numcommands; // all frames will have their vertexes rearranged and expanded // so they are in the order expected by the command list int vertexorder[8192]; int numorder; int allverts, alltris; int stripverts[128]; int striptris[128]; int stripcount; /* ================ StripLength ================ */ int StripLength (int starttri, int startv) { int m1, m2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+2)%3]; m2 = last->vertindex[(startv+1)%3]; // look for a matching triangle nexttri: for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k=0 ; k<3 ; k++) { if (check->vertindex[k] != m1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge if (stripcount & 1) m2 = check->vertindex[ (k+2)%3 ]; else m1 = check->vertindex[ (k+2)%3 ]; stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j=starttri+1 ; j<pheader->numtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } /* =========== FanLength =========== */ int FanLength (int starttri, int startv) { int m1, m2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+0)%3]; m2 = last->vertindex[(startv+2)%3]; // look for a matching triangle nexttri: for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k=0 ; k<3 ; k++) { if (check->vertindex[k] != m1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge m2 = check->vertindex[ (k+2)%3 ]; stripverts[stripcount+2] = m2; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j=starttri+1 ; j<pheader->numtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } /* ================ BuildTris Generate a list of trifans or strips for the model, which holds for all frames ================ */ void BuildTris (void) { int i, j, k; int startv; float s, t; int len, bestlen, besttype; int bestverts[1024]; int besttris[1024]; int type; // // build tristrips // numorder = 0; numcommands = 0; memset (used, 0, sizeof(used)); for (i = 0; i < pheader->numtris; i++) { // pick an unused triangle and start the trifan if (used[i]) continue; bestlen = 0; besttype = 0; for (type = 0 ; type < 2 ; type++) // type = 1; { for (startv = 0; startv < 3; startv++) { if (type == 1) len = StripLength (i, startv); else len = FanLength (i, startv); if (len > bestlen) { besttype = type; bestlen = len; for (j = 0; j < bestlen+2; j++) bestverts[j] = stripverts[j]; for (j = 0; j < bestlen; j++) besttris[j] = striptris[j]; } } } // mark the tris on the best strip as used for (j = 0; j < bestlen; j++) used[besttris[j]] = 1; if (besttype == 1) commands[numcommands++] = (bestlen+2); else commands[numcommands++] = -(bestlen+2); for (j = 0; j < bestlen+2; j++) { int tmp; // emit a vertex into the reorder buffer k = bestverts[j]; vertexorder[numorder++] = k; // emit s/t coords into the commands stream s = stverts[k].s; t = stverts[k].t; if (!triangles[besttris[0]].facesfront && stverts[k].onseam) s += pheader->skinwidth / 2; // on back side s = (s + 0.5) / pheader->skinwidth; t = (t + 0.5) / pheader->skinheight; // *(float *)&commands[numcommands++] = s; // *(float *)&commands[numcommands++] = t; // NOTE: 4 == sizeof(int) // == sizeof(float) memcpy (&tmp, &s, 4); commands[numcommands++] = tmp; memcpy (&tmp, &t, 4); commands[numcommands++] = tmp; } } commands[numcommands++] = 0; // end of list marker Con_DPrintf2 ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); allverts += numorder; alltris += pheader->numtris; } static void GL_MakeAliasModelDisplayLists_VBO (void); static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr); /* ================ GL_MakeAliasModelDisplayLists ================ */ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) { int i, j; int *cmds; trivertx_t *verts; float hscale, vscale; //johnfitz -- padded skins int count; //johnfitz -- precompute texcoords for padded skins int *loadcmds; //johnfitz //johnfitz -- padded skins hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); //johnfitz aliasmodel = m; paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); //johnfitz -- generate meshes Con_DPrintf2 ("meshing %s...\n",m->name); BuildTris (); // save the data out paliashdr->poseverts = numorder; cmds = (int *) Hunk_Alloc (numcommands * 4); paliashdr->commands = (byte *)cmds - (byte *)paliashdr; //johnfitz -- precompute texcoords for padded skins loadcmds = commands; while(1) { *cmds++ = count = *loadcmds++; if (!count) break; if (count < 0) count = -count; do { *(float *)cmds++ = hscale * (*(float *)loadcmds++); *(float *)cmds++ = vscale * (*(float *)loadcmds++); } while (--count); } //johnfitz verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t)); paliashdr->posedata = (byte *)verts - (byte *)paliashdr; for (i=0 ; i<paliashdr->numposes ; i++) for (j=0 ; j<numorder ; j++) *verts++ = poseverts[i][vertexorder[j]]; // ericw GL_MakeAliasModelDisplayLists_VBO (); } unsigned int r_meshindexbuffer = 0; unsigned int r_meshvertexbuffer = 0; /* ================ GL_MakeAliasModelDisplayLists_VBO Saves data needed to build the VBO for this model on the hunk. Afterwards this is copied to Mod_Extradata. Original code by MH from RMQEngine ================ */ void GL_MakeAliasModelDisplayLists_VBO (void) { int i, j; int maxverts_vbo; trivertx_t *verts; unsigned short *indexes; aliasmesh_t *desc; if (!gl_glsl_alias_able) return; // first, copy the verts onto the hunk verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->numverts * sizeof(trivertx_t)); paliashdr->vertexes = (byte *)verts - (byte *)paliashdr; for (i=0 ; i<paliashdr->numposes ; i++) for (j=0 ; j<paliashdr->numverts ; j++) verts[i*paliashdr->numverts + j] = poseverts[i][j]; // there can never be more than this number of verts and we just put them all on the hunk maxverts_vbo = pheader->numtris * 3; desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo); // there will always be this number of indexes indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * maxverts_vbo); pheader->indexes = (intptr_t) indexes - (intptr_t) pheader; pheader->meshdesc = (intptr_t) desc - (intptr_t) pheader; pheader->numindexes = 0; pheader->numverts_vbo = 0; for (i = 0; i < pheader->numtris; i++) { for (j = 0; j < 3; j++) { int v; // index into hdr->vertexes unsigned short vertindex = triangles[i].vertindex[j]; // basic s/t coords int s = stverts[vertindex].s; int t = stverts[vertindex].t; // check for back side and adjust texcoord s if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2; // see does this vert already exist for (v = 0; v < pheader->numverts_vbo; v++) { // it could use the same xyz but have different s and t if (desc[v].vertindex == vertindex && (int) desc[v].st[0] == s && (int) desc[v].st[1] == t) { // exists; emit an index for it indexes[pheader->numindexes++] = v; // no need to check any more break; } } if (v == pheader->numverts_vbo) { // doesn't exist; emit a new vert and index indexes[pheader->numindexes++] = pheader->numverts_vbo; desc[pheader->numverts_vbo].vertindex = vertindex; desc[pheader->numverts_vbo].st[0] = s; desc[pheader->numverts_vbo++].st[1] = t; } } } // upload immediately GLMesh_LoadVertexBuffer (aliasmodel, pheader); } #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; /* ================ GLMesh_LoadVertexBuffer Upload the given alias model's mesh to a VBO Original code by MH from RMQEngine ================ */ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr) { int totalvbosize = 0; const aliasmesh_t *desc; const short *indexes; const trivertx_t *trivertexes; byte *vbodata; int f; if (!gl_glsl_alias_able) return; // count the sizes we need // ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not // mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t // (test case: roman1.bsp from arwop, 64mb heap) m->vboindexofs = 0; m->vboxyzofs = 0; totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm m->vbostofs = totalvbosize; totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t)); if (!hdr->numindexes) return; if (!totalvbosize) return; // grab the pointers to data in the extradata desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc); indexes = (short *) ((byte *) hdr + hdr->indexes); trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes); // upload indices buffer GL_DeleteBuffersFunc (1, &m->meshindexesvbo); GL_GenBuffersFunc (1, &m->meshindexesvbo); GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo); GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, hdr->numindexes * sizeof (unsigned short), indexes, GL_STATIC_DRAW); // create the vertex buffer (empty) vbodata = (byte *) malloc(totalvbosize); memset(vbodata, 0, totalvbosize); // fill in the vertices at the start of the buffer for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm { int v; meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t))); const trivertx_t *tv = trivertexes + (hdr->numverts * f); for (v = 0; v < hdr->numverts_vbo; v++) { trivertx_t trivert = tv[desc[v].vertindex]; xyz[v].xyz[0] = trivert.v[0]; xyz[v].xyz[1] = trivert.v[1]; xyz[v].xyz[2] = trivert.v[2]; xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression // map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char. // this introduces some error (less than 0.004), but the normals were very coarse // to begin with xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0]; xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1]; xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2]; xyz[v].normal[3] = 0; // unused; for 4-byte alignment } } // fill in the ST coords at the end of the buffer { meshst_t *st; float hscale, vscale; //johnfitz -- padded skins hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); //johnfitz st = (meshst_t *) (vbodata + m->vbostofs); for (f = 0; f < hdr->numverts_vbo; f++) { st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth; st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight; } } // upload vertexes buffer GL_DeleteBuffersFunc (1, &m->meshvbo); GL_GenBuffersFunc (1, &m->meshvbo); GL_BindBufferFunc (GL_ARRAY_BUFFER, m->meshvbo); GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, vbodata, GL_STATIC_DRAW); free (vbodata); // invalidate the cached bindings GL_ClearBufferBindings (); } /* ================ GLMesh_LoadVertexBuffers Loop over all precached alias models, and upload each one to a VBO. ================ */ void GLMesh_LoadVertexBuffers (void) { int j; qmodel_t *m; const aliashdr_t *hdr; if (!gl_glsl_alias_able) return; for (j = 1; j < MAX_MODELS; j++) { if (!(m = cl.model_precache[j])) break; if (m->type != mod_alias) continue; hdr = (const aliashdr_t *) Mod_Extradata (m); GLMesh_LoadVertexBuffer (m, hdr); } } /* ================ GLMesh_DeleteVertexBuffers Delete VBOs for all loaded alias models ================ */ void GLMesh_DeleteVertexBuffers (void) { int j; qmodel_t *m; if (!gl_glsl_alias_able) return; for (j = 1; j < MAX_MODELS; j++) { if (!(m = cl.model_precache[j])) break; if (m->type != mod_alias) continue; GL_DeleteBuffersFunc (1, &m->meshvbo); m->meshvbo = 0; GL_DeleteBuffersFunc (1, &m->meshindexesvbo); m->meshindexesvbo = 0; } GL_ClearBufferBindings (); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/build_cross_win64-sdl2.sh���������������������������������������������������0000755�0000000�0000000�00000000703�12475156650�020274� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Change this script to meet your needs and/or environment. TARGET=x86_64-w64-mingw32 PREFIX=/opt/cross_win64 PATH="$PREFIX/bin:$PATH" export PATH MAKE_CMD=make CC="$TARGET-gcc" AS="$TARGET-as" RANLIB="$TARGET-ranlib" AR="$TARGET-ar" WINDRES="$TARGET-windres" STRIP="$TARGET-strip" export PATH CC AS AR RANLIB WINDRES STRIP exec $MAKE_CMD USE_SDL2=1 CC=$CC AS=$AS RANLIB=$RANLIB AR=$AR WINDRES=$WINDRES STRIP=$STRIP -f Makefile.w64 $* �������������������������������������������������������������quakespasm-0.93.0/Quake/client.h��������������������������������������������������������������������0000644�0000000�0000000�00000022254�13147214764�015166� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _CLIENT_H_ #define _CLIENT_H_ // client.h typedef struct { int length; char map[MAX_STYLESTRING]; char average; //johnfitz char peak; //johnfitz } lightstyle_t; typedef struct { char name[MAX_SCOREBOARDNAME]; float entertime; int frags; int colors; // two 4 bit fields byte translations[VID_GRADES*256]; } scoreboard_t; typedef struct { int destcolor[3]; int percent; // 0-256 } cshift_t; #define CSHIFT_CONTENTS 0 #define CSHIFT_DAMAGE 1 #define CSHIFT_BONUS 2 #define CSHIFT_POWERUP 3 #define NUM_CSHIFTS 4 #define NAME_LENGTH 64 // // client_state_t should hold all pieces of the client state // #define SIGNONS 4 // signon messages to receive before connected #define MAX_DLIGHTS 64 //johnfitz -- was 32 typedef struct { vec3_t origin; float radius; float die; // stop lighting after this time float decay; // drop this each second float minlight; // don't add when contributing less int key; vec3_t color; //johnfitz -- lit support via lordhavoc } dlight_t; #define MAX_BEAMS 32 //johnfitz -- was 24 typedef struct { int entity; struct qmodel_s *model; float endtime; vec3_t start, end; } beam_t; #define MAX_MAPSTRING 2048 #define MAX_DEMOS 8 #define MAX_DEMONAME 16 typedef enum { ca_dedicated, // a dedicated server with no ability to start a client ca_disconnected, // full screen console with no connection ca_connected // valid netcon, talking to a server } cactive_t; // // the client_static_t structure is persistant through an arbitrary number // of server connections // typedef struct { cactive_t state; // personalization data sent to server char spawnparms[MAX_MAPSTRING]; // to restart a level // demo loop control int demonum; // -1 = don't play demos char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) qboolean demorecording; qboolean demoplayback; // did the user pause demo playback? (separate from cl.paused because we don't // want a svc_setpause inside the demo to actually pause demo playback). qboolean demopaused; qboolean timedemo; int forcetrack; // -1 = use normal cd track FILE *demofile; int td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start float td_starttime; // realtime at second frame of timedemo // connection information int signon; // 0 to SIGNONS struct qsocket_s *netcon; sizebuf_t message; // writing buffer to send to server } client_static_t; extern client_static_t cls; // // the client_state_t structure is wiped completely at every // server signon // typedef struct { int movemessages; // since connecting to this server // throw out the first couple, so the player // doesn't accidentally do something the // first frame usercmd_t cmd; // last command sent to the server // information for local display int stats[MAX_CL_STATS]; // health, etc int items; // inventory bit flags float item_gettime[32]; // cl.time of aquiring item, for blinking float faceanimtime; // use anim frame if cl.time < this cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types // the client maintains its own idea of view angles, which are // sent to the server each frame. The server sets punchangle when // the view is temporarliy offset, and an angle reset commands at the start // of each level and after teleporting. vec3_t mviewangles[2]; // during demo playback viewangles is lerped // between these vec3_t viewangles; vec3_t mvelocity[2]; // update by server, used for lean+bob // (0 is newest) vec3_t velocity; // lerped between mvelocity[0] and [1] vec3_t punchangle; // temporary offset // pitch drifting vars float idealpitch; float pitchvel; qboolean nodrift; float driftmove; double laststop; float viewheight; float crouch; // local amount for smoothing stepups qboolean paused; // send over by server qboolean onground; qboolean inwater; int intermission; // don't change view angle, full screen, etc int completed_time; // latched at intermission start double mtime[2]; // the timestamp of last two messages double time; // clients view of time, should be between // servertime and oldservertime to generate // a lerp point for other data double oldtime; // previous cl.time, time-oldtime is used // to decay light values and smooth step ups float last_received_message; // (realtime) for net trouble icon // // information that is static for the entire time connected to a server // struct qmodel_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; char mapname[128]; char levelname[128]; // for display on solo scoreboard //johnfitz -- was 40. int viewentity; // cl_entitites[cl.viewentity] = player int maxclients; int gametype; // refresh related state struct qmodel_s *worldmodel; // cl_entitites[0].model struct efrag_s *free_efrags; int num_efrags; int num_entities; // held in cl_entities array int num_statics; // held in cl_staticentities array entity_t viewent; // the gun model int cdtrack, looptrack; // cd audio // frag scoreboard scoreboard_t *scores; // [cl.maxclients] unsigned protocol; //johnfitz unsigned protocolflags; } client_state_t; // // cvars // extern cvar_t cl_name; extern cvar_t cl_color; extern cvar_t cl_upspeed; extern cvar_t cl_forwardspeed; extern cvar_t cl_backspeed; extern cvar_t cl_sidespeed; extern cvar_t cl_movespeedkey; extern cvar_t cl_yawspeed; extern cvar_t cl_pitchspeed; extern cvar_t cl_anglespeedkey; extern cvar_t cl_alwaysrun; // QuakeSpasm extern cvar_t cl_autofire; extern cvar_t cl_shownet; extern cvar_t cl_nolerp; extern cvar_t cfg_unbindall; extern cvar_t cl_pitchdriftspeed; extern cvar_t lookspring; extern cvar_t lookstrafe; extern cvar_t sensitivity; extern cvar_t m_pitch; extern cvar_t m_yaw; extern cvar_t m_forward; extern cvar_t m_side; #define MAX_TEMP_ENTITIES 256 //johnfitz -- was 64 #define MAX_STATIC_ENTITIES 4096 //ericw -- was 512 //johnfitz -- was 128 #define MAX_VISEDICTS 4096 // larger, now we support BSP2 extern client_state_t cl; // FIXME, allocate dynamically extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; extern dlight_t cl_dlights[MAX_DLIGHTS]; extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; extern beam_t cl_beams[MAX_BEAMS]; extern entity_t *cl_visedicts[MAX_VISEDICTS]; extern int cl_numvisedicts; extern entity_t *cl_entities; //johnfitz -- was a static array, now on hunk extern int cl_max_edicts; //johnfitz -- only changes when new map loads //============================================================================= // // cl_main // dlight_t *CL_AllocDlight (int key); void CL_DecayLights (void); void CL_Init (void); void CL_EstablishConnection (const char *host); void CL_Signon1 (void); void CL_Signon2 (void); void CL_Signon3 (void); void CL_Signon4 (void); void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_NextDemo (void); // // cl_input // typedef struct { int down[2]; // key nums holding it down int state; // low bit is down state } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; void CL_InitInput (void); void CL_SendCmd (void); void CL_SendMove (const usercmd_t *cmd); int CL_ReadFromServer (void); void CL_BaseMove (usercmd_t *cmd); void CL_ParseTEnt (void); void CL_UpdateTEnts (void); void CL_ClearState (void); // // cl_demo.c // void CL_StopPlayback (void); int CL_GetMessage (void); void CL_Stop_f (void); void CL_Record_f (void); void CL_PlayDemo_f (void); void CL_TimeDemo_f (void); // // cl_parse.c // void CL_ParseServerMessage (void); void CL_NewTranslation (int slot); // // view // void V_StartPitchDrift (void); void V_StopPitchDrift (void); void V_RenderView (void); //void V_UpdatePalette (void); //johnfitz void V_Register (void); void V_ParseDamage (void); void V_SetContentsColor (int contents); // // cl_tent // void CL_InitTEnts (void); void CL_SignonReply (void); // // chase // extern cvar_t chase_active; void Chase_Init (void); void TraceLine (vec3_t start, vec3_t end, vec3_t impact); void Chase_UpdateForClient (void); //johnfitz void Chase_UpdateForDrawing (void); //johnfitz #endif /* _CLIENT_H_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/OWMakefile.win32������������������������������������������������������������0000644�0000000�0000000�00000011634�13137021520�016367� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# makefile to build quakespasm.exe for Windows using Open Watcom: # wmake -f OWMakefile.win32 ### Enable/disable SDL2 USE_SDL2=0 ### Enable/disable codecs for streaming music support USE_CODEC_WAVE=1 USE_CODEC_FLAC=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 USE_CODEC_OPUS=1 # either mikmod or xmp USE_CODEC_MIKMOD=1 USE_CODEC_XMP=0 USE_CODEC_UMX=1 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis WINSOCK2= 0 # --------------------------- # build variables # --------------------------- CFLAGS_BASE = -zq -wx -bm -bt=nt -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 CFLAGS = $(CFLAGS_BASE) !ifneq USE_SDL2 1 SDL_CFLAGS = -I../Windows/SDL/include SDL_LIBS = ../Windows/SDL/watcom/SDL.lib !else SDL_CFLAGS = -I../Windows/SDL2/include SDL_LIBS = ../Windows/SDL2/watcom/SDL2.lib CFLAGS += -DUSE_SDL2 !endif !ifeq WINSOCK2 1 DEFWINSOCK =-D_USE_WINSOCK2 LIBWINSOCK = ws2_32.lib !else DEFWINSOCK = LIBWINSOCK = wsock32.lib !endif CFLAGS += $(DEFWINSOCK) NET_LIBS = $(LIBWINSOCK) # note: all codec libraries are static. CODEC_INC = -I../Windows/codecs/include LIBCODEC = ../Windows/codecs/x86-watcom/ !ifeq MP3LIB mad mp3_obj=snd_mp3.obj lib_mp3dec=$(LIBCODEC)mad.lib !endif !ifeq MP3LIB mpg123 mp3_obj=snd_mpg123.obj lib_mp3dec=$(LIBCODEC)mpg123.lib !endif !ifeq VORBISLIB vorbis cpp_vorbisdec= lib_vorbisdec=$(LIBCODEC)vorbisfile.lib $(LIBCODEC)vorbis.lib $(LIBCODEC)ogg.lib !endif !ifeq VORBISLIB tremor cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=$(LIBCODEC)vorbisidec.lib $(LIBCODEC)ogg.lib !endif CODECLIBS = !ifeq USE_CODEC_WAVE 1 CFLAGS+= -DUSE_CODEC_WAVE !endif !ifeq USE_CODEC_FLAC 1 CFLAGS+= -DUSE_CODEC_FLAC CFLAGS+= -DFLAC__NO_DLL CODECLIBS+= $(LIBCODEC)FLAC.lib !endif !ifeq USE_CODEC_OPUS 1 CFLAGS+= -DUSE_CODEC_OPUS CODECLIBS+= $(LIBCODEC)opusfile.lib $(LIBCODEC)opus.lib $(LIBCODEC)ogg.lib !endif !ifeq USE_CODEC_VORBIS 1 CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODECLIBS+= $(lib_vorbisdec) !endif !ifeq USE_CODEC_MP3 1 CFLAGS+= -DUSE_CODEC_MP3 CODECLIBS+= $(lib_mp3dec) !endif !ifeq USE_CODEC_MIKMOD 1 CFLAGS+= -DUSE_CODEC_MIKMOD CFLAGS+= -DMIKMOD_STATIC CODECLIBS+= $(LIBCODEC)mikmod.lib !endif !ifeq USE_CODEC_XMP 1 CFLAGS+= -DUSE_CODEC_XMP CFLAGS+= -DXMP_NO_DLL CODECLIBS+= $(LIBCODEC)libxmp.lib !endif !ifeq USE_CODEC_UMX 1 CFLAGS+= -DUSE_CODEC_UMX !endif CFLAGS+= $(CODEC_INC) COMMON_LIBS= opengl32.lib winmm.lib LIBS = $(CODECLIBS) $(SDL_LIBS) $(COMMON_LIBS) $(NET_LIBS) # --------------------------- # targets # --------------------------- all: quakespasm.exe # --------------------------- # rules # --------------------------- .EXTENSIONS: .res .rc .c.obj: wcc386 $(INCLUDES) $(CFLAGS) $(SDL_CFLAGS) -fo=$^@ $< SDL_win32_main.obj: ../Windows/SDL/main/SDL_win32_main.c wcc386 $(CFLAGS_BASE) $(SDL_CFLAGS) -fo=$^@ $< SDL_windows_main.obj: ../Windows/SDL2/main/SDL_windows_main.c wcc386 $(CFLAGS_BASE) $(SDL_CFLAGS) -I../Windows/SDL2/main -fo=$^@ $< quakespasm.res: ../Windows/QuakeSpasm.rc wrc -q -r -bt=nt -I../Windows -fo=$^@ $< # ---------------------------------------------------------------------------- # objects # ---------------------------------------------------------------------------- MUSIC_OBJS= bgmusic.obj & snd_codec.obj & snd_flac.obj & snd_wave.obj & snd_vorbis.obj & snd_opus.obj & $(mp3_obj) & snd_mikmod.obj & snd_xmp.obj & snd_umx.obj COMOBJ_SND = snd_dma.obj snd_mix.obj snd_mem.obj $(MUSIC_OBJS) SYSOBJ_SND = snd_sdl.obj SYSOBJ_CDA = cd_sdl.obj SYSOBJ_INPUT = in_sdl.obj SYSOBJ_GL_VID= gl_vidsdl.obj SYSOBJ_NET = net_win.obj net_wins.obj net_wipx.obj SYSOBJ_SYS = pl_win.obj sys_sdl_win.obj SYSOBJ_MAIN= main_sdl.obj !ifeq USE_SDL2 1 SYSOBJ_MAIN+= SDL_windows_main.obj !else SYSOBJ_MAIN+= SDL_win32_main.obj !endif SYSOBJ_RES = quakespasm.res GLOBJS = & gl_refrag.obj & gl_rlight.obj & gl_rmain.obj & gl_fog.obj & gl_rmisc.obj & r_part.obj & r_world.obj & gl_screen.obj & gl_sky.obj & gl_warp.obj & $(SYSOBJ_GL_VID) & gl_draw.obj & image.obj & gl_texmgr.obj & gl_mesh.obj & r_sprite.obj & r_alias.obj & r_brush.obj & gl_model.obj OBJS = strlcat.obj & strlcpy.obj & $(GLOBJS) & $(SYSOBJ_INPUT) & $(COMOBJ_SND) & $(SYSOBJ_SND) & $(SYSOBJ_CDA) & $(SYSOBJ_NET) & net_dgrm.obj & net_loop.obj & net_main.obj & chase.obj & cl_demo.obj & cl_input.obj & cl_main.obj & cl_parse.obj & cl_tent.obj & console.obj & keys.obj & menu.obj & sbar.obj & view.obj & wad.obj & cmd.obj & common.obj & crc.obj & cvar.obj & cfgfile.obj & host.obj & host_cmd.obj & mathlib.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_user.obj & world.obj & zone.obj & $(SYSOBJ_SYS) $(SYSOBJ_MAIN) # ------------------------ # Watcom build rules # ------------------------ # 1 MB stack size. quakespasm.exe: $(OBJS) quakespasm.res wlink N $@ SYS NT_WIN OPTION STACK=0x100000 LIBR {$(LIBS)} F {$(OBJS)} wrc -q $^*.res clean: .symbolic rm -f *.obj *.res *.err quakespasm.exe ����������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_udp.h�������������������������������������������������������������������0000644�0000000�0000000�00000003522�12407762022�015334� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __net_udp_h #define __net_udp_h sys_socket_t UDP_Init (void); void UDP_Shutdown (void); void UDP_Listen (qboolean state); sys_socket_t UDP_OpenSocket (int port); int UDP_CloseSocket (sys_socket_t socketid); int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t UDP_CheckNewConnections (void); int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *UDP_AddrToString (struct qsockaddr *addr); int UDP_StringToAddr (const char *string, struct qsockaddr *addr); int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name); int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr); int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int UDP_GetSocketPort (struct qsockaddr *addr); int UDP_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __net_udp_h */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/strlcat.c�������������������������������������������������������������������0000644�0000000�0000000�00000003256�11676323013�015351� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/types.h> #include <string.h> #include "strl_fn.h" /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t q_strlcat (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pr_edict.c������������������������������������������������������������������0000644�0000000�0000000�00000066053�13201237470�015467� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_edict.c -- entity dictionary #include "quakedef.h" dprograms_t *progs; dfunction_t *pr_functions; static char *pr_strings; static int pr_stringssize; static const char **pr_knownstrings; static int pr_maxknownstrings; static int pr_numknownstrings; static ddef_t *pr_fielddefs; static ddef_t *pr_globaldefs; qboolean pr_alpha_supported; //johnfitz dstatement_t *pr_statements; globalvars_t *pr_global_struct; float *pr_globals; // same as pr_global_struct int pr_edict_size; // in bytes unsigned short pr_crc; int type_size[8] = { 1, // ev_void 1, // sizeof(string_t) / 4 // ev_string 1, // ev_float 3, // ev_vector 1, // ev_entity 1, // ev_field 1, // sizeof(func_t) / 4 // ev_function 1 // sizeof(void *) / 4 // ev_pointer }; static ddef_t *ED_FieldAtOfs (int ofs); static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s); #define MAX_FIELD_LEN 64 #define GEFV_CACHESIZE 2 typedef struct { ddef_t *pcache; char field[MAX_FIELD_LEN]; } gefv_cache; static gefv_cache gefvCache[GEFV_CACHESIZE] = { { NULL, "" }, { NULL, "" } }; cvar_t nomonsters = {"nomonsters", "0", CVAR_NONE}; cvar_t gamecfg = {"gamecfg", "0", CVAR_NONE}; cvar_t scratch1 = {"scratch1", "0", CVAR_NONE}; cvar_t scratch2 = {"scratch2", "0", CVAR_NONE}; cvar_t scratch3 = {"scratch3", "0", CVAR_NONE}; cvar_t scratch4 = {"scratch4", "0", CVAR_NONE}; cvar_t savedgamecfg = {"savedgamecfg", "0", CVAR_ARCHIVE}; cvar_t saved1 = {"saved1", "0", CVAR_ARCHIVE}; cvar_t saved2 = {"saved2", "0", CVAR_ARCHIVE}; cvar_t saved3 = {"saved3", "0", CVAR_ARCHIVE}; cvar_t saved4 = {"saved4", "0", CVAR_ARCHIVE}; /* ================= ED_ClearEdict Sets everything to NULL ================= */ void ED_ClearEdict (edict_t *e) { memset (&e->v, 0, progs->entityfields * 4); e->free = false; } /* ================= ED_Alloc Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for (i = svs.maxclients + 1; i < sv.num_edicts; i++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) { ED_ClearEdict (e); return e; } } if (i == sv.max_edicts) //johnfitz -- use sv.max_edicts instead of MAX_EDICTS Host_Error ("ED_Alloc: no free edicts (max_edicts is %i)", sv.max_edicts); sv.num_edicts++; e = EDICT_NUM(i); memset(e, 0, pr_edict_size); // ericw -- switched sv.edicts to malloc(), so we are accessing uninitialized memory and must fully zero it, not just ED_ClearEdict return e; } /* ================= ED_Free Marks the edict as free FIXME: walk all entities and NULL out references to this entity ================= */ void ED_Free (edict_t *ed) { SV_UnlinkEdict (ed); // unlink from world bsp ed->free = true; ed->v.model = 0; ed->v.takedamage = 0; ed->v.modelindex = 0; ed->v.colormap = 0; ed->v.skin = 0; ed->v.frame = 0; VectorCopy (vec3_origin, ed->v.origin); VectorCopy (vec3_origin, ed->v.angles); ed->v.nextthink = -1; ed->v.solid = 0; ed->alpha = ENTALPHA_DEFAULT; //johnfitz -- reset alpha for next entity ed->freetime = sv.time; } //=========================================================================== /* ============ ED_GlobalAtOfs ============ */ static ddef_t *ED_GlobalAtOfs (int ofs) { ddef_t *def; int i; for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FieldAtOfs ============ */ static ddef_t *ED_FieldAtOfs (int ofs) { ddef_t *def; int i; for (i = 0; i < progs->numfielddefs; i++) { def = &pr_fielddefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FindField ============ */ static ddef_t *ED_FindField (const char *name) { ddef_t *def; int i; for (i = 0; i < progs->numfielddefs; i++) { def = &pr_fielddefs[i]; if ( !strcmp(PR_GetString(def->s_name), name) ) return def; } return NULL; } /* ============ ED_FindGlobal ============ */ static ddef_t *ED_FindGlobal (const char *name) { ddef_t *def; int i; for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; if ( !strcmp(PR_GetString(def->s_name), name) ) return def; } return NULL; } /* ============ ED_FindFunction ============ */ static dfunction_t *ED_FindFunction (const char *fn_name) { dfunction_t *func; int i; for (i = 0; i < progs->numfunctions; i++) { func = &pr_functions[i]; if ( !strcmp(PR_GetString(func->s_name), fn_name) ) return func; } return NULL; } /* ============ GetEdictFieldValue ============ */ eval_t *GetEdictFieldValue(edict_t *ed, const char *field) { ddef_t *def = NULL; int i; static int rep = 0; for (i = 0; i < GEFV_CACHESIZE; i++) { if (!strcmp(field, gefvCache[i].field)) { def = gefvCache[i].pcache; goto Done; } } def = ED_FindField (field); if (strlen(field) < MAX_FIELD_LEN) { gefvCache[rep].pcache = def; strcpy (gefvCache[rep].field, field); rep ^= 1; } Done: if (!def) return NULL; return (eval_t *)((char *)&ed->v + def->ofs*4); } /* ============ PR_ValueString (etype_t type, eval_t *val) Returns a string describing *data in a type specific manner ============= */ static const char *PR_ValueString (int type, eval_t *val) { static char line[512]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: sprintf (line, "%s", PR_GetString(val->string)); break; case ev_entity: sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); break; case ev_function: f = pr_functions + val->function; sprintf (line, "%s()", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); sprintf (line, ".%s", PR_GetString(def->s_name)); break; case ev_void: sprintf (line, "void"); break; case ev_float: sprintf (line, "%5.1f", val->_float); break; case ev_vector: sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); break; case ev_pointer: sprintf (line, "pointer"); break; default: sprintf (line, "bad type %i", type); break; } return line; } /* ============ PR_UglyValueString (etype_t type, eval_t *val) Returns a string describing *data in a type specific manner Easier to parse than PR_ValueString ============= */ static const char *PR_UglyValueString (int type, eval_t *val) { static char line[512]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: sprintf (line, "%s", PR_GetString(val->string)); break; case ev_entity: sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); break; case ev_function: f = pr_functions + val->function; sprintf (line, "%s", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); sprintf (line, "%s", PR_GetString(def->s_name)); break; case ev_void: sprintf (line, "void"); break; case ev_float: sprintf (line, "%f", val->_float); break; case ev_vector: sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); break; default: sprintf (line, "bad type %i", type); break; } return line; } /* ============ PR_GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ const char *PR_GlobalString (int ofs) { static char line[512]; const char *s; int i; ddef_t *def; void *val; val = (void *)&pr_globals[ofs]; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else { s = PR_ValueString (def->type, (eval_t *)val); sprintf (line,"%i(%s)%s", ofs, PR_GetString(def->s_name), s); } i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; } const char *PR_GlobalStringNoContents (int ofs) { static char line[512]; int i; ddef_t *def; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else sprintf (line,"%i(%s)", ofs, PR_GetString(def->s_name)); i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; } /* ============= ED_Print For debugging ============= */ void ED_Print (edict_t *ed) { ddef_t *d; int *v; int i, j, l; const char *name; int type; if (ed->free) { Con_Printf ("FREE\n"); return; } Con_SafePrintf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); //johnfitz -- was Con_Printf for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); l = strlen (name); if (l > 1 && name[l - 2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; Con_SafePrintf ("%s", name); //johnfitz -- was Con_Printf while (l++ < 15) Con_SafePrintf (" "); //johnfitz -- was Con_Printf Con_SafePrintf ("%s\n", PR_ValueString(d->type, (eval_t *)v)); //johnfitz -- was Con_Printf } } /* ============= ED_Write For savegames ============= */ void ED_Write (FILE *f, edict_t *ed) { ddef_t *d; int *v; int i, j; const char *name; int type; fprintf (f, "{\n"); if (ed->free) { fprintf (f, "}\n"); return; } for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); j = strlen (name); if (j > 1 && name[j - 2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; fprintf (f, "\"%s\" ", name); fprintf (f, "\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v)); } //johnfitz -- save entity alpha manually when progs.dat doesn't know about alpha if (!pr_alpha_supported && ed->alpha != ENTALPHA_DEFAULT) fprintf (f, "\"alpha\" \"%f\"\n", ENTALPHA_TOSAVE(ed->alpha)); //johnfitz fprintf (f, "}\n"); } void ED_PrintNum (int ent) { ED_Print (EDICT_NUM(ent)); } /* ============= ED_PrintEdicts For debugging, prints all the entities in the current server ============= */ void ED_PrintEdicts (void) { int i; if (!sv.active) return; Con_Printf ("%i entities\n", sv.num_edicts); for (i = 0; i < sv.num_edicts; i++) ED_PrintNum (i); } /* ============= ED_PrintEdict_f For debugging, prints a single edicy ============= */ static void ED_PrintEdict_f (void) { int i; if (!sv.active) return; i = Q_atoi (Cmd_Argv(1)); if (i < 0 || i >= sv.num_edicts) { Con_Printf("Bad edict number\n"); return; } ED_PrintNum (i); } /* ============= ED_Count For debugging ============= */ static void ED_Count (void) { edict_t *ent; int i, active, models, solid, step; if (!sv.active) return; active = models = solid = step = 0; for (i = 0; i < sv.num_edicts; i++) { ent = EDICT_NUM(i); if (ent->free) continue; active++; if (ent->v.solid) solid++; if (ent->v.model) models++; if (ent->v.movetype == MOVETYPE_STEP) step++; } Con_Printf ("num_edicts:%3i\n", sv.num_edicts); Con_Printf ("active :%3i\n", active); Con_Printf ("view :%3i\n", models); Con_Printf ("touch :%3i\n", solid); Con_Printf ("step :%3i\n", step); } /* ============================================================================== ARCHIVING GLOBALS FIXME: need to tag constants, doesn't really work ============================================================================== */ /* ============= ED_WriteGlobals ============= */ void ED_WriteGlobals (FILE *f) { ddef_t *def; int i; const char *name; int type; fprintf (f, "{\n"); for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; type = def->type; if ( !(def->type & DEF_SAVEGLOBAL) ) continue; type &= ~DEF_SAVEGLOBAL; if (type != ev_string && type != ev_float && type != ev_entity) continue; name = PR_GetString(def->s_name); fprintf (f, "\"%s\" ", name); fprintf (f, "\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); } fprintf (f, "}\n"); } /* ============= ED_ParseGlobals ============= */ const char *ED_ParseGlobals (const char *data) { char keyname[64]; ddef_t *key; while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Host_Error ("ED_ParseEntity: EOF without closing brace"); q_strlcpy (keyname, com_token, sizeof(keyname)); // parse value data = COM_Parse (data); if (!data) Host_Error ("ED_ParseEntity: EOF without closing brace"); if (com_token[0] == '}') Host_Error ("ED_ParseEntity: closing brace without data"); key = ED_FindGlobal (keyname); if (!key) { Con_Printf ("'%s' is not a global\n", keyname); continue; } if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) Host_Error ("ED_ParseGlobals: parse error"); } return data; } //============================================================================ /* ============= ED_NewString ============= */ static string_t ED_NewString (const char *string) { char *new_p; int i, l; string_t num; l = strlen(string) + 1; num = PR_AllocString (l, &new_p); for (i = 0; i < l; i++) { if (string[i] == '\\' && i < l-1) { i++; if (string[i] == 'n') *new_p++ = '\n'; else *new_p++ = '\\'; } else *new_p++ = string[i]; } return num; } /* ============= ED_ParseEval Can parse either fields or globals returns false if error ============= */ static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s) { int i; char string[128]; ddef_t *def; char *v, *w; char *end; void *d; dfunction_t *func; d = (void *)((int *)base + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: *(string_t *)d = ED_NewString(s); break; case ev_float: *(float *)d = atof (s); break; case ev_vector: q_strlcpy(string, s, sizeof(string)); end = (char *)string + strlen(string); v = string; w = string; for (i = 0; i < 3 && (w <= end); i++) // ericw -- added (w <= end) check { // set `v` to the next space (or 0 byte), and change that char to a 0 byte while (*v && *v != ' ') v++; *v = 0; ((float *)d)[i] = atof (w); w = v = v+1; } // ericw -- fill remaining elements to 0.0f in case we hit the end of string before reading 3 floats if (i < 3) { Con_DWarning("vanilla will read garbage for \"%s\" \"%s\"\n", PR_GetString(key->s_name), s); for (; i < 3; i++) ((float *)d)[i] = 0.0f; } break; case ev_entity: *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); break; case ev_field: def = ED_FindField (s); if (!def) { //johnfitz -- HACK -- suppress error becuase fog/sky fields might not be mentioned in defs.qc if (strncmp(s, "sky", 3) && strcmp(s, "fog")) Con_DPrintf ("Can't find field %s\n", s); return false; } *(int *)d = G_INT(def->ofs); break; case ev_function: func = ED_FindFunction (s); if (!func) { Con_Printf ("Can't find function %s\n", s); return false; } *(func_t *)d = func - pr_functions; break; default: break; } return true; } /* ==================== ED_ParseEdict Parses an edict out of the given string, returning the new position ed should be a properly initialized empty edict. Used for initial level load and for savegames. ==================== */ const char *ED_ParseEdict (const char *data, edict_t *ent) { ddef_t *key; char keyname[256]; qboolean anglehack, init; int n; init = false; // clear it if (ent != sv.edicts) // hack memset (&ent->v, 0, progs->entityfields * 4); // go through all the dictionary pairs while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Host_Error ("ED_ParseEntity: EOF without closing brace"); // anglehack is to allow QuakeEd to write single scalar angles // and allow them to be turned into vectors. (FIXME...) if (!strcmp(com_token, "angle")) { strcpy (com_token, "angles"); anglehack = true; } else anglehack = false; // FIXME: change light to _light to get rid of this hack if (!strcmp(com_token, "light")) strcpy (com_token, "light_lev"); // hack for single light def strcpy (keyname, com_token); // another hack to fix keynames with trailing spaces n = strlen(keyname); while (n && keyname[n-1] == ' ') { keyname[n-1] = 0; n--; } // parse value data = COM_Parse (data); if (!data) Host_Error ("ED_ParseEntity: EOF without closing brace"); if (com_token[0] == '}') Host_Error ("ED_ParseEntity: closing brace without data"); init = true; // keynames with a leading underscore are used for utility comments, // and are immediately discarded by quake if (keyname[0] == '_') continue; //johnfitz -- hack to support .alpha even when progs.dat doesn't know about it if (!strcmp(keyname, "alpha")) ent->alpha = ENTALPHA_ENCODE(atof(com_token)); //johnfitz key = ED_FindField (keyname); if (!key) { //johnfitz -- HACK -- suppress error becuase fog/sky/alpha fields might not be mentioned in defs.qc if (strncmp(keyname, "sky", 3) && strcmp(keyname, "fog") && strcmp(keyname, "alpha")) Con_DPrintf ("\"%s\" is not a field\n", keyname); //johnfitz -- was Con_Printf continue; } if (anglehack) { char temp[32]; strcpy (temp, com_token); sprintf (com_token, "0 %s 0", temp); } if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) Host_Error ("ED_ParseEdict: parse error"); } if (!init) ent->free = true; return data; } /* ================ ED_LoadFromFile The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. ================ */ void ED_LoadFromFile (const char *data) { dfunction_t *func; edict_t *ent = NULL; int inhibit = 0; pr_global_struct->time = sv.time; // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; if (com_token[0] != '{') Host_Error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = EDICT_NUM(0); else ent = ED_Alloc (); data = ED_ParseEdict (data, ent); // remove things from different skill levels or deathmatch if (deathmatch.value) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; continue; } // // immediately call spawn function // if (!ent->v.classname) { Con_SafePrintf ("No classname for:\n"); //johnfitz -- was Con_Printf ED_Print (ent); ED_Free (ent); continue; } // look for the spawn function func = ED_FindFunction ( PR_GetString(ent->v.classname) ); if (!func) { Con_SafePrintf ("No spawn function for:\n"); //johnfitz -- was Con_Printf ED_Print (ent); ED_Free (ent); continue; } pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (func - pr_functions); } Con_DPrintf ("%i entities inhibited\n", inhibit); } /* =============== PR_LoadProgs =============== */ void PR_LoadProgs (void) { int i; // flush the non-C variable lookup cache for (i = 0; i < GEFV_CACHESIZE; i++) gefvCache[i].field[0] = 0; CRC_Init (&pr_crc); progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat", NULL); if (!progs) Host_Error ("PR_LoadProgs: couldn't load progs.dat"); Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024); for (i = 0; i < com_filesize; i++) CRC_ProcessByte (&pr_crc, ((byte *)progs)[i]); // byte swap the header for (i = 0; i < (int) sizeof(*progs) / 4; i++) ((int *)progs)[i] = LittleLong ( ((int *)progs)[i] ); if (progs->version != PROG_VERSION) Host_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); if (progs->crc != PROGHEADER_CRC) Host_Error ("progs.dat system vars have been modified, progdefs.h is out of date"); pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); pr_strings = (char *)progs + progs->ofs_strings; if (progs->ofs_strings + progs->numstrings >= com_filesize) Host_Error ("progs.dat strings go past end of file\n"); // initialize the strings pr_numknownstrings = 0; pr_maxknownstrings = 0; pr_stringssize = progs->numstrings; if (pr_knownstrings) Z_Free ((void *)pr_knownstrings); pr_knownstrings = NULL; PR_SetEngineString(""); pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); pr_globals = (float *)pr_global_struct; // byte swap the lumps for (i = 0; i < progs->numstatements; i++) { pr_statements[i].op = LittleShort(pr_statements[i].op); pr_statements[i].a = LittleShort(pr_statements[i].a); pr_statements[i].b = LittleShort(pr_statements[i].b); pr_statements[i].c = LittleShort(pr_statements[i].c); } for (i = 0; i < progs->numfunctions; i++) { pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); pr_functions[i].locals = LittleLong (pr_functions[i].locals); } for (i = 0; i < progs->numglobaldefs; i++) { pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); } pr_alpha_supported = false; //johnfitz for (i = 0; i < progs->numfielddefs; i++) { pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) Host_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); //johnfitz -- detect alpha support in progs.dat if (!strcmp(pr_strings + pr_fielddefs[i].s_name,"alpha")) pr_alpha_supported = true; //johnfitz } for (i = 0; i < progs->numglobals; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); pr_edict_size = progs->entityfields * 4 + sizeof(edict_t) - sizeof(entvars_t); // round off to next highest whole word address (esp for Alpha) // this ensures that pointers in the engine data area are always // properly aligned pr_edict_size += sizeof(void *) - 1; pr_edict_size &= ~(sizeof(void *) - 1); } /* =============== PR_Init =============== */ void PR_Init (void) { Cmd_AddCommand ("edict", ED_PrintEdict_f); Cmd_AddCommand ("edicts", ED_PrintEdicts); Cmd_AddCommand ("edictcount", ED_Count); Cmd_AddCommand ("profile", PR_Profile_f); Cvar_RegisterVariable (&nomonsters); Cvar_RegisterVariable (&gamecfg); Cvar_RegisterVariable (&scratch1); Cvar_RegisterVariable (&scratch2); Cvar_RegisterVariable (&scratch3); Cvar_RegisterVariable (&scratch4); Cvar_RegisterVariable (&savedgamecfg); Cvar_RegisterVariable (&saved1); Cvar_RegisterVariable (&saved2); Cvar_RegisterVariable (&saved3); Cvar_RegisterVariable (&saved4); } edict_t *EDICT_NUM(int n) { if (n < 0 || n >= sv.max_edicts) Host_Error ("EDICT_NUM: bad number %i", n); return (edict_t *)((byte *)sv.edicts + (n)*pr_edict_size); } int NUM_FOR_EDICT(edict_t *e) { int b; b = (byte *)e - (byte *)sv.edicts; b = b / pr_edict_size; if (b < 0 || b >= sv.num_edicts) Host_Error ("NUM_FOR_EDICT: bad pointer"); return b; } //=========================================================================== #define PR_STRING_ALLOCSLOTS 256 static void PR_AllocStringSlots (void) { pr_maxknownstrings += PR_STRING_ALLOCSLOTS; Con_DPrintf2("PR_AllocStringSlots: realloc'ing for %d slots\n", pr_maxknownstrings); pr_knownstrings = (const char **) Z_Realloc ((void *)pr_knownstrings, pr_maxknownstrings * sizeof(char *)); } const char *PR_GetString (int num) { if (num >= 0 && num < pr_stringssize) return pr_strings + num; else if (num < 0 && num >= -pr_numknownstrings) { if (!pr_knownstrings[-1 - num]) { Host_Error ("PR_GetString: attempt to get a non-existant string %d\n", num); return ""; } return pr_knownstrings[-1 - num]; } else { Host_Error("PR_GetString: invalid string offset %d\n", num); return ""; } } int PR_SetEngineString (const char *s) { int i; if (!s) return 0; #if 0 /* can't: sv.model_precache & sv.sound_precache points to pr_strings */ if (s >= pr_strings && s <= pr_strings + pr_stringssize) Host_Error("PR_SetEngineString: \"%s\" in pr_strings area\n", s); #else if (s >= pr_strings && s <= pr_strings + pr_stringssize - 2) return (int)(s - pr_strings); #endif for (i = 0; i < pr_numknownstrings; i++) { if (pr_knownstrings[i] == s) return -1 - i; } // new unknown engine string //Con_DPrintf ("PR_SetEngineString: new engine string %p\n", s); #if 0 for (i = 0; i < pr_numknownstrings; i++) { if (!pr_knownstrings[i]) break; } #endif // if (i >= pr_numknownstrings) // { if (i >= pr_maxknownstrings) PR_AllocStringSlots(); pr_numknownstrings++; // } pr_knownstrings[i] = s; return -1 - i; } int PR_AllocString (int size, char **ptr) { int i; if (!size) return 0; for (i = 0; i < pr_numknownstrings; i++) { if (!pr_knownstrings[i]) break; } // if (i >= pr_numknownstrings) // { if (i >= pr_maxknownstrings) PR_AllocStringSlots(); pr_numknownstrings++; // } pr_knownstrings[i] = (char *)Hunk_AllocName(size, "string"); if (ptr) *ptr = (char *) pr_knownstrings[i]; return -1 - i; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/in_sdl.c��������������������������������������������������������������������0000644�0000000�0000000�00000076464�13147214764�015167� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif static qboolean textmode; static cvar_t in_debugkeys = {"in_debugkeys", "0", CVAR_NONE}; #ifdef __APPLE__ /* Mouse acceleration needs to be disabled on OS X */ #define MACOS_X_ACCELERATION_HACK #endif #ifdef MACOS_X_ACCELERATION_HACK #include <IOKit/IOTypes.h> #include <IOKit/hidsystem/IOHIDLib.h> #include <IOKit/hidsystem/IOHIDParameter.h> #include <IOKit/hidsystem/event_status_driver.h> #endif // SDL2 Game Controller cvars cvar_t joy_deadzone = { "joy_deadzone", "0.175", CVAR_ARCHIVE }; cvar_t joy_deadzone_trigger = { "joy_deadzone_trigger", "0.2", CVAR_ARCHIVE }; cvar_t joy_sensitivity_yaw = { "joy_sensitivity_yaw", "300", CVAR_ARCHIVE }; cvar_t joy_sensitivity_pitch = { "joy_sensitivity_pitch", "150", CVAR_ARCHIVE }; cvar_t joy_invert = { "joy_invert", "0", CVAR_ARCHIVE }; cvar_t joy_exponent = { "joy_exponent", "3", CVAR_ARCHIVE }; cvar_t joy_swapmovelook = { "joy_swapmovelook", "0", CVAR_ARCHIVE }; cvar_t joy_enable = { "joy_enable", "1", CVAR_ARCHIVE }; #if defined(USE_SDL2) static SDL_JoystickID joy_active_instaceid = -1; static SDL_GameController *joy_active_controller = NULL; #endif static qboolean no_mouse = false; static int buttonremap[] = { K_MOUSE1, K_MOUSE3, /* right button */ K_MOUSE2, /* middle button */ #if !defined(USE_SDL2) /* mousewheel up/down not counted as buttons in SDL2 */ K_MWHEELUP, K_MWHEELDOWN, #endif K_MOUSE4, K_MOUSE5 }; /* total accumulated mouse movement since last frame */ static int total_dx, total_dy = 0; static int SDLCALL IN_FilterMouseEvents (const SDL_Event *event) { switch (event->type) { case SDL_MOUSEMOTION: // case SDL_MOUSEBUTTONDOWN: // case SDL_MOUSEBUTTONUP: return 0; } return 1; } #if defined(USE_SDL2) static int SDLCALL IN_SDL2_FilterMouseEvents (void *userdata, SDL_Event *event) { return IN_FilterMouseEvents (event); } #endif static void IN_BeginIgnoringMouseEvents(void) { #if defined(USE_SDL2) SDL_EventFilter currentFilter = NULL; void *currentUserdata = NULL; SDL_GetEventFilter(¤tFilter, ¤tUserdata); if (currentFilter != IN_SDL2_FilterMouseEvents) SDL_SetEventFilter(IN_SDL2_FilterMouseEvents, NULL); #else if (SDL_GetEventFilter() != IN_FilterMouseEvents) SDL_SetEventFilter(IN_FilterMouseEvents); #endif } static void IN_EndIgnoringMouseEvents(void) { #if defined(USE_SDL2) SDL_EventFilter currentFilter; void *currentUserdata; if (SDL_GetEventFilter(¤tFilter, ¤tUserdata) == SDL_TRUE) SDL_SetEventFilter(NULL, NULL); #else if (SDL_GetEventFilter() != NULL) SDL_SetEventFilter(NULL); #endif } #ifdef MACOS_X_ACCELERATION_HACK static cvar_t in_disablemacosxmouseaccel = {"in_disablemacosxmouseaccel", "1", CVAR_ARCHIVE}; static double originalMouseSpeed = -1.0; static io_connect_t IN_GetIOHandle(void) { io_connect_t iohandle = MACH_PORT_NULL; io_service_t iohidsystem = MACH_PORT_NULL; mach_port_t masterport; kern_return_t status; status = IOMasterPort(MACH_PORT_NULL, &masterport); if (status != KERN_SUCCESS) return 0; iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem"); if (!iohidsystem) return 0; status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle); IOObjectRelease(iohidsystem); return iohandle; } static void IN_DisableOSXMouseAccel (void) { io_connect_t mouseDev = IN_GetIOHandle(); if (mouseDev != 0) { if (IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess) { if (IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess) { Cvar_Set("in_disablemacosxmouseaccel", "0"); Con_Printf("WARNING: Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n"); } } else { Cvar_Set("in_disablemacosxmouseaccel", "0"); Con_Printf("WARNING: Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n"); } IOServiceClose(mouseDev); } else { Cvar_Set("in_disablemacosxmouseaccel", "0"); Con_Printf("WARNING: Could not disable mouse acceleration (failed at IO_GetIOHandle).\n"); } } static void IN_ReenableOSXMouseAccel (void) { io_connect_t mouseDev = IN_GetIOHandle(); if (mouseDev != 0) { if (IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), originalMouseSpeed) != kIOReturnSuccess) Con_Printf("WARNING: Could not re-enable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n"); IOServiceClose(mouseDev); } else { Con_Printf("WARNING: Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n"); } originalMouseSpeed = -1; } #endif /* MACOS_X_ACCELERATION_HACK */ void IN_Activate (void) { if (no_mouse) return; #ifdef MACOS_X_ACCELERATION_HACK /* Save the status of mouse acceleration */ if (originalMouseSpeed == -1 && in_disablemacosxmouseaccel.value) IN_DisableOSXMouseAccel(); #endif #if defined(USE_SDL2) if (SDL_SetRelativeMouseMode(SDL_TRUE) != 0) { Con_Printf("WARNING: SDL_SetRelativeMouseMode(SDL_TRUE) failed.\n"); } #else if (SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON) { SDL_WM_GrabInput(SDL_GRAB_ON); if (SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON) Con_Printf("WARNING: SDL_WM_GrabInput(SDL_GRAB_ON) failed.\n"); } if (SDL_ShowCursor(SDL_QUERY) != SDL_DISABLE) { SDL_ShowCursor(SDL_DISABLE); if (SDL_ShowCursor(SDL_QUERY) != SDL_DISABLE) Con_Printf("WARNING: SDL_ShowCursor(SDL_DISABLE) failed.\n"); } #endif IN_EndIgnoringMouseEvents(); total_dx = 0; total_dy = 0; } void IN_Deactivate (qboolean free_cursor) { if (no_mouse) return; #ifdef MACOS_X_ACCELERATION_HACK if (originalMouseSpeed != -1) IN_ReenableOSXMouseAccel(); #endif if (free_cursor) { #if defined(USE_SDL2) SDL_SetRelativeMouseMode(SDL_FALSE); #else if (SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_OFF) { SDL_WM_GrabInput(SDL_GRAB_OFF); if (SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_OFF) Con_Printf("WARNING: SDL_WM_GrabInput(SDL_GRAB_OFF) failed.\n"); } if (SDL_ShowCursor(SDL_QUERY) != SDL_ENABLE) { SDL_ShowCursor(SDL_ENABLE); if (SDL_ShowCursor(SDL_QUERY) != SDL_ENABLE) Con_Printf("WARNING: SDL_ShowCursor(SDL_ENABLE) failed.\n"); } #endif } /* discard all mouse events when input is deactivated */ IN_BeginIgnoringMouseEvents(); } void IN_StartupJoystick (void) { #if defined(USE_SDL2) int i; int nummappings; char controllerdb[MAX_OSPATH]; SDL_GameController *gamecontroller; if (COM_CheckParm("-nojoy")) return; if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1 ) { Con_Warning("could not initialize SDL Game Controller\n"); return; } // Load additional SDL2 controller definitions from gamecontrollerdb.txt q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", com_basedir); nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb); if (nummappings > 0) Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); // Also try host_parms->userdir if (host_parms->userdir != host_parms->basedir) { q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", host_parms->userdir); nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb); if (nummappings > 0) Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); } for (i = 0; i < SDL_NumJoysticks(); i++) { const char *joyname = SDL_JoystickNameForIndex(i); if ( SDL_IsGameController(i) ) { const char *controllername = SDL_GameControllerNameForIndex(i); gamecontroller = SDL_GameControllerOpen(i); if (gamecontroller) { Con_Printf("detected controller: %s\n", controllername != NULL ? controllername : "NULL"); joy_active_instaceid = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller)); joy_active_controller = gamecontroller; break; } else { Con_Warning("failed to open controller: %s\n", controllername != NULL ? controllername : "NULL"); } } else { Con_Warning("joystick missing controller mappings: %s\n", joyname != NULL ? joyname : "NULL" ); } } #endif } void IN_ShutdownJoystick (void) { #if defined(USE_SDL2) SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); #endif } void IN_Init (void) { textmode = Key_TextEntry(); #if !defined(USE_SDL2) SDL_EnableUNICODE (textmode); if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) == -1) Con_Printf("Warning: SDL_EnableKeyRepeat() failed.\n"); #else if (textmode) SDL_StartTextInput(); else SDL_StopTextInput(); #endif if (safemode || COM_CheckParm("-nomouse")) { no_mouse = true; /* discard all mouse events when input is deactivated */ IN_BeginIgnoringMouseEvents(); } #ifdef MACOS_X_ACCELERATION_HACK Cvar_RegisterVariable(&in_disablemacosxmouseaccel); #endif Cvar_RegisterVariable(&in_debugkeys); Cvar_RegisterVariable(&joy_sensitivity_yaw); Cvar_RegisterVariable(&joy_sensitivity_pitch); Cvar_RegisterVariable(&joy_deadzone); Cvar_RegisterVariable(&joy_deadzone_trigger); Cvar_RegisterVariable(&joy_invert); Cvar_RegisterVariable(&joy_exponent); Cvar_RegisterVariable(&joy_swapmovelook); Cvar_RegisterVariable(&joy_enable); IN_Activate(); IN_StartupJoystick(); } void IN_Shutdown (void) { IN_Deactivate(true); IN_ShutdownJoystick(); } extern cvar_t cl_maxpitch; /* johnfitz -- variable pitch clamping */ extern cvar_t cl_minpitch; /* johnfitz -- variable pitch clamping */ void IN_MouseMotion(int dx, int dy) { total_dx += dx; total_dy += dy; } #if defined(USE_SDL2) typedef struct joyaxis_s { float x; float y; } joyaxis_t; typedef struct joy_buttonstate_s { qboolean buttondown[SDL_CONTROLLER_BUTTON_MAX]; } joybuttonstate_t; typedef struct axisstate_s { float axisvalue[SDL_CONTROLLER_AXIS_MAX]; // normalized to +-1 } joyaxisstate_t; static joybuttonstate_t joy_buttonstate; static joyaxisstate_t joy_axisstate; static double joy_buttontimer[SDL_CONTROLLER_BUTTON_MAX]; static double joy_emulatedkeytimer[10]; #ifdef __WATCOMC__ /* OW1.9 doesn't have powf() / sqrtf() */ #define powf pow #define sqrtf sqrt #endif /* ================ IN_AxisMagnitude Returns the vector length of the given joystick axis ================ */ static vec_t IN_AxisMagnitude(joyaxis_t axis) { vec_t magnitude = sqrtf((axis.x * axis.x) + (axis.y * axis.y)); return magnitude; } /* ================ IN_ApplyLookEasing assumes axis values are in [-1, 1] and the vector magnitude has been clamped at 1. Raises the axis values to the given exponent, keeping signs. ================ */ static joyaxis_t IN_ApplyLookEasing(joyaxis_t axis, float exponent) { joyaxis_t result = {0}; vec_t eased_magnitude; vec_t magnitude = IN_AxisMagnitude(axis); if (magnitude == 0) return result; eased_magnitude = powf(magnitude, exponent); result.x = axis.x * (eased_magnitude / magnitude); result.y = axis.y * (eased_magnitude / magnitude); return result; } /* ================ IN_ApplyMoveEasing clamps coordinates to a square with coordinates +/- sqrt(2)/2, then scales them to +/- 1. This wastes a bit of stick range, but gives the diagonals coordinates of (+/-1,+/-1), so holding the stick on a diagonal gives the same speed boost as holding the forward and strafe keyboard keys. ================ */ static joyaxis_t IN_ApplyMoveEasing(joyaxis_t axis) { joyaxis_t result = {0}; const float v = sqrtf(2.0f) / 2.0f; result.x = q_max(-v, q_min(v, axis.x)); result.y = q_max(-v, q_min(v, axis.y)); result.x /= v; result.y /= v; return result; } /* ================ IN_ApplyDeadzone in: raw joystick axis values converted to floats in +-1 out: applies a circular deadzone and clamps the magnitude at 1 (my 360 controller is slightly non-circular and the stick travels further on the diagonals) deadzone is expected to satisfy 0 < deadzone < 1 from https://github.com/jeremiah-sypult/Quakespasm-Rift and adapted from http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html ================ */ static joyaxis_t IN_ApplyDeadzone(joyaxis_t axis, float deadzone) { joyaxis_t result = {0}; vec_t magnitude = IN_AxisMagnitude(axis); if ( magnitude > deadzone ) { const vec_t new_magnitude = q_min(1.0, (magnitude - deadzone) / (1.0 - deadzone)); const vec_t scale = new_magnitude / magnitude; result.x = axis.x * scale; result.y = axis.y * scale; } return result; } /* ================ IN_KeyForControllerButton ================ */ static int IN_KeyForControllerButton(SDL_GameControllerButton button) { switch (button) { case SDL_CONTROLLER_BUTTON_A: return K_ABUTTON; case SDL_CONTROLLER_BUTTON_B: return K_BBUTTON; case SDL_CONTROLLER_BUTTON_X: return K_XBUTTON; case SDL_CONTROLLER_BUTTON_Y: return K_YBUTTON; case SDL_CONTROLLER_BUTTON_BACK: return K_TAB; case SDL_CONTROLLER_BUTTON_START: return K_ESCAPE; case SDL_CONTROLLER_BUTTON_LEFTSTICK: return K_LTHUMB; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return K_RTHUMB; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return K_LSHOULDER; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return K_RSHOULDER; case SDL_CONTROLLER_BUTTON_DPAD_UP: return K_UPARROW; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return K_DOWNARROW; case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return K_LEFTARROW; case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return K_RIGHTARROW; default: return 0; } } /* ================ IN_JoyKeyEvent Sends a Key_Event if a unpressed -> pressed or pressed -> unpressed transition occurred, and generates key repeats if the button is held down. Adapted from DarkPlaces by lordhavoc ================ */ static void IN_JoyKeyEvent(qboolean wasdown, qboolean isdown, int key, double *timer) { // we can't use `realtime` for key repeats because it is not monotomic const double currenttime = Sys_DoubleTime(); if (wasdown) { if (isdown) { if (currenttime >= *timer) { *timer = currenttime + 0.1; Key_Event(key, true); } } else { *timer = 0; Key_Event(key, false); } } else { if (isdown) { *timer = currenttime + 0.5; Key_Event(key, true); } } } #endif /* ================ IN_Commands Emit key events for game controller buttons, including emulated buttons for analog sticks/triggers ================ */ void IN_Commands (void) { #if defined(USE_SDL2) joyaxisstate_t newaxisstate; int i; const float stickthreshold = 0.9; const float triggerthreshold = joy_deadzone_trigger.value; if (!joy_enable.value) return; if (!joy_active_controller) return; // emit key events for controller buttons for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) { qboolean newstate = SDL_GameControllerGetButton(joy_active_controller, (SDL_GameControllerButton)i); qboolean oldstate = joy_buttonstate.buttondown[i]; joy_buttonstate.buttondown[i] = newstate; // NOTE: This can cause a reentrant call of IN_Commands, via SCR_ModalMessage when confirming a new game. IN_JoyKeyEvent(oldstate, newstate, IN_KeyForControllerButton((SDL_GameControllerButton)i), &joy_buttontimer[i]); } for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++) { newaxisstate.axisvalue[i] = SDL_GameControllerGetAxis(joy_active_controller, (SDL_GameControllerAxis)i) / 32768.0f; } // emit emulated arrow keys so the analog sticks can be used in the menu if (key_dest != key_game) { IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[0]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[1]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[2]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[3]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[4]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[5]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[6]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[7]); } // emit emulated keys for the analog triggers IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, K_LTRIGGER, &joy_emulatedkeytimer[8]); IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, K_RTRIGGER, &joy_emulatedkeytimer[9]); joy_axisstate = newaxisstate; #endif } /* ================ IN_JoyMove ================ */ void IN_JoyMove (usercmd_t *cmd) { #if defined(USE_SDL2) float speed; joyaxis_t moveRaw, moveDeadzone, moveEased; joyaxis_t lookRaw, lookDeadzone, lookEased; if (!joy_enable.value) return; if (!joy_active_controller) return; moveRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX]; moveRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY]; lookRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX]; lookRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY]; if (joy_swapmovelook.value) { joyaxis_t temp = moveRaw; moveRaw = lookRaw; lookRaw = temp; } moveDeadzone = IN_ApplyDeadzone(moveRaw, joy_deadzone.value); lookDeadzone = IN_ApplyDeadzone(lookRaw, joy_deadzone.value); moveEased = IN_ApplyMoveEasing(moveDeadzone); lookEased = IN_ApplyLookEasing(lookDeadzone, joy_exponent.value); if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0)) speed = cl_movespeedkey.value; else speed = 1; cmd->sidemove += (cl_sidespeed.value * speed * moveEased.x); cmd->forwardmove -= (cl_forwardspeed.value * speed * moveEased.y); cl.viewangles[YAW] -= lookEased.x * joy_sensitivity_yaw.value * host_frametime; cl.viewangles[PITCH] += lookEased.y * joy_sensitivity_pitch.value * (joy_invert.value ? -1.0 : 1.0) * host_frametime; if (lookEased.x != 0 || lookEased.y != 0) V_StopPitchDrift(); /* johnfitz -- variable pitch clamping */ if (cl.viewangles[PITCH] > cl_maxpitch.value) cl.viewangles[PITCH] = cl_maxpitch.value; if (cl.viewangles[PITCH] < cl_minpitch.value) cl.viewangles[PITCH] = cl_minpitch.value; #endif } void IN_MouseMove(usercmd_t *cmd) { int dmx, dmy; dmx = total_dx * sensitivity.value; dmy = total_dy * sensitivity.value; total_dx = 0; total_dy = 0; if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) cmd->sidemove += m_side.value * dmx; else cl.viewangles[YAW] -= m_yaw.value * dmx; if (in_mlook.state & 1) { if (dmx || dmy) V_StopPitchDrift (); } if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * dmy; /* johnfitz -- variable pitch clamping */ if (cl.viewangles[PITCH] > cl_maxpitch.value) cl.viewangles[PITCH] = cl_maxpitch.value; if (cl.viewangles[PITCH] < cl_minpitch.value) cl.viewangles[PITCH] = cl_minpitch.value; } else { if ((in_strafe.state & 1) && noclip_anglehack) cmd->upmove -= m_forward.value * dmy; else cmd->forwardmove -= m_forward.value * dmy; } } void IN_Move(usercmd_t *cmd) { IN_JoyMove(cmd); IN_MouseMove(cmd); } void IN_ClearStates (void) { } void IN_UpdateInputMode (void) { qboolean want_textmode = Key_TextEntry(); if (textmode != want_textmode) { textmode = want_textmode; #if !defined(USE_SDL2) SDL_EnableUNICODE(textmode); if (in_debugkeys.value) Con_Printf("SDL_EnableUNICODE %d time: %g\n", textmode, Sys_DoubleTime()); #else if (textmode) { SDL_StartTextInput(); if (in_debugkeys.value) Con_Printf("SDL_StartTextInput time: %g\n", Sys_DoubleTime()); } else { SDL_StopTextInput(); if (in_debugkeys.value) Con_Printf("SDL_StopTextInput time: %g\n", Sys_DoubleTime()); } #endif } } #if !defined(USE_SDL2) static inline int IN_SDL_KeysymToQuakeKey(SDLKey sym) { if (sym > SDLK_SPACE && sym < SDLK_DELETE) return sym; switch (sym) { case SDLK_TAB: return K_TAB; case SDLK_RETURN: return K_ENTER; case SDLK_ESCAPE: return K_ESCAPE; case SDLK_SPACE: return K_SPACE; case SDLK_BACKSPACE: return K_BACKSPACE; case SDLK_UP: return K_UPARROW; case SDLK_DOWN: return K_DOWNARROW; case SDLK_LEFT: return K_LEFTARROW; case SDLK_RIGHT: return K_RIGHTARROW; case SDLK_LALT: return K_ALT; case SDLK_RALT: return K_ALT; case SDLK_LCTRL: return K_CTRL; case SDLK_RCTRL: return K_CTRL; case SDLK_LSHIFT: return K_SHIFT; case SDLK_RSHIFT: return K_SHIFT; case SDLK_F1: return K_F1; case SDLK_F2: return K_F2; case SDLK_F3: return K_F3; case SDLK_F4: return K_F4; case SDLK_F5: return K_F5; case SDLK_F6: return K_F6; case SDLK_F7: return K_F7; case SDLK_F8: return K_F8; case SDLK_F9: return K_F9; case SDLK_F10: return K_F10; case SDLK_F11: return K_F11; case SDLK_F12: return K_F12; case SDLK_INSERT: return K_INS; case SDLK_DELETE: return K_DEL; case SDLK_PAGEDOWN: return K_PGDN; case SDLK_PAGEUP: return K_PGUP; case SDLK_HOME: return K_HOME; case SDLK_END: return K_END; case SDLK_NUMLOCK: return K_KP_NUMLOCK; case SDLK_KP_DIVIDE: return K_KP_SLASH; case SDLK_KP_MULTIPLY: return K_KP_STAR; case SDLK_KP_MINUS:return K_KP_MINUS; case SDLK_KP7: return K_KP_HOME; case SDLK_KP8: return K_KP_UPARROW; case SDLK_KP9: return K_KP_PGUP; case SDLK_KP_PLUS: return K_KP_PLUS; case SDLK_KP4: return K_KP_LEFTARROW; case SDLK_KP5: return K_KP_5; case SDLK_KP6: return K_KP_RIGHTARROW; case SDLK_KP1: return K_KP_END; case SDLK_KP2: return K_KP_DOWNARROW; case SDLK_KP3: return K_KP_PGDN; case SDLK_KP_ENTER: return K_KP_ENTER; case SDLK_KP0: return K_KP_INS; case SDLK_KP_PERIOD: return K_KP_DEL; case SDLK_LMETA: return K_COMMAND; case SDLK_RMETA: return K_COMMAND; case SDLK_BREAK: return K_PAUSE; case SDLK_PAUSE: return K_PAUSE; case SDLK_WORLD_18: return '~'; // the '²' key default: return 0; } } #endif #if defined(USE_SDL2) static inline int IN_SDL2_ScancodeToQuakeKey(SDL_Scancode scancode) { switch (scancode) { case SDL_SCANCODE_TAB: return K_TAB; case SDL_SCANCODE_RETURN: return K_ENTER; case SDL_SCANCODE_RETURN2: return K_ENTER; case SDL_SCANCODE_ESCAPE: return K_ESCAPE; case SDL_SCANCODE_SPACE: return K_SPACE; case SDL_SCANCODE_A: return 'a'; case SDL_SCANCODE_B: return 'b'; case SDL_SCANCODE_C: return 'c'; case SDL_SCANCODE_D: return 'd'; case SDL_SCANCODE_E: return 'e'; case SDL_SCANCODE_F: return 'f'; case SDL_SCANCODE_G: return 'g'; case SDL_SCANCODE_H: return 'h'; case SDL_SCANCODE_I: return 'i'; case SDL_SCANCODE_J: return 'j'; case SDL_SCANCODE_K: return 'k'; case SDL_SCANCODE_L: return 'l'; case SDL_SCANCODE_M: return 'm'; case SDL_SCANCODE_N: return 'n'; case SDL_SCANCODE_O: return 'o'; case SDL_SCANCODE_P: return 'p'; case SDL_SCANCODE_Q: return 'q'; case SDL_SCANCODE_R: return 'r'; case SDL_SCANCODE_S: return 's'; case SDL_SCANCODE_T: return 't'; case SDL_SCANCODE_U: return 'u'; case SDL_SCANCODE_V: return 'v'; case SDL_SCANCODE_W: return 'w'; case SDL_SCANCODE_X: return 'x'; case SDL_SCANCODE_Y: return 'y'; case SDL_SCANCODE_Z: return 'z'; case SDL_SCANCODE_1: return '1'; case SDL_SCANCODE_2: return '2'; case SDL_SCANCODE_3: return '3'; case SDL_SCANCODE_4: return '4'; case SDL_SCANCODE_5: return '5'; case SDL_SCANCODE_6: return '6'; case SDL_SCANCODE_7: return '7'; case SDL_SCANCODE_8: return '8'; case SDL_SCANCODE_9: return '9'; case SDL_SCANCODE_0: return '0'; case SDL_SCANCODE_MINUS: return '-'; case SDL_SCANCODE_EQUALS: return '='; case SDL_SCANCODE_LEFTBRACKET: return '['; case SDL_SCANCODE_RIGHTBRACKET: return ']'; case SDL_SCANCODE_BACKSLASH: return '\\'; case SDL_SCANCODE_NONUSHASH: return '#'; case SDL_SCANCODE_SEMICOLON: return ';'; case SDL_SCANCODE_APOSTROPHE: return '\''; case SDL_SCANCODE_GRAVE: return '`'; case SDL_SCANCODE_COMMA: return ','; case SDL_SCANCODE_PERIOD: return '.'; case SDL_SCANCODE_SLASH: return '/'; case SDL_SCANCODE_NONUSBACKSLASH: return '\\'; case SDL_SCANCODE_BACKSPACE: return K_BACKSPACE; case SDL_SCANCODE_UP: return K_UPARROW; case SDL_SCANCODE_DOWN: return K_DOWNARROW; case SDL_SCANCODE_LEFT: return K_LEFTARROW; case SDL_SCANCODE_RIGHT: return K_RIGHTARROW; case SDL_SCANCODE_LALT: return K_ALT; case SDL_SCANCODE_RALT: return K_ALT; case SDL_SCANCODE_LCTRL: return K_CTRL; case SDL_SCANCODE_RCTRL: return K_CTRL; case SDL_SCANCODE_LSHIFT: return K_SHIFT; case SDL_SCANCODE_RSHIFT: return K_SHIFT; case SDL_SCANCODE_F1: return K_F1; case SDL_SCANCODE_F2: return K_F2; case SDL_SCANCODE_F3: return K_F3; case SDL_SCANCODE_F4: return K_F4; case SDL_SCANCODE_F5: return K_F5; case SDL_SCANCODE_F6: return K_F6; case SDL_SCANCODE_F7: return K_F7; case SDL_SCANCODE_F8: return K_F8; case SDL_SCANCODE_F9: return K_F9; case SDL_SCANCODE_F10: return K_F10; case SDL_SCANCODE_F11: return K_F11; case SDL_SCANCODE_F12: return K_F12; case SDL_SCANCODE_INSERT: return K_INS; case SDL_SCANCODE_DELETE: return K_DEL; case SDL_SCANCODE_PAGEDOWN: return K_PGDN; case SDL_SCANCODE_PAGEUP: return K_PGUP; case SDL_SCANCODE_HOME: return K_HOME; case SDL_SCANCODE_END: return K_END; case SDL_SCANCODE_NUMLOCKCLEAR: return K_KP_NUMLOCK; case SDL_SCANCODE_KP_DIVIDE: return K_KP_SLASH; case SDL_SCANCODE_KP_MULTIPLY: return K_KP_STAR; case SDL_SCANCODE_KP_MINUS: return K_KP_MINUS; case SDL_SCANCODE_KP_7: return K_KP_HOME; case SDL_SCANCODE_KP_8: return K_KP_UPARROW; case SDL_SCANCODE_KP_9: return K_KP_PGUP; case SDL_SCANCODE_KP_PLUS: return K_KP_PLUS; case SDL_SCANCODE_KP_4: return K_KP_LEFTARROW; case SDL_SCANCODE_KP_5: return K_KP_5; case SDL_SCANCODE_KP_6: return K_KP_RIGHTARROW; case SDL_SCANCODE_KP_1: return K_KP_END; case SDL_SCANCODE_KP_2: return K_KP_DOWNARROW; case SDL_SCANCODE_KP_3: return K_KP_PGDN; case SDL_SCANCODE_KP_ENTER: return K_KP_ENTER; case SDL_SCANCODE_KP_0: return K_KP_INS; case SDL_SCANCODE_KP_PERIOD: return K_KP_DEL; case SDL_SCANCODE_LGUI: return K_COMMAND; case SDL_SCANCODE_RGUI: return K_COMMAND; case SDL_SCANCODE_PAUSE: return K_PAUSE; default: return 0; } } #endif #if defined(USE_SDL2) static void IN_DebugTextEvent(SDL_Event *event) { Con_Printf ("SDL_TEXTINPUT '%s' time: %g\n", event->text.text, Sys_DoubleTime()); } #endif static void IN_DebugKeyEvent(SDL_Event *event) { const char *eventtype = (event->key.state == SDL_PRESSED) ? "SDL_KEYDOWN" : "SDL_KEYUP"; #if defined(USE_SDL2) Con_Printf ("%s scancode: '%s' keycode: '%s' time: %g\n", eventtype, SDL_GetScancodeName(event->key.keysym.scancode), SDL_GetKeyName(event->key.keysym.sym), Sys_DoubleTime()); #else Con_Printf ("%s sym: '%s' unicode: %04x time: %g\n", eventtype, SDL_GetKeyName(event->key.keysym.sym), (int)event->key.keysym.unicode, Sys_DoubleTime()); #endif } void IN_SendKeyEvents (void) { SDL_Event event; int key; qboolean down; while (SDL_PollEvent(&event)) { switch (event.type) { #if defined(USE_SDL2) case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) S_UnblockSound(); else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) S_BlockSound(); break; #else case SDL_ACTIVEEVENT: if (event.active.state & (SDL_APPINPUTFOCUS|SDL_APPACTIVE)) { if (event.active.gain) S_UnblockSound(); else S_BlockSound(); } break; #endif #if defined(USE_SDL2) case SDL_TEXTINPUT: if (in_debugkeys.value) IN_DebugTextEvent(&event); // SDL2: We use SDL_TEXTINPUT for typing in the console / chat. // SDL2 uses the local keyboard layout and handles modifiers // (shift for uppercase, etc.) for us. { unsigned char *ch; for (ch = (unsigned char *)event.text.text; *ch; ch++) if ((*ch & ~0x7F) == 0) Char_Event (*ch); } break; #endif case SDL_KEYDOWN: case SDL_KEYUP: down = (event.key.state == SDL_PRESSED); if (in_debugkeys.value) IN_DebugKeyEvent(&event); #if defined(USE_SDL2) // SDL2: we interpret the keyboard as the US layout, so keybindings // are based on key position, not the label on the key cap. key = IN_SDL2_ScancodeToQuakeKey(event.key.keysym.scancode); #else key = IN_SDL_KeysymToQuakeKey(event.key.keysym.sym); #endif Key_Event (key, down); #if !defined(USE_SDL2) if (down && (event.key.keysym.unicode & ~0x7F) == 0) Char_Event (event.key.keysym.unicode); #endif break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if (event.button.button < 1 || event.button.button > sizeof(buttonremap) / sizeof(buttonremap[0])) { Con_Printf ("Ignored event for mouse button %d\n", event.button.button); break; } Key_Event(buttonremap[event.button.button - 1], event.button.state == SDL_PRESSED); break; #if defined(USE_SDL2) case SDL_MOUSEWHEEL: if (event.wheel.y > 0) { Key_Event(K_MWHEELUP, true); Key_Event(K_MWHEELUP, false); } else if (event.wheel.y < 0) { Key_Event(K_MWHEELDOWN, true); Key_Event(K_MWHEELDOWN, false); } break; #endif case SDL_MOUSEMOTION: IN_MouseMotion(event.motion.xrel, event.motion.yrel); break; #if defined(USE_SDL2) case SDL_CONTROLLERDEVICEADDED: if (joy_active_instaceid == -1) { joy_active_controller = SDL_GameControllerOpen(event.cdevice.which); if (joy_active_controller == NULL) Con_DPrintf("Couldn't open game controller\n"); else { SDL_Joystick *joy; joy = SDL_GameControllerGetJoystick(joy_active_controller); joy_active_instaceid = SDL_JoystickInstanceID(joy); } } else Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEADDED\n"); break; case SDL_CONTROLLERDEVICEREMOVED: if (joy_active_instaceid != -1 && event.cdevice.which == joy_active_instaceid) { SDL_GameControllerClose(joy_active_controller); joy_active_controller = NULL; joy_active_instaceid = -1; } else Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMOVED\n"); break; case SDL_CONTROLLERDEVICEREMAPPED: Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMAPPED\n"); break; #endif case SDL_QUIT: CL_Disconnect (); Sys_Quit (); break; default: break; } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_codec.h�����������������������������������������������������������������0000644�0000000�0000000�00000005742�12220541170�015615� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com> * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SND_CODEC_H_ #define _SND_CODEC_H_ typedef struct snd_info_s { int rate; int bits, width; int channels; int samples; int blocksize; int size; int dataofs; } snd_info_t; typedef enum { STREAM_NONE = -1, STREAM_INIT, STREAM_PAUSE, STREAM_PLAY } stream_status_t; typedef struct snd_codec_s snd_codec_t; typedef struct snd_stream_s { fshandle_t fh; qboolean pak; char name[MAX_QPATH]; /* name of the source file */ snd_info_t info; stream_status_t status; snd_codec_t *codec; /* codec handling this stream */ void *priv; /* data private to the codec. */ } snd_stream_t; void S_CodecInit (void); void S_CodecShutdown (void); /* Callers of the following S_CodecOpenStream* functions * are reponsible for attaching any path to the filename */ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type); /* Decides according to the required type. */ snd_stream_t *S_CodecOpenStreamAny (const char *filename); /* Decides according to file extension. if the * name has no extension, try all available. */ snd_stream_t *S_CodecOpenStreamExt (const char *filename); /* Decides according to file extension. the name * MUST have an extension. */ void S_CodecCloseStream (snd_stream_t *stream); int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer); int S_CodecRewindStream (snd_stream_t *stream); snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec); void S_CodecUtilClose(snd_stream_t **stream); #define CODECTYPE_NONE 0 #define CODECTYPE_MID (1U << 0) #define CODECTYPE_MOD (1U << 1) #define CODECTYPE_FLAC (1U << 2) #define CODECTYPE_WAV (1U << 3) #define CODECTYPE_MP3 (1U << 4) #define CODECTYPE_VORBIS (1U << 5) #define CODECTYPE_OPUS (1U << 6) #define CODECTYPE_UMX (1U << 7) #define CODECTYPE_WAVE CODECTYPE_WAV #define CODECTYPE_MIDI CODECTYPE_MID int S_CodecIsAvailable (unsigned int type); /* return 1 if available, 0 if codec failed init * or -1 if no such codec is present. */ #endif /* _SND_CODEC_H_ */ ������������������������������quakespasm-0.93.0/Quake/render.h��������������������������������������������������������������������0000644�0000000�0000000�00000013770�13070251546�015164� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_RENDER_H #define _QUAKE_RENDER_H // refresh.h -- public interface to refresh functions #define MAXCLIPPLANES 11 #define TOP_RANGE 16 // soldier uniform colors #define BOTTOM_RANGE 96 //============================================================================= typedef struct efrag_s { struct efrag_s *leafnext; struct entity_s *entity; } efrag_t; //johnfitz -- for lerping #define LERP_MOVESTEP (1<<0) //this is a MOVETYPE_STEP entity, enable movement lerp #define LERP_RESETANIM (1<<1) //disable anim lerping until next anim frame #define LERP_RESETANIM2 (1<<2) //set this and previous flag to disable anim lerping for two anim frames #define LERP_RESETMOVE (1<<3) //disable movement lerping until next origin/angles change #define LERP_FINISH (1<<4) //use lerpfinish time from server update instead of assuming interval of 0.1 //johnfitz typedef struct entity_s { qboolean forcelink; // model changed int update_type; entity_state_t baseline; // to fill in defaults in updates double msgtime; // time of last update vec3_t msg_origins[2]; // last two updates (0 is newest) vec3_t origin; vec3_t msg_angles[2]; // last two updates (0 is newest) vec3_t angles; struct qmodel_s *model; // NULL = no model struct efrag_s *efrag; // linked list of efrags int frame; float syncbase; // for client-side animations byte *colormap; int effects; // light, particles, etc int skinnum; // for Alias models int visframe; // last frame this entity was // found in an active leaf int dlightframe; // dynamic lighting int dlightbits; // FIXME: could turn these into a union int trivial_accept; struct mnode_s *topnode; // for bmodels, first world node // that splits bmodel, or NULL if // not split byte alpha; //johnfitz -- alpha byte lerpflags; //johnfitz -- lerping float lerpstart; //johnfitz -- animation lerping float lerptime; //johnfitz -- animation lerping float lerpfinish; //johnfitz -- lerping -- server sent us a more accurate interval, use it instead of 0.1 short previouspose; //johnfitz -- animation lerping short currentpose; //johnfitz -- animation lerping // short futurepose; //johnfitz -- animation lerping float movelerpstart; //johnfitz -- transform lerping vec3_t previousorigin; //johnfitz -- transform lerping vec3_t currentorigin; //johnfitz -- transform lerping vec3_t previousangles; //johnfitz -- transform lerping vec3_t currentangles; //johnfitz -- transform lerping } entity_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vrect_t vrect; // subwindow in video for refresh // FIXME: not need vrect next field here? vrect_t aliasvrect; // scaled Alias version int vrectright, vrectbottom; // right & bottom screen coords int aliasvrectright, aliasvrectbottom; // scaled Alias versions float vrectrightedge; // rightmost right edge we care about, // for use in edge list float fvrectx, fvrecty; // for floating-point compares float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping float horizontalFieldOfView; // at Z = 1.0, this many X is visible // 2.0 = 90 degrees float xOrigin; // should probably allways be 0.5 float yOrigin; // between be around 0.3 to 0.5 vec3_t vieworg; vec3_t viewangles; float fov_x, fov_y; int ambientlight; } refdef_t; // // refresh // extern int reinit_surfcache; extern refdef_t r_refdef; extern vec3_t r_origin, vpn, vright, vup; void R_Init (void); void R_InitTextures (void); void R_InitEfrags (void); void R_RenderView (void); // must set r_refdef first void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect); // called whenever r_refdef or vid change //void R_InitSky (struct texture_s *mt); // called at level load void R_CheckEfrags (void); //johnfitz void R_AddEfrags (entity_t *ent); void R_NewMap (void); void R_ParseParticleEffect (void); void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); void R_RocketTrail (vec3_t start, vec3_t end, int type); void R_EntityParticles (entity_t *ent); void R_BlobExplosion (vec3_t org); void R_ParticleExplosion (vec3_t org); void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength); void R_LavaSplash (vec3_t org); void R_TeleportSplash (vec3_t org); void R_PushDlights (void); // // surface cache related // extern int reinit_surfcache; // if 1, surface cache is currently empty and extern qboolean r_cache_thrash; // set if thrashing the surface cache int D_SurfaceCacheForRes (int width, int height); void D_FlushCaches (void); void D_DeleteSurfaceCache (void); void D_InitCaches (void *buffer, int size); void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); #endif /* _QUAKE_RENDER_H */ ��������quakespasm-0.93.0/Quake/q_ctype.h�������������������������������������������������������������������0000644�0000000�0000000�00000004044�12757031150�015341� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Locale insensitive ctype.h functions taken from the RPM library - * RPM is Copyright (c) 1998 by Red Hat Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q_CTYPE_H #define Q_CTYPE_H static inline int q_isascii(int c) { return ((c & ~0x7f) == 0); } static inline int q_islower(int c) { return (c >= 'a' && c <= 'z'); } static inline int q_isupper(int c) { return (c >= 'A' && c <= 'Z'); } static inline int q_isalpha(int c) { return (q_islower(c) || q_isupper(c)); } static inline int q_isdigit(int c) { return (c >= '0' && c <= '9'); } static inline int q_isxdigit(int c) { return (q_isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } static inline int q_isalnum(int c) { return (q_isalpha(c) || q_isdigit(c)); } static inline int q_isblank(int c) { return (c == ' ' || c == '\t'); } static inline int q_isspace(int c) { switch(c) { case ' ': case '\t': case '\n': case '\r': case '\f': case '\v': return 1; } return 0; } static inline int q_isgraph(int c) { return (c > 0x20 && c <= 0x7e); } static inline int q_isprint(int c) { return (c >= 0x20 && c <= 0x7e); } static inline int q_toascii(int c) { return (c & 0x7f); } static inline int q_tolower(int c) { return ((q_isupper(c)) ? (c | ('a' - 'A')) : c); } static inline int q_toupper(int c) { return ((q_islower(c)) ? (c & ~('a' - 'A')) : c); } #endif /* Q_CTYPE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_sky.c��������������������������������������������������������������������0000644�0000000�0000000�00000052250�12555253135�015167� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //gl_sky.c #include "quakedef.h" #define MAX_CLIP_VERTS 64 float Fog_GetDensity(void); float *Fog_GetColor(void); extern qmodel_t *loadmodel; extern int rs_skypolys; //for r_speeds readout extern int rs_skypasses; //for r_speeds readout float skyflatcolor[3]; float skymins[2][6], skymaxs[2][6]; char skybox_name[32] = ""; //name of current skybox, or "" if no skybox gltexture_t *skybox_textures[6]; gltexture_t *solidskytexture, *alphaskytexture; extern cvar_t gl_farclip; cvar_t r_fastsky = {"r_fastsky", "0", CVAR_NONE}; cvar_t r_sky_quality = {"r_sky_quality", "12", CVAR_NONE}; cvar_t r_skyalpha = {"r_skyalpha", "1", CVAR_NONE}; cvar_t r_skyfog = {"r_skyfog","0.5",CVAR_NONE}; int skytexorder[6] = {0,2,1,3,4,5}; //for skybox vec3_t skyclip[6] = { {1,1,0}, {1,-1,0}, {0,-1,1}, {0,1,1}, {1,0,1}, {-1,0,1} }; int st_to_vec[6][3] = { {3,-1,2}, {-3,1,2}, {1,3,2}, {-1,-3,2}, {-2,-1,3}, // straight up {2,-1,-3} // straight down }; int vec_to_st[6][3] = { {-2,3,1}, {2,3,-1}, {1,3,2}, {-1,3,-2}, {-2,-1,3}, {-2,1,-3} }; float skyfog; // ericw //============================================================================== // // INIT // //============================================================================== /* ============= Sky_LoadTexture A sky texture is 256*128, with the left side being a masked overlay ============== */ void Sky_LoadTexture (texture_t *mt) { char texturename[64]; int i, j, p, r, g, b, count; byte *src; static byte front_data[128*128]; //FIXME: Hunk_Alloc static byte back_data[128*128]; //FIXME: Hunk_Alloc unsigned *rgba; src = (byte *)mt + mt->offsets[0]; // extract back layer and upload for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) back_data[(i*128) + j] = src[i*256 + j + 128]; q_snprintf(texturename, sizeof(texturename), "%s:%s_back", loadmodel->name, mt->name); solidskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, back_data, "", (src_offset_t)back_data, TEXPREF_NONE); // extract front layer and upload for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) { front_data[(i*128) + j] = src[i*256 + j]; if (front_data[(i*128) + j] == 0) front_data[(i*128) + j] = 255; } q_snprintf(texturename, sizeof(texturename), "%s:%s_front", loadmodel->name, mt->name); alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA); // calculate r_fastsky color based on average of all opaque foreground colors r = g = b = count = 0; for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) { p = src[i*256 + j]; if (p != 0) { rgba = &d_8to24table[p]; r += ((byte *)rgba)[0]; g += ((byte *)rgba)[1]; b += ((byte *)rgba)[2]; count++; } } skyflatcolor[0] = (float)r/(count*255); skyflatcolor[1] = (float)g/(count*255); skyflatcolor[2] = (float)b/(count*255); } /* ================== Sky_LoadSkyBox ================== */ const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; void Sky_LoadSkyBox (const char *name) { int i, mark, width, height; char filename[MAX_OSPATH]; byte *data; qboolean nonefound = true; if (strcmp(skybox_name, name) == 0) return; //no change //purge old textures for (i=0; i<6; i++) { if (skybox_textures[i] && skybox_textures[i] != notexture) TexMgr_FreeTexture (skybox_textures[i]); skybox_textures[i] = NULL; } //turn off skybox if sky is set to "" if (name[0] == 0) { skybox_name[0] = 0; return; } //load textures for (i=0; i<6; i++) { mark = Hunk_LowMark (); q_snprintf (filename, sizeof(filename), "gfx/env/%s%s", name, suf[i]); data = Image_LoadImage (filename, &width, &height); if (data) { skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename, width, height, SRC_RGBA, data, filename, 0, TEXPREF_NONE); nonefound = false; } else { Con_Printf ("Couldn't load %s\n", filename); skybox_textures[i] = notexture; } Hunk_FreeToLowMark (mark); } if (nonefound) // go back to scrolling sky if skybox is totally missing { for (i=0; i<6; i++) { if (skybox_textures[i] && skybox_textures[i] != notexture) TexMgr_FreeTexture (skybox_textures[i]); skybox_textures[i] = NULL; } skybox_name[0] = 0; return; } strcpy(skybox_name, name); } /* ================= Sky_NewMap ================= */ void Sky_NewMap (void) { char key[128], value[4096]; const char *data; int i; // // initially no sky // skybox_name[0] = 0; for (i=0; i<6; i++) skybox_textures[i] = NULL; skyfog = r_skyfog.value; // // read worldspawn (this is so ugly, and shouldn't it be done on the server?) // data = cl.worldmodel->entities; if (!data) return; //FIXME: how could this possibly ever happen? -- if there's no // worldspawn then the sever wouldn't send the loadmap message to the client data = COM_Parse(data); if (!data) //should never happen return; // error if (com_token[0] != '{') //should never happen return; // error while (1) { data = COM_Parse(data); if (!data) return; // error if (com_token[0] == '}') break; // end of worldspawn if (com_token[0] == '_') strcpy(key, com_token + 1); else strcpy(key, com_token); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; data = COM_Parse(data); if (!data) return; // error strcpy(value, com_token); if (!strcmp("sky", key)) Sky_LoadSkyBox(value); if (!strcmp("skyfog", key)) skyfog = atof(value); #if 1 //also accept non-standard keys else if (!strcmp("skyname", key)) //half-life Sky_LoadSkyBox(value); else if (!strcmp("qlsky", key)) //quake lives Sky_LoadSkyBox(value); #endif } } /* ================= Sky_SkyCommand_f ================= */ void Sky_SkyCommand_f (void) { switch (Cmd_Argc()) { case 1: Con_Printf("\"sky\" is \"%s\"\n", skybox_name); break; case 2: Sky_LoadSkyBox(Cmd_Argv(1)); break; default: Con_Printf("usage: sky <skyname>\n"); } } /* ==================== R_SetSkyfog_f -- ericw ==================== */ static void R_SetSkyfog_f (cvar_t *var) { // clear any skyfog setting from worldspawn skyfog = var->value; } /* ============= Sky_Init ============= */ void Sky_Init (void) { int i; Cvar_RegisterVariable (&r_fastsky); Cvar_RegisterVariable (&r_sky_quality); Cvar_RegisterVariable (&r_skyalpha); Cvar_RegisterVariable (&r_skyfog); Cvar_SetCallback (&r_skyfog, R_SetSkyfog_f); Cmd_AddCommand ("sky",Sky_SkyCommand_f); for (i=0; i<6; i++) skybox_textures[i] = NULL; } //============================================================================== // // PROCESS SKY SURFS // //============================================================================== /* ================= Sky_ProjectPoly update sky bounds ================= */ void Sky_ProjectPoly (int nump, vec3_t vecs) { int i,j; vec3_t v, av; float s, t, dv; int axis; float *vp; // decide which face it maps to VectorCopy (vec3_origin, v); for (i=0, vp=vecs ; i<nump ; i++, vp+=3) { VectorAdd (vp, v, v); } av[0] = fabs(v[0]); av[1] = fabs(v[1]); av[2] = fabs(v[2]); if (av[0] > av[1] && av[0] > av[2]) { if (v[0] < 0) axis = 1; else axis = 0; } else if (av[1] > av[2] && av[1] > av[0]) { if (v[1] < 0) axis = 3; else axis = 2; } else { if (v[2] < 0) axis = 5; else axis = 4; } // project new texture coords for (i=0 ; i<nump ; i++, vecs+=3) { j = vec_to_st[axis][2]; if (j > 0) dv = vecs[j - 1]; else dv = -vecs[-j - 1]; j = vec_to_st[axis][0]; if (j < 0) s = -vecs[-j -1] / dv; else s = vecs[j-1] / dv; j = vec_to_st[axis][1]; if (j < 0) t = -vecs[-j -1] / dv; else t = vecs[j-1] / dv; if (s < skymins[0][axis]) skymins[0][axis] = s; if (t < skymins[1][axis]) skymins[1][axis] = t; if (s > skymaxs[0][axis]) skymaxs[0][axis] = s; if (t > skymaxs[1][axis]) skymaxs[1][axis] = t; } } /* ================= Sky_ClipPoly ================= */ void Sky_ClipPoly (int nump, vec3_t vecs, int stage) { float *norm; float *v; qboolean front, back; float d, e; float dists[MAX_CLIP_VERTS]; int sides[MAX_CLIP_VERTS]; vec3_t newv[2][MAX_CLIP_VERTS]; int newc[2]; int i, j; if (nump > MAX_CLIP_VERTS-2) Sys_Error ("Sky_ClipPoly: MAX_CLIP_VERTS"); if (stage == 6) // fully clipped { Sky_ProjectPoly (nump, vecs); return; } front = back = false; norm = skyclip[stage]; for (i=0, v = vecs ; i<nump ; i++, v+=3) { d = DotProduct (v, norm); if (d > ON_EPSILON) { front = true; sides[i] = SIDE_FRONT; } else if (d < ON_EPSILON) { back = true; sides[i] = SIDE_BACK; } else sides[i] = SIDE_ON; dists[i] = d; } if (!front || !back) { // not clipped Sky_ClipPoly (nump, vecs, stage+1); return; } // clip it sides[i] = sides[0]; dists[i] = dists[0]; VectorCopy (vecs, (vecs+(i*3)) ); newc[0] = newc[1] = 0; for (i=0, v = vecs ; i<nump ; i++, v+=3) { switch (sides[i]) { case SIDE_FRONT: VectorCopy (v, newv[0][newc[0]]); newc[0]++; break; case SIDE_BACK: VectorCopy (v, newv[1][newc[1]]); newc[1]++; break; case SIDE_ON: VectorCopy (v, newv[0][newc[0]]); newc[0]++; VectorCopy (v, newv[1][newc[1]]); newc[1]++; break; } if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; d = dists[i] / (dists[i] - dists[i+1]); for (j=0 ; j<3 ; j++) { e = v[j] + d*(v[j+3] - v[j]); newv[0][newc[0]][j] = e; newv[1][newc[1]][j] = e; } newc[0]++; newc[1]++; } // continue Sky_ClipPoly (newc[0], newv[0][0], stage+1); Sky_ClipPoly (newc[1], newv[1][0], stage+1); } /* ================ Sky_ProcessPoly ================ */ void Sky_ProcessPoly (glpoly_t *p) { int i; vec3_t verts[MAX_CLIP_VERTS]; //draw it DrawGLPoly(p); rs_brushpasses++; //update sky bounds if (!r_fastsky.value) { for (i=0 ; i<p->numverts ; i++) VectorSubtract (p->verts[i], r_origin, verts[i]); Sky_ClipPoly (p->numverts, verts[0], 0); } } /* ================ Sky_ProcessTextureChains -- handles sky polys in world model ================ */ void Sky_ProcessTextureChains (void) { int i; msurface_t *s; texture_t *t; if (!r_drawworld_cheatsafe) return; for (i=0 ; i<cl.worldmodel->numtextures ; i++) { t = cl.worldmodel->textures[i]; if (!t || !t->texturechains[chain_world] || !(t->texturechains[chain_world]->flags & SURF_DRAWSKY)) continue; for (s = t->texturechains[chain_world]; s; s = s->texturechain) if (!s->culled) Sky_ProcessPoly (s->polys); } } /* ================ Sky_ProcessEntities -- handles sky polys on brush models ================ */ void Sky_ProcessEntities (void) { entity_t *e; msurface_t *s; glpoly_t *p; int i,j,k,mark; float dot; qboolean rotated; vec3_t temp, forward, right, up; if (!r_drawentities.value) return; for (i=0 ; i<cl_numvisedicts ; i++) { e = cl_visedicts[i]; if (e->model->type != mod_brush) continue; if (R_CullModelForEntity(e)) continue; if (e->alpha == ENTALPHA_ZERO) continue; VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; AngleVectors (e->angles, forward, right, up); VectorCopy (modelorg, temp); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } else rotated = false; s = &e->model->surfaces[e->model->firstmodelsurface]; for (j=0 ; j<e->model->nummodelsurfaces ; j++, s++) { if (s->flags & SURF_DRAWSKY) { dot = DotProduct (modelorg, s->plane->normal) - s->plane->dist; if (((s->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(s->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { //copy the polygon and translate manually, since Sky_ProcessPoly needs it to be in world space mark = Hunk_LowMark(); p = (glpoly_t *) Hunk_Alloc (sizeof(*s->polys)); //FIXME: don't allocate for each poly p->numverts = s->polys->numverts; for (k=0; k<p->numverts; k++) { if (rotated) { p->verts[k][0] = e->origin[0] + s->polys->verts[k][0] * forward[0] - s->polys->verts[k][1] * right[0] + s->polys->verts[k][2] * up[0]; p->verts[k][1] = e->origin[1] + s->polys->verts[k][0] * forward[1] - s->polys->verts[k][1] * right[1] + s->polys->verts[k][2] * up[1]; p->verts[k][2] = e->origin[2] + s->polys->verts[k][0] * forward[2] - s->polys->verts[k][1] * right[2] + s->polys->verts[k][2] * up[2]; } else VectorAdd(s->polys->verts[k], e->origin, p->verts[k]); } Sky_ProcessPoly (p); Hunk_FreeToLowMark (mark); } } } } } //============================================================================== // // RENDER SKYBOX // //============================================================================== /* ============== Sky_EmitSkyBoxVertex ============== */ void Sky_EmitSkyBoxVertex (float s, float t, int axis) { vec3_t v, b; int j, k; float w, h; b[0] = s * gl_farclip.value / sqrt(3.0); b[1] = t * gl_farclip.value / sqrt(3.0); b[2] = gl_farclip.value / sqrt(3.0); for (j=0 ; j<3 ; j++) { k = st_to_vec[axis][j]; if (k < 0) v[j] = -b[-k - 1]; else v[j] = b[k - 1]; v[j] += r_origin[j]; } // convert from range [-1,1] to [0,1] s = (s+1)*0.5; t = (t+1)*0.5; // avoid bilerp seam w = skybox_textures[skytexorder[axis]]->width; h = skybox_textures[skytexorder[axis]]->height; s = s * (w-1)/w + 0.5/w; t = t * (h-1)/h + 0.5/h; t = 1.0 - t; glTexCoord2f (s, t); glVertex3fv (v); } /* ============== Sky_DrawSkyBox FIXME: eliminate cracks by adding an extra vert on tjuncs ============== */ void Sky_DrawSkyBox (void) { int i; for (i=0 ; i<6 ; i++) { if (skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i]) continue; GL_Bind (skybox_textures[skytexorder[i]]); #if 1 //FIXME: this is to avoid tjunctions until i can do it the right way skymins[0][i] = -1; skymins[1][i] = -1; skymaxs[0][i] = 1; skymaxs[1][i] = 1; #endif glBegin (GL_QUADS); Sky_EmitSkyBoxVertex (skymins[0][i], skymins[1][i], i); Sky_EmitSkyBoxVertex (skymins[0][i], skymaxs[1][i], i); Sky_EmitSkyBoxVertex (skymaxs[0][i], skymaxs[1][i], i); Sky_EmitSkyBoxVertex (skymaxs[0][i], skymins[1][i], i); glEnd (); rs_skypolys++; rs_skypasses++; if (Fog_GetDensity() > 0 && skyfog > 0) { float *c; c = Fog_GetColor(); glEnable (GL_BLEND); glDisable (GL_TEXTURE_2D); glColor4f (c[0],c[1],c[2], CLAMP(0.0,skyfog,1.0)); glBegin (GL_QUADS); Sky_EmitSkyBoxVertex (skymins[0][i], skymins[1][i], i); Sky_EmitSkyBoxVertex (skymins[0][i], skymaxs[1][i], i); Sky_EmitSkyBoxVertex (skymaxs[0][i], skymaxs[1][i], i); Sky_EmitSkyBoxVertex (skymaxs[0][i], skymins[1][i], i); glEnd (); glColor3f (1, 1, 1); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); rs_skypasses++; } } } //============================================================================== // // RENDER CLOUDS // //============================================================================== /* ============== Sky_SetBoxVert ============== */ void Sky_SetBoxVert (float s, float t, int axis, vec3_t v) { vec3_t b; int j, k; b[0] = s * gl_farclip.value / sqrt(3.0); b[1] = t * gl_farclip.value / sqrt(3.0); b[2] = gl_farclip.value / sqrt(3.0); for (j=0 ; j<3 ; j++) { k = st_to_vec[axis][j]; if (k < 0) v[j] = -b[-k - 1]; else v[j] = b[k - 1]; v[j] += r_origin[j]; } } /* ============= Sky_GetTexCoord ============= */ void Sky_GetTexCoord (vec3_t v, float speed, float *s, float *t) { vec3_t dir; float length, scroll; VectorSubtract (v, r_origin, dir); dir[2] *= 3; // flatten the sphere length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; length = sqrt (length); length = 6*63/length; scroll = cl.time*speed; scroll -= (int)scroll & ~127; *s = (scroll + dir[0] * length) * (1.0/128); *t = (scroll + dir[1] * length) * (1.0/128); } /* =============== Sky_DrawFaceQuad =============== */ void Sky_DrawFaceQuad (glpoly_t *p) { float s, t; float *v; int i; if (gl_mtexable && r_skyalpha.value >= 1.0) { GL_Bind (solidskytexture); GL_EnableMultitexture(); GL_Bind (alphaskytexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBegin (GL_QUADS); for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE) { Sky_GetTexCoord (v, 8, &s, &t); GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, s, t); Sky_GetTexCoord (v, 16, &s, &t); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, s, t); glVertex3fv (v); } glEnd (); GL_DisableMultitexture(); rs_skypolys++; rs_skypasses++; } else { GL_Bind (solidskytexture); if (r_skyalpha.value < 1.0) glColor3f (1, 1, 1); glBegin (GL_QUADS); for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE) { Sky_GetTexCoord (v, 8, &s, &t); glTexCoord2f (s, t); glVertex3fv (v); } glEnd (); GL_Bind (alphaskytexture); glEnable (GL_BLEND); if (r_skyalpha.value < 1.0) glColor4f (1, 1, 1, r_skyalpha.value); glBegin (GL_QUADS); for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE) { Sky_GetTexCoord (v, 16, &s, &t); glTexCoord2f (s, t); glVertex3fv (v); } glEnd (); glDisable (GL_BLEND); rs_skypolys++; rs_skypasses += 2; } if (Fog_GetDensity() > 0 && skyfog > 0) { float *c; c = Fog_GetColor(); glEnable (GL_BLEND); glDisable (GL_TEXTURE_2D); glColor4f (c[0],c[1],c[2], CLAMP(0.0,skyfog,1.0)); glBegin (GL_QUADS); for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE) glVertex3fv (v); glEnd (); glColor3f (1, 1, 1); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); rs_skypasses++; } } /* ============== Sky_DrawFace ============== */ void Sky_DrawFace (int axis) { glpoly_t *p; vec3_t verts[4]; int i, j, start; float di,qi,dj,qj; vec3_t vup, vright, temp, temp2; Sky_SetBoxVert(-1.0, -1.0, axis, verts[0]); Sky_SetBoxVert(-1.0, 1.0, axis, verts[1]); Sky_SetBoxVert(1.0, 1.0, axis, verts[2]); Sky_SetBoxVert(1.0, -1.0, axis, verts[3]); start = Hunk_LowMark (); p = (glpoly_t *) Hunk_Alloc(sizeof(glpoly_t)); VectorSubtract(verts[2],verts[3],vup); VectorSubtract(verts[2],verts[1],vright); di = q_max((int)r_sky_quality.value, 1); qi = 1.0 / di; dj = (axis < 4) ? di*2 : di; //subdivide vertically more than horizontally on skybox sides qj = 1.0 / dj; for (i=0; i<di; i++) { for (j=0; j<dj; j++) { if (i*qi < skymins[0][axis]/2+0.5 - qi || i*qi > skymaxs[0][axis]/2+0.5 || j*qj < skymins[1][axis]/2+0.5 - qj || j*qj > skymaxs[1][axis]/2+0.5) continue; //if (i&1 ^ j&1) continue; //checkerboard test VectorScale (vright, qi*i, temp); VectorScale (vup, qj*j, temp2); VectorAdd(temp,temp2,temp); VectorAdd(verts[0],temp,p->verts[0]); VectorScale (vup, qj, temp); VectorAdd (p->verts[0],temp,p->verts[1]); VectorScale (vright, qi, temp); VectorAdd (p->verts[1],temp,p->verts[2]); VectorAdd (p->verts[0],temp,p->verts[3]); Sky_DrawFaceQuad (p); } } Hunk_FreeToLowMark (start); } /* ============== Sky_DrawSkyLayers draws the old-style scrolling cloud layers ============== */ void Sky_DrawSkyLayers (void) { int i; if (r_skyalpha.value < 1.0) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); for (i=0 ; i<6 ; i++) if (skymins[0][i] < skymaxs[0][i] && skymins[1][i] < skymaxs[1][i]) Sky_DrawFace (i); if (r_skyalpha.value < 1.0) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } /* ============== Sky_DrawSky called once per frame before drawing anything else ============== */ void Sky_DrawSky (void) { int i; //in these special render modes, the sky faces are handled in the normal world/brush renderer if (r_drawflat_cheatsafe || r_lightmap_cheatsafe ) return; // // reset sky bounds // for (i=0 ; i<6 ; i++) { skymins[0][i] = skymins[1][i] = 9999; skymaxs[0][i] = skymaxs[1][i] = -9999; } // // process world and bmodels: draw flat-shaded sky surfs, and update skybounds // Fog_DisableGFog (); glDisable (GL_TEXTURE_2D); if (Fog_GetDensity() > 0) glColor3fv (Fog_GetColor()); else glColor3fv (skyflatcolor); Sky_ProcessTextureChains (); Sky_ProcessEntities (); glColor3f (1, 1, 1); glEnable (GL_TEXTURE_2D); // // render slow sky: cloud layers or skybox // if (!r_fastsky.value && !(Fog_GetDensity() > 0 && skyfog >= 1)) { glDepthFunc(GL_GEQUAL); glDepthMask(0); if (skybox_name[0]) Sky_DrawSkyBox (); else Sky_DrawSkyLayers(); glDepthMask(1); glDepthFunc(GL_LEQUAL); } Fog_EnableGFog (); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/progs.h���������������������������������������������������������������������0000644�0000000�0000000�00000010020�13157300151�015011� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_PROGS_H #define _QUAKE_PROGS_H #include "pr_comp.h" /* defs shared with qcc */ #include "progdefs.h" /* generated by program cdefs */ typedef union eval_s { string_t string; float _float; float vector[3]; func_t function; int _int; int edict; } eval_t; #define MAX_ENT_LEAFS 32 typedef struct edict_s { qboolean free; link_t area; /* linked to a division node or leaf */ int num_leafs; int leafnums[MAX_ENT_LEAFS]; entity_state_t baseline; unsigned char alpha; /* johnfitz -- hack to support alpha since it's not part of entvars_t */ qboolean sendinterval; /* johnfitz -- send time until nextthink to client for better lerp timing */ float freetime; /* sv.time when the object was freed */ entvars_t v; /* C exported fields from progs */ /* other fields from progs come immediately after */ } edict_t; #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) //============================================================================ extern dprograms_t *progs; extern dfunction_t *pr_functions; extern dstatement_t *pr_statements; extern globalvars_t *pr_global_struct; extern float *pr_globals; /* same as pr_global_struct */ extern int pr_edict_size; /* in bytes */ void PR_Init (void); void PR_ExecuteProgram (func_t fnum); void PR_LoadProgs (void); const char *PR_GetString (int num); int PR_SetEngineString (const char *s); int PR_AllocString (int bufferlength, char **ptr); void PR_Profile_f (void); edict_t *ED_Alloc (void); void ED_Free (edict_t *ed); void ED_Print (edict_t *ed); void ED_Write (FILE *f, edict_t *ed); const char *ED_ParseEdict (const char *data, edict_t *ent); void ED_WriteGlobals (FILE *f); const char *ED_ParseGlobals (const char *data); void ED_LoadFromFile (const char *data); /* #define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) #define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts) / pr_edict_size) */ edict_t *EDICT_NUM(int n); int NUM_FOR_EDICT(edict_t *e); #define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) #define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) #define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) #define G_FLOAT(o) (pr_globals[o]) #define G_INT(o) (*(int *)&pr_globals[o]) #define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) #define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) #define G_VECTOR(o) (&pr_globals[o]) #define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o])) #define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) #define E_FLOAT(e,o) (((float*)&e->v)[o]) #define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) #define E_VECTOR(e,o) (&((float*)&e->v)[o]) #define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o])) extern int type_size[8]; typedef void (*builtin_t) (void); extern builtin_t *pr_builtins; extern int pr_numbuiltins; extern int pr_argc; extern qboolean pr_trace; extern dfunction_t *pr_xfunction; extern int pr_xstatement; extern unsigned short pr_crc; FUNC_NORETURN void PR_RunError (const char *error, ...) FUNC_PRINTF(1,2); #ifdef __WATCOMC__ #pragma aux PR_RunError aborts; #endif void ED_PrintEdicts (void); void ED_PrintNum (int ent); eval_t *GetEdictFieldValue(edict_t *ed, const char *field); #endif /* _QUAKE_PROGS_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_loop.h������������������������������������������������������������������0000644�0000000�0000000�00000002552�12407762022�015517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __NET_LOOP_H #define __NET_LOOP_H // net_loop.h int Loop_Init (void); void Loop_Listen (qboolean state); void Loop_SearchForHosts (qboolean xmit); qsocket_t *Loop_Connect (const char *host); qsocket_t *Loop_CheckNewConnections (void); int Loop_GetMessage (qsocket_t *sock); int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data); int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); qboolean Loop_CanSendMessage (qsocket_t *sock); qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock); void Loop_Close (qsocket_t *sock); void Loop_Shutdown (void); #endif /* __NET_LOOP_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_vorbis.c����������������������������������������������������������������0000644�0000000�0000000�00000011757�12424476246�016063� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Ogg/Vorbis streaming music support, loosely based on several open source * Quake engine based projects with many modifications. * * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_VORBIS) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_vorbis.h" #define OV_EXCLUDE_STATIC_CALLBACKS #if defined(VORBIS_USE_TREMOR) /* for Tremor / Vorbisfile api differences, * see doc/diff.html in the Tremor package. */ #include <tremor/ivorbisfile.h> #else #include <vorbis/vorbisfile.h> #endif /* Vorbis codec can return the samples in a number of different * formats, we use the standard signed short format. */ #define VORBIS_SAMPLEBITS 16 #define VORBIS_SAMPLEWIDTH 2 #define VORBIS_SIGNED_DATA 1 /* CALLBACK FUNCTIONS: */ static int ovc_fclose (void *f) { return 0; /* we fclose() elsewhere. */ } static int ovc_fseek (void *f, ogg_int64_t off, int whence) { if (f == NULL) return (-1); return FS_fseek((fshandle_t *)f, (long) off, whence); } static ov_callbacks ovc_qfs = { (size_t (*)(void *, size_t, size_t, void *)) FS_fread, (int (*)(void *, ogg_int64_t, int)) ovc_fseek, (int (*)(void *)) ovc_fclose, (long (*)(void *)) FS_ftell }; #define OV_OPEN_CALLBACKS ov_open_callbacks static qboolean S_VORBIS_CodecInitialize (void) { return true; } static void S_VORBIS_CodecShutdown (void) { } static qboolean S_VORBIS_CodecOpenStream (snd_stream_t *stream) { OggVorbis_File *ovFile; vorbis_info *ovf_info; long numstreams; int res; ovFile = (OggVorbis_File *) Z_Malloc(sizeof(OggVorbis_File)); stream->priv = ovFile; res = OV_OPEN_CALLBACKS(&stream->fh, ovFile, NULL, 0, ovc_qfs); if (res != 0) { Con_Printf("%s is not a valid Ogg Vorbis file (error %i).\n", stream->name, res); goto _fail; } if (!ov_seekable(ovFile)) { Con_Printf("Stream %s not seekable.\n", stream->name); goto _fail; } ovf_info = ov_info(ovFile, 0); if (!ovf_info) { Con_Printf("Unable to get stream info for %s.\n", stream->name); goto _fail; } /* FIXME: handle section changes */ numstreams = ov_streams(ovFile); if (numstreams != 1) { Con_Printf("More than one (%ld) stream in %s.\n", numstreams, stream->name); goto _fail; } if (ovf_info->channels != 1 && ovf_info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", ovf_info->channels, stream->name); goto _fail; } stream->info.rate = ovf_info->rate; stream->info.channels = ovf_info->channels; stream->info.bits = VORBIS_SAMPLEBITS; stream->info.width = VORBIS_SAMPLEWIDTH; return true; _fail: if (res == 0) ov_clear(ovFile); Z_Free(ovFile); return false; } static int S_VORBIS_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int section; /* FIXME: handle section changes */ int cnt, res, rem; char * ptr; cnt = 0; rem = bytes; ptr = (char *) buffer; while (1) { /* # ov_read() from libvorbisfile returns the decoded PCM audio * in requested endianness, signedness and word size. * # ov_read() from Tremor (libvorbisidec) returns decoded audio * always in host-endian, signed 16 bit PCM format. * # For both of the libraries, if the audio is multichannel, * the channels are interleaved in the output buffer. */ res = ov_read( (OggVorbis_File *)stream->priv, ptr, rem, #if !defined(VORBIS_USE_TREMOR) host_bigendian, VORBIS_SAMPLEWIDTH, VORBIS_SIGNED_DATA, #endif /* ! VORBIS_USE_TREMOR */ §ion ); if (res <= 0) break; rem -= res; cnt += res; if (rem <= 0) break; ptr += res; } if (res < 0) return res; return cnt; } static void S_VORBIS_CodecCloseStream (snd_stream_t *stream) { ov_clear((OggVorbis_File *)stream->priv); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_VORBIS_CodecRewindStream (snd_stream_t *stream) { /* for libvorbisfile, the ov_time_seek() position argument * is seconds as doubles, whereas for Tremor libvorbisidec * it is milliseconds as 64 bit integers. */ return ov_time_seek ((OggVorbis_File *)stream->priv, 0); } snd_codec_t vorbis_codec = { CODECTYPE_VORBIS, true, /* always available. */ "ogg", S_VORBIS_CodecInitialize, S_VORBIS_CodecShutdown, S_VORBIS_CodecOpenStream, S_VORBIS_CodecReadStream, S_VORBIS_CodecRewindStream, S_VORBIS_CodecCloseStream, NULL }; #endif /* USE_CODEC_VORBIS */ �����������������quakespasm-0.93.0/Quake/sbar.c����������������������������������������������������������������������0000644�0000000�0000000�00000074223�12775777333�014651� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sbar.c -- status bar code #include "quakedef.h" int sb_updates; // if >= vid.numpages, no update needed #define STAT_MINUS 10 // num frame for '-' stats digit qpic_t *sb_nums[2][11]; qpic_t *sb_colon, *sb_slash; qpic_t *sb_ibar; qpic_t *sb_sbar; qpic_t *sb_scorebar; qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes qpic_t *sb_ammo[4]; qpic_t *sb_sigil[4]; qpic_t *sb_armor[3]; qpic_t *sb_items[32]; qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive // 0 is static, 1 is temporary animation qpic_t *sb_face_invis; qpic_t *sb_face_quad; qpic_t *sb_face_invuln; qpic_t *sb_face_invis_invuln; qboolean sb_showscores; int sb_lines; // scan lines to draw qpic_t *rsb_invbar[2]; qpic_t *rsb_weapons[5]; qpic_t *rsb_items[2]; qpic_t *rsb_ammo[3]; qpic_t *rsb_teambord; // PGM 01/19/97 - team color border //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher qpic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes //MED 01/04/97 added array to simplify weapon parsing int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT}; //MED 01/04/97 added hipnotic items array qpic_t *hsb_items[2]; void Sbar_MiniDeathmatchOverlay (void); void Sbar_DeathmatchOverlay (void); void M_DrawPic (int x, int y, qpic_t *pic); /* =============== Sbar_ShowScores Tab key down =============== */ void Sbar_ShowScores (void) { if (sb_showscores) return; sb_showscores = true; sb_updates = 0; } /* =============== Sbar_DontShowScores Tab key up =============== */ void Sbar_DontShowScores (void) { sb_showscores = false; sb_updates = 0; } /* =============== Sbar_Changed =============== */ void Sbar_Changed (void) { sb_updates = 0; // update next frame } /* =============== Sbar_LoadPics -- johnfitz -- load all the sbar pics =============== */ void Sbar_LoadPics (void) { int i; for (i = 0; i < 10; i++) { sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i)); sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i)); } sb_nums[0][10] = Draw_PicFromWad ("num_minus"); sb_nums[1][10] = Draw_PicFromWad ("anum_minus"); sb_colon = Draw_PicFromWad ("num_colon"); sb_slash = Draw_PicFromWad ("num_slash"); sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun"); sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun"); sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun"); sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun"); sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch"); sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch"); sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng"); sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun"); sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun"); sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun"); sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun"); sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch"); sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch"); sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng"); for (i = 0; i < 5; i++) { sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1)); sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1)); sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1)); sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1)); sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1)); sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1)); sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1)); } sb_ammo[0] = Draw_PicFromWad ("sb_shells"); sb_ammo[1] = Draw_PicFromWad ("sb_nails"); sb_ammo[2] = Draw_PicFromWad ("sb_rocket"); sb_ammo[3] = Draw_PicFromWad ("sb_cells"); sb_armor[0] = Draw_PicFromWad ("sb_armor1"); sb_armor[1] = Draw_PicFromWad ("sb_armor2"); sb_armor[2] = Draw_PicFromWad ("sb_armor3"); sb_items[0] = Draw_PicFromWad ("sb_key1"); sb_items[1] = Draw_PicFromWad ("sb_key2"); sb_items[2] = Draw_PicFromWad ("sb_invis"); sb_items[3] = Draw_PicFromWad ("sb_invuln"); sb_items[4] = Draw_PicFromWad ("sb_suit"); sb_items[5] = Draw_PicFromWad ("sb_quad"); sb_sigil[0] = Draw_PicFromWad ("sb_sigil1"); sb_sigil[1] = Draw_PicFromWad ("sb_sigil2"); sb_sigil[2] = Draw_PicFromWad ("sb_sigil3"); sb_sigil[3] = Draw_PicFromWad ("sb_sigil4"); sb_faces[4][0] = Draw_PicFromWad ("face1"); sb_faces[4][1] = Draw_PicFromWad ("face_p1"); sb_faces[3][0] = Draw_PicFromWad ("face2"); sb_faces[3][1] = Draw_PicFromWad ("face_p2"); sb_faces[2][0] = Draw_PicFromWad ("face3"); sb_faces[2][1] = Draw_PicFromWad ("face_p3"); sb_faces[1][0] = Draw_PicFromWad ("face4"); sb_faces[1][1] = Draw_PicFromWad ("face_p4"); sb_faces[0][0] = Draw_PicFromWad ("face5"); sb_faces[0][1] = Draw_PicFromWad ("face_p5"); sb_face_invis = Draw_PicFromWad ("face_invis"); sb_face_invuln = Draw_PicFromWad ("face_invul2"); sb_face_invis_invuln = Draw_PicFromWad ("face_inv2"); sb_face_quad = Draw_PicFromWad ("face_quad"); sb_sbar = Draw_PicFromWad ("sbar"); sb_ibar = Draw_PicFromWad ("ibar"); sb_scorebar = Draw_PicFromWad ("scorebar"); //MED 01/04/97 added new hipnotic weapons if (hipnotic) { hsb_weapons[0][0] = Draw_PicFromWad ("inv_laser"); hsb_weapons[0][1] = Draw_PicFromWad ("inv_mjolnir"); hsb_weapons[0][2] = Draw_PicFromWad ("inv_gren_prox"); hsb_weapons[0][3] = Draw_PicFromWad ("inv_prox_gren"); hsb_weapons[0][4] = Draw_PicFromWad ("inv_prox"); hsb_weapons[1][0] = Draw_PicFromWad ("inv2_laser"); hsb_weapons[1][1] = Draw_PicFromWad ("inv2_mjolnir"); hsb_weapons[1][2] = Draw_PicFromWad ("inv2_gren_prox"); hsb_weapons[1][3] = Draw_PicFromWad ("inv2_prox_gren"); hsb_weapons[1][4] = Draw_PicFromWad ("inv2_prox"); for (i = 0; i < 5; i++) { hsb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_laser",i+1)); hsb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_mjolnir",i+1)); hsb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_gren_prox",i+1)); hsb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_prox_gren",i+1)); hsb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_prox",i+1)); } hsb_items[0] = Draw_PicFromWad ("sb_wsuit"); hsb_items[1] = Draw_PicFromWad ("sb_eshld"); } if (rogue) { rsb_invbar[0] = Draw_PicFromWad ("r_invbar1"); rsb_invbar[1] = Draw_PicFromWad ("r_invbar2"); rsb_weapons[0] = Draw_PicFromWad ("r_lava"); rsb_weapons[1] = Draw_PicFromWad ("r_superlava"); rsb_weapons[2] = Draw_PicFromWad ("r_gren"); rsb_weapons[3] = Draw_PicFromWad ("r_multirock"); rsb_weapons[4] = Draw_PicFromWad ("r_plasma"); rsb_items[0] = Draw_PicFromWad ("r_shield1"); rsb_items[1] = Draw_PicFromWad ("r_agrav1"); // PGM 01/19/97 - team color border rsb_teambord = Draw_PicFromWad ("r_teambord"); // PGM 01/19/97 - team color border rsb_ammo[0] = Draw_PicFromWad ("r_ammolava"); rsb_ammo[1] = Draw_PicFromWad ("r_ammomulti"); rsb_ammo[2] = Draw_PicFromWad ("r_ammoplasma"); } } /* =============== Sbar_Init -- johnfitz -- rewritten =============== */ void Sbar_Init (void) { Cmd_AddCommand ("+showscores", Sbar_ShowScores); Cmd_AddCommand ("-showscores", Sbar_DontShowScores); Sbar_LoadPics (); } //============================================================================= // drawing routines are relative to the status bar location /* ============= Sbar_DrawPic -- johnfitz -- rewritten now that GL_SetCanvas is doing the work ============= */ void Sbar_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x, y + 24, pic); } /* ============= Sbar_DrawPicAlpha -- johnfitz ============= */ void Sbar_DrawPicAlpha (int x, int y, qpic_t *pic, float alpha) { glDisable (GL_ALPHA_TEST); glEnable (GL_BLEND); glColor4f(1,1,1,alpha); Draw_Pic (x, y + 24, pic); glColor4f(1,1,1,1); // ericw -- changed from glColor3f to work around intel 855 bug with "r_oldwater 0" and "scr_sbaralpha 0" glDisable (GL_BLEND); glEnable (GL_ALPHA_TEST); } /* ================ Sbar_DrawCharacter -- johnfitz -- rewritten now that GL_SetCanvas is doing the work ================ */ void Sbar_DrawCharacter (int x, int y, int num) { Draw_Character (x, y + 24, num); } /* ================ Sbar_DrawString -- johnfitz -- rewritten now that GL_SetCanvas is doing the work ================ */ void Sbar_DrawString (int x, int y, const char *str) { Draw_String (x, y + 24, str); } /* =============== Sbar_DrawScrollString -- johnfitz scroll the string inside a glscissor region =============== */ void Sbar_DrawScrollString (int x, int y, int width, const char *str) { float scale; int len, ofs, left; scale = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); left = x * scale; if (cl.gametype != GAME_DEATHMATCH) left += (((float)glwidth - 320.0 * scale) / 2); glEnable (GL_SCISSOR_TEST); glScissor (left, 0, width * scale, glheight); len = strlen(str)*8 + 40; ofs = ((int)(realtime*30))%len; Sbar_DrawString (x - ofs, y, str); Sbar_DrawCharacter (x - ofs + len - 32, y, '/'); Sbar_DrawCharacter (x - ofs + len - 24, y, '/'); Sbar_DrawCharacter (x - ofs + len - 16, y, '/'); Sbar_DrawString (x - ofs + len, y, str); glDisable (GL_SCISSOR_TEST); } /* ============= Sbar_itoa ============= */ int Sbar_itoa (int num, char *buf) { char *str; int pow10; int dig; str = buf; if (num < 0) { *str++ = '-'; num = -num; } for (pow10 = 10 ; num >= pow10 ; pow10 *= 10) ; do { pow10 /= 10; dig = num/pow10; *str++ = '0'+dig; num -= dig*pow10; } while (pow10 != 1); *str = 0; return str-buf; } /* ============= Sbar_DrawNum ============= */ void Sbar_DrawNum (int x, int y, int num, int digits, int color) { char str[12]; char *ptr; int l, frame; num = q_min(999,num); //johnfitz -- cap high values rather than truncating number l = Sbar_itoa (num, str); ptr = str; if (l > digits) ptr += (l-digits); if (l < digits) x += (digits-l)*24; while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Sbar_DrawPic (x,y,sb_nums[color][frame]); //johnfitz -- DrawTransPic is obsolete x += 24; ptr++; } } //============================================================================= int fragsort[MAX_SCOREBOARD]; char scoreboardtext[MAX_SCOREBOARD][20]; int scoreboardtop[MAX_SCOREBOARD]; int scoreboardbottom[MAX_SCOREBOARD]; int scoreboardcount[MAX_SCOREBOARD]; int scoreboardlines; /* =============== Sbar_SortFrags =============== */ void Sbar_SortFrags (void) { int i, j, k; // sort by frags scoreboardlines = 0; for (i = 0; i < cl.maxclients; i++) { if (cl.scores[i].name[0]) { fragsort[scoreboardlines] = i; scoreboardlines++; } } for (i = 0; i < scoreboardlines; i++) { for (j = 0; j < scoreboardlines - 1 - i; j++) { if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags) { k = fragsort[j]; fragsort[j] = fragsort[j+1]; fragsort[j+1] = k; } } } } int Sbar_ColorForMap (int m) { return m < 128 ? m + 8 : m + 8; } /* =============== Sbar_UpdateScoreboard =============== */ void Sbar_UpdateScoreboard (void) { int i, k; int top, bottom; scoreboard_t *s; Sbar_SortFrags (); // draw the text memset (scoreboardtext, 0, sizeof(scoreboardtext)); for (i = 0; i < scoreboardlines; i++) { k = fragsort[i]; s = &cl.scores[k]; sprintf (&scoreboardtext[i][1], "%3i %s", s->frags, s->name); top = s->colors & 0xf0; bottom = (s->colors & 15) <<4; scoreboardtop[i] = Sbar_ColorForMap (top); scoreboardbottom[i] = Sbar_ColorForMap (bottom); } } /* =============== Sbar_SoloScoreboard -- johnfitz -- new layout =============== */ void Sbar_SoloScoreboard (void) { char str[256]; int minutes, seconds, tens, units; int len; sprintf (str,"Kills: %i/%i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); Sbar_DrawString (8, 12, str); sprintf (str,"Secrets: %i/%i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]); Sbar_DrawString (312 - strlen(str)*8, 12, str); if (!fitzmode) { /* QuakeSpasm customization: */ q_snprintf (str, sizeof(str), "skill %i", (int)(skill.value + 0.5)); Sbar_DrawString (160 - strlen(str)*4, 12, str); q_snprintf (str, sizeof(str), "%s (%s)", cl.levelname, cl.mapname); len = strlen (str); if (len > 40) Sbar_DrawScrollString (0, 4, 320, str); else Sbar_DrawString (160 - len*4, 4, str); return; } minutes = cl.time / 60; seconds = cl.time - 60*minutes; tens = seconds / 10; units = seconds - 10*tens; sprintf (str,"%i:%i%i", minutes, tens, units); Sbar_DrawString (160 - strlen(str)*4, 12, str); len = strlen (cl.levelname); if (len > 40) Sbar_DrawScrollString (0, 4, 320, cl.levelname); else Sbar_DrawString (160 - len*4, 4, cl.levelname); } /* =============== Sbar_DrawScoreboard =============== */ void Sbar_DrawScoreboard (void) { Sbar_SoloScoreboard (); if (cl.gametype == GAME_DEATHMATCH) Sbar_DeathmatchOverlay (); } //============================================================================= /* =============== Sbar_DrawInventory =============== */ void Sbar_DrawInventory (void) { int i, val; char num[6]; float time; int flashon; if (rogue) { if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) Sbar_DrawPicAlpha (0, -24, rsb_invbar[0], scr_sbaralpha.value); //johnfitz -- scr_sbaralpha else Sbar_DrawPicAlpha (0, -24, rsb_invbar[1], scr_sbaralpha.value); //johnfitz -- scr_sbaralpha } else { Sbar_DrawPicAlpha (0, -24, sb_ibar, scr_sbaralpha.value); //johnfitz -- scr_sbaralpha } // weapons for (i = 0; i < 7; i++) { if (cl.items & (IT_SHOTGUN<<i) ) { time = cl.item_gettime[i]; flashon = (int)((cl.time - time)*10); if (flashon >= 10) { if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) ) flashon = 1; else flashon = 0; } else flashon = (flashon%5) + 2; Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]); if (flashon > 1) sb_updates = 0; // force update to remove flash } } // MED 01/04/97 // hipnotic weapons if (hipnotic) { int grenadeflashing = 0; for (i = 0; i < 4; i++) { if (cl.items & (1<<hipweapons[i])) { time = cl.item_gettime[hipweapons[i]]; flashon = (int)((cl.time - time)*10); if (flashon >= 10) { if (cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])) flashon = 1; else flashon = 0; } else flashon = (flashon%5) + 2; // check grenade launcher if (i == 2) { if (cl.items & HIT_PROXIMITY_GUN) { if (flashon) { grenadeflashing = 1; Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]); } } } else if (i == 3) { if (cl.items & (IT_SHOTGUN<<4)) { if (flashon && !grenadeflashing) { Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]); } else if (!grenadeflashing) { Sbar_DrawPic (96, -16, hsb_weapons[0][3]); } } else Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]); } else Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]); if (flashon > 1) sb_updates = 0; // force update to remove flash } } } if (rogue) { // check for powered up weapon. if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) { for (i=0;i<5;i++) { if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i)) { Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]); } } } } // ammo counts for (i = 0; i < 4; i++) { val = cl.stats[STAT_SHELLS+i]; val = (val < 0)? 0 : q_min(999,val);//johnfitz -- cap displayed value to 999 sprintf (num, "%3i", val); if (num[0] != ' ') Sbar_DrawCharacter ( (6*i+1)*8 + 2, -24, 18 + num[0] - '0'); if (num[1] != ' ') Sbar_DrawCharacter ( (6*i+2)*8 + 2, -24, 18 + num[1] - '0'); if (num[2] != ' ') Sbar_DrawCharacter ( (6*i+3)*8 + 2, -24, 18 + num[2] - '0'); } flashon = 0; // items for (i = 0; i < 6; i++) { if (cl.items & (1<<(17+i))) { time = cl.item_gettime[17+i]; if (time && time > cl.time - 2 && flashon) { // flash frame sb_updates = 0; } else { //MED 01/04/97 changed keys if (!hipnotic || (i > 1)) { Sbar_DrawPic (192 + i*16, -16, sb_items[i]); } } if (time && time > cl.time - 2) sb_updates = 0; } } //MED 01/04/97 added hipnotic items // hipnotic items if (hipnotic) { for (i = 0; i < 2; i++) { if (cl.items & (1<<(24+i))) { time = cl.item_gettime[24+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; } else { Sbar_DrawPic (288 + i*16, -16, hsb_items[i]); } if (time && time > cl.time - 2) sb_updates = 0; } } } if (rogue) { // new rogue items for (i = 0; i < 2; i++) { if (cl.items & (1<<(29+i))) { time = cl.item_gettime[29+i]; if (time && time > cl.time - 2 && flashon) { // flash frame sb_updates = 0; } else { Sbar_DrawPic (288 + i*16, -16, rsb_items[i]); } if (time && time > cl.time - 2) sb_updates = 0; } } } else { // sigils for (i = 0; i < 4; i++) { if (cl.items & (1<<(28+i))) { time = cl.item_gettime[28+i]; if (time && time > cl.time - 2 && flashon) { // flash frame sb_updates = 0; } else Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]); if (time && time > cl.time - 2) sb_updates = 0; } } } } //============================================================================= /* =============== Sbar_DrawFrags -- johnfitz -- heavy revision =============== */ void Sbar_DrawFrags (void) { int numscores, i, x, color; char num[12]; scoreboard_t *s; Sbar_SortFrags (); // draw the text numscores = q_min(scoreboardlines, 4); for (i = 0, x = 184; i<numscores; i++, x += 32) { s = &cl.scores[fragsort[i]]; if (!s->name[0]) continue; // top color color = s->colors & 0xf0; color = Sbar_ColorForMap (color); Draw_Fill (x + 10, 1, 28, 4, color, 1); // bottom color color = (s->colors & 15)<<4; color = Sbar_ColorForMap (color); Draw_Fill (x + 10, 5, 28, 3, color, 1); // number sprintf (num, "%3i", s->frags); Sbar_DrawCharacter (x + 12, -24, num[0]); Sbar_DrawCharacter (x + 20, -24, num[1]); Sbar_DrawCharacter (x + 28, -24, num[2]); // brackets if (fragsort[i] == cl.viewentity - 1) { Sbar_DrawCharacter (x + 6, -24, 16); Sbar_DrawCharacter (x + 32, -24, 17); } } } //============================================================================= /* =============== Sbar_DrawFace =============== */ void Sbar_DrawFace (void) { int f, anim; // PGM 01/19/97 - team color drawing // PGM 03/02/97 - fixed so color swatch only appears in CTF modes if (rogue && (cl.maxclients != 1) && (teamplay.value>3) && (teamplay.value<7)) { int top, bottom; int xofs; char num[12]; scoreboard_t *s; s = &cl.scores[cl.viewentity - 1]; // draw background top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); if (cl.gametype == GAME_DEATHMATCH) xofs = 113; else xofs = ((vid.width - 320)>>1) + 113; Sbar_DrawPic (112, 0, rsb_teambord); Draw_Fill (xofs, /*vid.height-*/24+3, 22, 9, top, 1); //johnfitz -- sbar coords are now relative Draw_Fill (xofs, /*vid.height-*/24+12, 22, 9, bottom, 1); //johnfitz -- sbar coords are now relative // draw number f = s->frags; sprintf (num, "%3i",f); if (top == 8) { if (num[0] != ' ') Sbar_DrawCharacter(113, 3, 18 + num[0] - '0'); if (num[1] != ' ') Sbar_DrawCharacter(120, 3, 18 + num[1] - '0'); if (num[2] != ' ') Sbar_DrawCharacter(127, 3, 18 + num[2] - '0'); } else { Sbar_DrawCharacter (113, 3, num[0]); Sbar_DrawCharacter (120, 3, num[1]); Sbar_DrawCharacter (127, 3, num[2]); } return; } // PGM 01/19/97 - team color drawing if ((cl.items & (IT_INVISIBILITY | IT_INVULNERABILITY)) == (IT_INVISIBILITY | IT_INVULNERABILITY)) { Sbar_DrawPic (112, 0, sb_face_invis_invuln); return; } if (cl.items & IT_QUAD) { Sbar_DrawPic (112, 0, sb_face_quad ); return; } if (cl.items & IT_INVISIBILITY) { Sbar_DrawPic (112, 0, sb_face_invis ); return; } if (cl.items & IT_INVULNERABILITY) { Sbar_DrawPic (112, 0, sb_face_invuln); return; } if (cl.stats[STAT_HEALTH] >= 100) f = 4; else f = cl.stats[STAT_HEALTH] / 20; if (f < 0) // in case we ever decide to draw when health <= 0 f = 0; if (cl.time <= cl.faceanimtime) { anim = 1; sb_updates = 0; // make sure the anim gets drawn over } else anim = 0; Sbar_DrawPic (112, 0, sb_faces[f][anim]); } /* =============== Sbar_Draw =============== */ void Sbar_Draw (void) { float w; //johnfitz if (scr_con_current == vid.height) return; // console is full screen if (cl.intermission) return; //johnfitz -- never draw sbar during intermission if (sb_updates >= vid.numpages && !gl_clear.value && scr_sbaralpha.value >= 1 //johnfitz -- gl_clear, scr_sbaralpha && !(gl_glsl_gamma_able && vid_gamma.value != 1)) //ericw -- must draw sbar every frame if doing glsl gamma return; sb_updates++; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz //johnfitz -- don't waste fillrate by clearing the area behind the sbar w = CLAMP (320.0f, scr_sbarscale.value * 320.0f, (float)glwidth); if (sb_lines && glwidth > w) { if (scr_sbaralpha.value < 1) Draw_TileClear (0, glheight - sb_lines, glwidth, sb_lines); if (cl.gametype == GAME_DEATHMATCH) Draw_TileClear (w, glheight - sb_lines, glwidth - w, sb_lines); else { Draw_TileClear (0, glheight - sb_lines, (glwidth - w) / 2.0f, sb_lines); Draw_TileClear ((glwidth - w) / 2.0f + w, glheight - sb_lines, (glwidth - w) / 2.0f, sb_lines); } } //johnfitz GL_SetCanvas (CANVAS_SBAR); //johnfitz if (scr_viewsize.value < 110) //johnfitz -- check viewsize instead of sb_lines { Sbar_DrawInventory (); if (cl.maxclients != 1) Sbar_DrawFrags (); } if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { Sbar_DrawPicAlpha (0, 0, sb_scorebar, scr_sbaralpha.value); //johnfitz -- scr_sbaralpha Sbar_DrawScoreboard (); sb_updates = 0; } else if (scr_viewsize.value < 120) //johnfitz -- check viewsize instead of sb_lines { Sbar_DrawPicAlpha (0, 0, sb_sbar, scr_sbaralpha.value); //johnfitz -- scr_sbaralpha // keys (hipnotic only) //MED 01/04/97 moved keys here so they would not be overwritten if (hipnotic) { if (cl.items & IT_KEY1) Sbar_DrawPic (209, 3, sb_items[0]); if (cl.items & IT_KEY2) Sbar_DrawPic (209, 12, sb_items[1]); } // armor if (cl.items & IT_INVULNERABILITY) { Sbar_DrawNum (24, 0, 666, 3, 1); Sbar_DrawPic (0, 0, draw_disc); } else { if (rogue) { Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); if (cl.items & RIT_ARMOR3) Sbar_DrawPic (0, 0, sb_armor[2]); else if (cl.items & RIT_ARMOR2) Sbar_DrawPic (0, 0, sb_armor[1]); else if (cl.items & RIT_ARMOR1) Sbar_DrawPic (0, 0, sb_armor[0]); } else { Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3 , cl.stats[STAT_ARMOR] <= 25); if (cl.items & IT_ARMOR3) Sbar_DrawPic (0, 0, sb_armor[2]); else if (cl.items & IT_ARMOR2) Sbar_DrawPic (0, 0, sb_armor[1]); else if (cl.items & IT_ARMOR1) Sbar_DrawPic (0, 0, sb_armor[0]); } } // face Sbar_DrawFace (); // health Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3 , cl.stats[STAT_HEALTH] <= 25); // ammo icon if (rogue) { if (cl.items & RIT_SHELLS) Sbar_DrawPic (224, 0, sb_ammo[0]); else if (cl.items & RIT_NAILS) Sbar_DrawPic (224, 0, sb_ammo[1]); else if (cl.items & RIT_ROCKETS) Sbar_DrawPic (224, 0, sb_ammo[2]); else if (cl.items & RIT_CELLS) Sbar_DrawPic (224, 0, sb_ammo[3]); else if (cl.items & RIT_LAVA_NAILS) Sbar_DrawPic (224, 0, rsb_ammo[0]); else if (cl.items & RIT_PLASMA_AMMO) Sbar_DrawPic (224, 0, rsb_ammo[1]); else if (cl.items & RIT_MULTI_ROCKETS) Sbar_DrawPic (224, 0, rsb_ammo[2]); } else { if (cl.items & IT_SHELLS) Sbar_DrawPic (224, 0, sb_ammo[0]); else if (cl.items & IT_NAILS) Sbar_DrawPic (224, 0, sb_ammo[1]); else if (cl.items & IT_ROCKETS) Sbar_DrawPic (224, 0, sb_ammo[2]); else if (cl.items & IT_CELLS) Sbar_DrawPic (224, 0, sb_ammo[3]); } Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); } //johnfitz -- removed the vid.width > 320 check here if (cl.gametype == GAME_DEATHMATCH) Sbar_MiniDeathmatchOverlay (); } //============================================================================= /* ================== Sbar_IntermissionNumber ================== */ void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color) { char str[12]; char *ptr; int l, frame; l = Sbar_itoa (num, str); ptr = str; if (l > digits) ptr += (l-digits); if (l < digits) x += (digits-l)*24; while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Draw_Pic (x,y,sb_nums[color][frame]); //johnfitz -- stretched menus x += 24; ptr++; } } /* ================== Sbar_DeathmatchOverlay ================== */ void Sbar_DeathmatchOverlay (void) { qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[12]; scoreboard_t *s; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/ranking.lmp"); M_DrawPic ((320-pic->width)/2, 8, pic); // scores Sbar_SortFrags (); // draw the text l = scoreboardlines; x = 80; //johnfitz -- simplified becuase some positioning is handled elsewhere y = 40; for (i = 0; i < l; i++) { k = fragsort[i]; s = &cl.scores[k]; if (!s->name[0]) continue; // draw background top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); Draw_Fill ( x, y, 40, 4, top, 1); //johnfitz -- stretched overlays Draw_Fill ( x, y+4, 40, 4, bottom, 1); //johnfitz -- stretched overlays // draw number f = s->frags; sprintf (num, "%3i",f); Draw_Character ( x+8 , y, num[0]); //johnfitz -- stretched overlays Draw_Character ( x+16 , y, num[1]); //johnfitz -- stretched overlays Draw_Character ( x+24 , y, num[2]); //johnfitz -- stretched overlays if (k == cl.viewentity - 1) Draw_Character ( x - 8, y, 12); //johnfitz -- stretched overlays #if 0 { int total; int n, minutes, tens, units; // draw time total = cl.completed_time - s->entertime; minutes = (int)total/60; n = total - minutes*60; tens = n/10; units = n%10; sprintf (num, "%3i:%i%i", minutes, tens, units); M_Print ( x+48 , y, num); //johnfitz -- was Draw_String, changed for stretched overlays } #endif // draw name M_Print (x+64, y, s->name); //johnfitz -- was Draw_String, changed for stretched overlays y += 10; } GL_SetCanvas (CANVAS_SBAR); //johnfitz } /* ================== Sbar_MiniDeathmatchOverlay ================== */ void Sbar_MiniDeathmatchOverlay (void) { int i, k, top, bottom, x, y, f, numlines; char num[12]; float scale; //johnfitz scoreboard_t *s; scale = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); //johnfitz //MAX_SCOREBOARDNAME = 32, so total width for this overlay plus sbar is 632, but we can cut off some i guess if (glwidth/scale < 512 || scr_viewsize.value >= 120) //johnfitz -- test should consider scr_sbarscale return; // scores Sbar_SortFrags (); // draw the text numlines = (scr_viewsize.value >= 110) ? 3 : 6; //johnfitz //find us for (i = 0; i < scoreboardlines; i++) if (fragsort[i] == cl.viewentity - 1) break; if (i == scoreboardlines) // we're not there i = 0; else // figure out start i = i - numlines/2; if (i > scoreboardlines - numlines) i = scoreboardlines - numlines; if (i < 0) i = 0; x = 324; y = (scr_viewsize.value >= 110) ? 24 : 0; //johnfitz -- start at the right place for ( ; i < scoreboardlines && y <= 48; i++, y+=8) //johnfitz -- change y init, test, inc { k = fragsort[i]; s = &cl.scores[k]; if (!s->name[0]) continue; // colors top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); Draw_Fill (x, y+1, 40, 4, top, 1); Draw_Fill (x, y+5, 40, 3, bottom, 1); // number f = s->frags; sprintf (num, "%3i",f); Draw_Character (x+ 8, y, num[0]); Draw_Character (x+16, y, num[1]); Draw_Character (x+24, y, num[2]); // brackets if (k == cl.viewentity - 1) { Draw_Character (x, y, 16); Draw_Character (x+32, y, 17); } // name Draw_String (x+48, y, s->name); } } /* ================== Sbar_IntermissionOverlay ================== */ void Sbar_IntermissionOverlay (void) { qpic_t *pic; int dig; int num; if (cl.gametype == GAME_DEATHMATCH) { Sbar_DeathmatchOverlay (); return; } GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/complete.lmp"); Draw_Pic (64, 24, pic); pic = Draw_CachePic ("gfx/inter.lmp"); Draw_Pic (0, 56, pic); dig = cl.completed_time/60; Sbar_IntermissionNumber (152, 64, dig, 3, 0); //johnfitz -- was 160 num = cl.completed_time - dig*60; Draw_Pic (224,64,sb_colon); //johnfitz -- was 234 Draw_Pic (240,64,sb_nums[0][num/10]); //johnfitz -- was 246 Draw_Pic (264,64,sb_nums[0][num%10]); //johnfitz -- was 266 Sbar_IntermissionNumber (152, 104, cl.stats[STAT_SECRETS], 3, 0); //johnfitz -- was 160 Draw_Pic (224,104,sb_slash); //johnfitz -- was 232 Sbar_IntermissionNumber (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); //johnfitz -- was 248 Sbar_IntermissionNumber (152, 144, cl.stats[STAT_MONSTERS], 3, 0); //johnfitz -- was 160 Draw_Pic (224,144,sb_slash); //johnfitz -- was 232 Sbar_IntermissionNumber (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); //johnfitz -- was 248 } /* ================== Sbar_FinaleOverlay ================== */ void Sbar_FinaleOverlay (void) { qpic_t *pic; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/finale.lmp"); Draw_Pic ( (320 - pic->width)/2, 16, pic); //johnfitz -- stretched menus } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_model.c������������������������������������������������������������������0000644�0000000�0000000�00000220126�13203233562�015451� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // models.c -- model loading and caching // models are the only shared resource between a client and server running // on the same machine. #include "quakedef.h" qmodel_t *loadmodel; char loadname[32]; // for hunk tags void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer); void Mod_LoadBrushModel (qmodel_t *mod, void *buffer); void Mod_LoadAliasModel (qmodel_t *mod, void *buffer); qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash); cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; static byte *mod_novis; static int mod_novis_capacity; static byte *mod_decompressed; static int mod_decompressed_capacity; #define MAX_MOD_KNOWN 2048 /*johnfitz -- was 512 */ qmodel_t mod_known[MAX_MOD_KNOWN]; int mod_numknown; texture_t *r_notexture_mip; //johnfitz -- moved here from r_main.c texture_t *r_notexture_mip2; //johnfitz -- used for non-lightmapped surfs with a missing texture /* =============== Mod_Init =============== */ void Mod_Init (void) { Cvar_RegisterVariable (&gl_subdivide_size); Cvar_RegisterVariable (&external_ents); //johnfitz -- create notexture miptex r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip"); strcpy (r_notexture_mip->name, "notexture"); r_notexture_mip->height = r_notexture_mip->width = 32; r_notexture_mip2 = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip2"); strcpy (r_notexture_mip2->name, "notexture2"); r_notexture_mip2->height = r_notexture_mip2->width = 32; //johnfitz } /* =============== Mod_Extradata Caches the data if needed =============== */ void *Mod_Extradata (qmodel_t *mod) { void *r; r = Cache_Check (&mod->cache); if (r) return r; Mod_LoadModel (mod, true); if (!mod->cache.data) Sys_Error ("Mod_Extradata: caching failed"); return mod->cache.data; } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model) { mnode_t *node; float d; mplane_t *plane; if (!model || !model->nodes) Sys_Error ("Mod_PointInLeaf: bad model"); node = model->nodes; while (1) { if (node->contents < 0) return (mleaf_t *)node; plane = node->plane; d = DotProduct (p,plane->normal) - plane->dist; if (d > 0) node = node->children[0]; else node = node->children[1]; } return NULL; // never reached } /* =================== Mod_DecompressVis =================== */ byte *Mod_DecompressVis (byte *in, qmodel_t *model) { int c; byte *out; int row; row = (model->numleafs+7)>>3; if (mod_decompressed == NULL || row > mod_decompressed_capacity) { mod_decompressed_capacity = row; mod_decompressed = (byte *) realloc (mod_decompressed, mod_decompressed_capacity); if (!mod_decompressed) Sys_Error ("Mod_DecompressVis: realloc() failed on %d bytes", mod_decompressed_capacity); } out = mod_decompressed; #if 0 memcpy (out, in, row); #else if (!in) { // no vis info, so make all visible while (row) { *out++ = 0xff; row--; } return mod_decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - mod_decompressed < row); #endif return mod_decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model) { if (leaf == model->leafs) return Mod_NoVisPVS (model); return Mod_DecompressVis (leaf->compressed_vis, model); } byte *Mod_NoVisPVS (qmodel_t *model) { int pvsbytes; pvsbytes = (model->numleafs+7)>>3; if (mod_novis == NULL || pvsbytes > mod_novis_capacity) { mod_novis_capacity = pvsbytes; mod_novis = (byte *) realloc (mod_novis, mod_novis_capacity); if (!mod_novis) Sys_Error ("Mod_NoVisPVS: realloc() failed on %d bytes", mod_novis_capacity); memset(mod_novis, 0xff, mod_novis_capacity); } return mod_novis; } /* =================== Mod_ClearAll =================== */ void Mod_ClearAll (void) { int i; qmodel_t *mod; for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) if (mod->type != mod_alias) { mod->needload = true; TexMgr_FreeTexturesForOwner (mod); //johnfitz } } void Mod_ResetAll (void) { int i; qmodel_t *mod; //ericw -- free alias model VBOs GLMesh_DeleteVertexBuffers (); for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) { if (!mod->needload) //otherwise Mod_ClearAll() did it already TexMgr_FreeTexturesForOwner (mod); memset(mod, 0, sizeof(qmodel_t)); } mod_numknown = 0; } /* ================== Mod_FindName ================== */ qmodel_t *Mod_FindName (const char *name) { int i; qmodel_t *mod; if (!name[0]) Sys_Error ("Mod_FindName: NULL name"); //johnfitz -- was "Mod_ForName" // // search the currently loaded models // for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) if (!strcmp (mod->name, name) ) break; if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); q_strlcpy (mod->name, name, MAX_QPATH); mod->needload = true; mod_numknown++; } return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (const char *name) { qmodel_t *mod; mod = Mod_FindName (name); if (!mod->needload) { if (mod->type == mod_alias) Cache_Check (&mod->cache); } } /* ================== Mod_LoadModel Loads a model into the cache ================== */ qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) { byte *buf; byte stackbuf[1024]; // avoid dirtying the cache heap int mod_type; if (!mod->needload) { if (mod->type == mod_alias) { if (Cache_Check (&mod->cache)) return mod; } else return mod; // not cached at all } // // because the world is so huge, load it one piece at a time // if (!crash) { } // // load the file // buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id); if (!buf) { if (crash) Host_Error ("Mod_LoadModel: %s not found", mod->name); //johnfitz -- was "Mod_NumForName" return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname, sizeof(loadname)); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = false; mod_type = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); switch (mod_type) { case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf); break; } return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ qmodel_t *Mod_ForName (const char *name, qboolean crash) { qmodel_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ byte *mod_base; /* ================= Mod_CheckFullbrights -- johnfitz ================= */ qboolean Mod_CheckFullbrights (byte *pixels, int count) { int i; for (i = 0; i < count; i++) if (*pixels++ > 223) return true; return false; } /* ================= Mod_LoadTextures ================= */ void Mod_LoadTextures (lump_t *l) { int i, j, pixels, num, maxanim, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; //johnfitz -- more variables char texturename[64]; int nummiptex; src_offset_t offset; int mark, fwidth, fheight; char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH]; byte *data; extern byte *hunk_base; //johnfitz //johnfitz -- don't return early if no textures; still need to create dummy texture if (!l->filelen) { Con_Printf ("Mod_LoadTextures: no textures in bsp file\n"); nummiptex = 0; m = NULL; // avoid bogus compiler warning } else { m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); nummiptex = m->nummiptex; } //johnfitz loadmodel->numtextures = nummiptex + 2; //johnfitz -- need 2 dummy texture chains for missing textures loadmodel->textures = (texture_t **) Hunk_AllocName (loadmodel->numtextures * sizeof(*loadmodel->textures) , loadname); for (i=0 ; i<nummiptex ; i++) { m->dataofs[i] = LittleLong(m->dataofs[i]); if (m->dataofs[i] == -1) continue; mt = (miptex_t *)((byte *)m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); for (j=0 ; j<MIPLEVELS ; j++) mt->offsets[j] = LittleLong (mt->offsets[j]); if ( (mt->width & 15) || (mt->height & 15) ) Sys_Error ("Texture %s is not 16 aligned", mt->name); pixels = mt->width*mt->height/64*85; tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); loadmodel->textures[i] = tx; memcpy (tx->name, mt->name, sizeof(tx->name)); tx->width = mt->width; tx->height = mt->height; for (j=0 ; j<MIPLEVELS ; j++) tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); // the pixels immediately follow the structures // ericw -- check for pixels extending past the end of the lump. // appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2), // kellbase1.bsp (quoth), and can lead to a segfault if we read past // the end of the .bsp file buffer if (((byte*)(mt+1) + pixels) > (mod_base + l->fileofs + l->filelen)) { Con_DPrintf("Texture %s extends past end of lump\n", mt->name); pixels = q_max(0, (mod_base + l->fileofs + l->filelen) - (byte*)(mt+1)); } memcpy ( tx+1, mt+1, pixels); tx->update_warp = false; //johnfitz tx->warpimage = NULL; //johnfitz tx->fullbright = NULL; //johnfitz //johnfitz -- lots of changes if (!isDedicated) //no texture uploading for dedicated server { if (!q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp Sky_LoadTexture (tx); else if (tx->name[0] == '*') //warping texture { //external textures -- first look in "textures/mapname/" then look in "textures/" mark = Hunk_LowMark(); COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname)); q_snprintf (filename, sizeof(filename), "textures/%s/#%s", mapname, tx->name+1); //this also replaces the '*' with a '#' data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { q_snprintf (filename, sizeof(filename), "textures/#%s", tx->name+1); data = Image_LoadImage (filename, &fwidth, &fheight); } //now load whatever we found if (data) //load external image { q_strlcpy (texturename, filename, sizeof(texturename)); tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_NONE); } else //use the texture from the bsp file { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_NONE); } //now create the warpimage, using dummy data from the hunk to create the initial image Hunk_Alloc (gl_warpimagesize*gl_warpimagesize*4); //make sure hunk is big enough so we don't reach an illegal address Hunk_FreeToLowMark (mark); q_snprintf (texturename, sizeof(texturename), "%s_warp", texturename); tx->warpimage = TexMgr_LoadImage (loadmodel, texturename, gl_warpimagesize, gl_warpimagesize, SRC_RGBA, hunk_base, "", (src_offset_t)hunk_base, TEXPREF_NOPICMIP | TEXPREF_WARPIMAGE); tx->update_warp = true; } else //regular texture { // ericw -- fence textures int extraflags; extraflags = 0; if (tx->name[0] == '{') extraflags |= TEXPREF_ALPHA; // ericw //external textures -- first look in "textures/mapname/" then look in "textures/" mark = Hunk_LowMark (); COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname)); q_snprintf (filename, sizeof(filename), "textures/%s/%s", mapname, tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { q_snprintf (filename, sizeof(filename), "textures/%s", tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); } //now load whatever we found if (data) //load external image { tx->gltexture = TexMgr_LoadImage (loadmodel, filename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags ); //now try to load glow/luma image from the same place Hunk_FreeToLowMark (mark); q_snprintf (filename2, sizeof(filename2), "%s_glow", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); if (!data) { q_snprintf (filename2, sizeof(filename2), "%s_luma", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); } if (data) tx->fullbright = TexMgr_LoadImage (loadmodel, filename2, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags ); } else //use the texture from the bsp file { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; if (Mod_CheckFullbrights ((byte *)(tx+1), pixels)) { tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_NOBRIGHT | extraflags); q_snprintf (texturename, sizeof(texturename), "%s:%s_glow", loadmodel->name, tx->name); tx->fullbright = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_FULLBRIGHT | extraflags); } else { tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | extraflags); } } Hunk_FreeToLowMark (mark); } } //johnfitz } //johnfitz -- last 2 slots in array should be filled with dummy textures loadmodel->textures[loadmodel->numtextures-2] = r_notexture_mip; //for lightmapped surfs loadmodel->textures[loadmodel->numtextures-1] = r_notexture_mip2; //for SURF_DRAWTILED surfs // // sequence the animations // for (i=0 ; i<nummiptex ; i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // allready sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); maxanim = tx->name[1]; altmax = 0; if (maxanim >= 'a' && maxanim <= 'z') maxanim -= 'a' - 'A'; if (maxanim >= '0' && maxanim <= '9') { maxanim -= '0'; altmax = 0; anims[maxanim] = tx; maxanim++; } else if (maxanim >= 'A' && maxanim <= 'J') { altmax = maxanim - 'A'; maxanim = 0; altanims[altmax] = tx; altmax++; } else Sys_Error ("Bad animating texture %s", tx->name); for (j=i+1 ; j<nummiptex ; j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp (tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; if (num >= 'a' && num <= 'z') num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num+1 > maxanim) maxanim = num + 1; } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else Sys_Error ("Bad animating texture %s", tx->name); } #define ANIM_CYCLE 2 // link them all together for (j=0 ; j<maxanim ; j++) { tx2 = anims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = maxanim * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[ (j+1)%maxanim ]; if (altmax) tx2->alternate_anims = altanims[0]; } for (j=0 ; j<altmax ; j++) { tx2 = altanims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = altmax * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = altanims[ (j+1)%altmax ]; if (maxanim) tx2->alternate_anims = anims[0]; } } } /* ================= Mod_LoadLighting -- johnfitz -- replaced with lit support code via lordhavoc ================= */ void Mod_LoadLighting (lump_t *l) { int i, mark; byte *in, *out, *data; byte d; char litfilename[MAX_OSPATH]; unsigned int path_id; loadmodel->lightdata = NULL; // LordHavoc: check for a .lit file q_strlcpy(litfilename, loadmodel->name, sizeof(litfilename)); COM_StripExtension(litfilename, litfilename, sizeof(litfilename)); q_strlcat(litfilename, ".lit", sizeof(litfilename)); mark = Hunk_LowMark(); data = (byte*) COM_LoadHunkFile (litfilename, &path_id); if (data) { // use lit file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", litfilename); } else if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { i = LittleLong(((int *)data)[1]); if (i == 1) { Con_DPrintf2("%s loaded\n", litfilename); loadmodel->lightdata = data + 8; return; } else { Hunk_FreeToLowMark(mark); Con_Printf("Unknown .lit file version (%d)\n", i); } } else { Hunk_FreeToLowMark(mark); Con_Printf("Corrupt .lit file (old version?), ignoring\n"); } } // LordHavoc: no .lit found, expand the white lighting data to color if (!l->filelen) return; loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen*3, litfilename); in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); for (i = 0;i < l->filelen;i++) { d = *in++; *out++ = d; *out++ = d; *out++ = d; } } /* ================= Mod_LoadVisibility ================= */ void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ void Mod_LoadEntities (lump_t *l) { char entfilename[MAX_QPATH]; char *ents; int mark; unsigned int path_id; if (! external_ents.value) goto _load_embedded; q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename)); COM_StripExtension(entfilename, entfilename, sizeof(entfilename)); q_strlcat(entfilename, ".ent", sizeof(entfilename)); Con_DPrintf2("trying to load %s\n", entfilename); mark = Hunk_LowMark(); ents = (char *) COM_LoadHunkFile (entfilename, &path_id); if (ents) { // use ent file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", entfilename); } else { loadmodel->entities = ents; Con_DPrintf("Loaded external entity file %s\n", entfilename); return; } } _load_embedded: if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadVertexes ================= */ void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->vertexes = out; loadmodel->numvertexes = count; for (i=0 ; i<count ; i++, in++, out++) { out->position[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadEdges ================= */ void Mod_LoadEdges (lump_t *l, int bsp2) { medge_t *out; int i, count; if (bsp2) { dledge_t *in = (dledge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); loadmodel->edges = out; loadmodel->numedges = count; for (i=0 ; i<count ; i++, in++, out++) { out->v[0] = LittleLong(in->v[0]); out->v[1] = LittleLong(in->v[1]); } } else { dsedge_t *in = (dsedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); loadmodel->edges = out; loadmodel->numedges = count; for (i=0 ; i<count ; i++, in++, out++) { out->v[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } } /* ================= Mod_LoadTexinfo ================= */ void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count, miptex; float len1, len2; int missing = 0; //johnfitz in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<4 ; j++) { out->vecs[0][j] = LittleFloat (in->vecs[0][j]); out->vecs[1][j] = LittleFloat (in->vecs[1][j]); } len1 = VectorLength (out->vecs[0]); len2 = VectorLength (out->vecs[1]); len1 = (len1 + len2)/2; if (len1 < 0.32) out->mipadjust = 4; else if (len1 < 0.49) out->mipadjust = 3; else if (len1 < 0.99) out->mipadjust = 2; else out->mipadjust = 1; #if 0 if (len1 + len2 < 0.001) out->mipadjust = 1; // don't crash else out->mipadjust = 1 / floor((len1+len2)/2 + 0.1); #endif miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); //johnfitz -- rewrote this section if (miptex >= loadmodel->numtextures-1 || !loadmodel->textures[miptex]) { if (out->flags & TEX_SPECIAL) out->texture = loadmodel->textures[loadmodel->numtextures-1]; else out->texture = loadmodel->textures[loadmodel->numtextures-2]; out->flags |= TEX_MISSING; missing++; } else { out->texture = loadmodel->textures[miptex]; } //johnfitz } //johnfitz: report missing textures if (missing && loadmodel->numtextures > 1) Con_Printf ("Mod_LoadTexinfo: %d texture(s) missing from BSP file\n", missing); //johnfitz } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i,j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; tex = s->texinfo; for (i=0 ; i<s->numedges ; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for (j=0 ; j<2 ; j++) { /* The following calculation is sensitive to floating-point * precision. It needs to produce the same result that the * light compiler does, because R_BuildLightMap uses surf-> * extents to know the width/height of a surface's lightmap, * and incorrect rounding here manifests itself as patches * of "corrupted" looking lightmaps. * Most light compilers are win32 executables, so they use * x87 floating point. This means the multiplies and adds * are done at 80-bit precision, and the result is rounded * down to 32-bits and stored in val. * Adding the casts to double seems to be good enough to fix * lighting glitches when Quakespasm is compiled as x86_64 * and using SSE2 floating-point. A potential trouble spot * is the hallway at the beginning of mfxsp17. -- ericw */ val = ((double)v->position[0] * (double)tex->vecs[j][0]) + ((double)v->position[1] * (double)tex->vecs[j][1]) + ((double)v->position[2] * (double)tex->vecs[j][2]) + (double)tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i=0 ; i<2 ; i++) { bmins[i] = floor(mins[i]/16); bmaxs[i] = ceil(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 2000) //johnfitz -- was 512 in glquake, 256 in winquake Sys_Error ("Bad surface extents"); } } /* ================ Mod_PolyForUnlitSurface -- johnfitz -- creates polys for unlightmapped surfaces (sky and water) TODO: merge this into BuildSurfaceDisplayList? ================ */ void Mod_PolyForUnlitSurface (msurface_t *fa) { vec3_t verts[64]; int numverts, i, lindex; float *vec; glpoly_t *poly; float texscale; if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) texscale = (1.0/128.0); //warp animation repeats every 128 else texscale = (1.0/32.0); //to match r_notexture_mip // convert edges back to a normal polygon numverts = 0; for (i=0 ; i<fa->numedges ; i++) { lindex = loadmodel->surfedges[fa->firstedge + i]; if (lindex > 0) vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; else vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; VectorCopy (vec, verts[numverts]); numverts++; } //create the poly poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); poly->next = NULL; fa->polys = poly; poly->numverts = numverts; for (i=0, vec=(float *)verts; i<numverts; i++, vec+= 3) { VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = DotProduct(vec, fa->texinfo->vecs[0]) * texscale; poly->verts[i][4] = DotProduct(vec, fa->texinfo->vecs[1]) * texscale; } } /* ================= Mod_CalcSurfaceBounds -- johnfitz -- calculate bounding box for per-surface frustum culling ================= */ void Mod_CalcSurfaceBounds (msurface_t *s) { int i, e; mvertex_t *v; s->mins[0] = s->mins[1] = s->mins[2] = 9999; s->maxs[0] = s->maxs[1] = s->maxs[2] = -9999; for (i=0 ; i<s->numedges ; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; if (s->mins[0] > v->position[0]) s->mins[0] = v->position[0]; if (s->mins[1] > v->position[1]) s->mins[1] = v->position[1]; if (s->mins[2] > v->position[2]) s->mins[2] = v->position[2]; if (s->maxs[0] < v->position[0]) s->maxs[0] = v->position[0]; if (s->maxs[1] < v->position[1]) s->maxs[1] = v->position[1]; if (s->maxs[2] < v->position[2]) s->maxs[2] = v->position[2]; } } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t *l, qboolean bsp2) { dsface_t *ins; dlface_t *inl; msurface_t *out; int i, count, surfnum, lofs; int planenum, side, texinfon; if (bsp2) { ins = NULL; inl = (dlface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*inl)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*inl); } else { ins = (dsface_t *)(mod_base + l->fileofs); inl = NULL; if (l->filelen % sizeof(*ins)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*ins); } out = (msurface_t *)Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn mappers about exceeding old limits if (count > 32767 && !bsp2) Con_DWarning ("%i faces exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum=0 ; surfnum<count ; surfnum++, out++) { if (bsp2) { out->firstedge = LittleLong(inl->firstedge); out->numedges = LittleLong(inl->numedges); planenum = LittleLong(inl->planenum); side = LittleLong(inl->side); texinfon = LittleLong (inl->texinfo); for (i=0 ; i<MAXLIGHTMAPS ; i++) out->styles[i] = inl->styles[i]; lofs = LittleLong(inl->lightofs); inl++; } else { out->firstedge = LittleLong(ins->firstedge); out->numedges = LittleShort(ins->numedges); planenum = LittleShort(ins->planenum); side = LittleShort(ins->side); texinfon = LittleShort (ins->texinfo); for (i=0 ; i<MAXLIGHTMAPS ; i++) out->styles[i] = ins->styles[i]; lofs = LittleLong(ins->lightofs); ins++; } out->flags = 0; if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + texinfon; CalcSurfaceExtents (out); Mod_CalcSurfaceBounds (out); //johnfitz -- for per-surface frustum culling // lighting info if (lofs == -1) out->samples = NULL; else out->samples = loadmodel->lightdata + (lofs * 3); //johnfitz -- lit support via lordhavoc (was "+ i") //johnfitz -- this section rewritten if (!q_strncasecmp(out->texinfo->texture->name,"sky",3)) // sky surface //also note -- was Q_strncmp, changed to match qbsp { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); Mod_PolyForUnlitSurface (out); //no more subdivision } else if (out->texinfo->texture->name[0] == '*') // warp surface { out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); // detect special liquid types if (!strncmp (out->texinfo->texture->name, "*lava", 5)) out->flags |= SURF_DRAWLAVA; else if (!strncmp (out->texinfo->texture->name, "*slime", 6)) out->flags |= SURF_DRAWSLIME; else if (!strncmp (out->texinfo->texture->name, "*tele", 5)) out->flags |= SURF_DRAWTELE; else out->flags |= SURF_DRAWWATER; Mod_PolyForUnlitSurface (out); GL_SubdivideSurface (out); } else if (out->texinfo->texture->name[0] == '{') // ericw -- fence textures { out->flags |= SURF_DRAWFENCE; } else if (out->texinfo->flags & TEX_MISSING) // texture is missing from bsp { if (out->samples) //lightmapped { out->flags |= SURF_NOTEXTURE; } else // not lightmapped { out->flags |= (SURF_NOTEXTURE | SURF_DRAWTILED); Mod_PolyForUnlitSurface (out); } } //johnfitz } } /* ================= Mod_SetParent ================= */ void Mod_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents < 0) return; Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_LoadNodes ================= */ void Mod_LoadNodes_S (lump_t *l) { int i, j, count, p; dsnode_t *in; mnode_t *out; in = (dsnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn mappers about exceeding old limits if (count > 32767) Con_DWarning ("%i nodes exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->nodes = out; loadmodel->numnodes = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = (unsigned short)LittleShort (in->firstface); //johnfitz -- explicit cast as unsigned short out->numsurfaces = (unsigned short)LittleShort (in->numfaces); //johnfitz -- explicit cast as unsigned short for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = (unsigned short)LittleShort(in->children[j]); if (p < count) out->children[j] = loadmodel->nodes + p; else { p = 65535 - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes_L1 (lump_t *l) { int i, j, count, p; dl1node_t *in; mnode_t *out; in = (dl1node_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->nodes = out; loadmodel->numnodes = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); //johnfitz -- explicit cast as unsigned short out->numsurfaces = LittleLong (in->numfaces); //johnfitz -- explicit cast as unsigned short for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = LittleLong(in->children[j]); if (p >= 0 && p < count) out->children[j] = loadmodel->nodes + p; else { p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p >= 0 && p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes_L2 (lump_t *l) { int i, j, count, p; dl2node_t *in; mnode_t *out; in = (dl2node_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->nodes = out; loadmodel->numnodes = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); //johnfitz -- explicit cast as unsigned short out->numsurfaces = LittleLong (in->numfaces); //johnfitz -- explicit cast as unsigned short for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = LittleLong(in->children[j]); if (p > 0 && p < count) out->children[j] = loadmodel->nodes + p; else { p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p >= 0 && p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes (lump_t *l, int bsp2) { if (bsp2 == 2) Mod_LoadNodes_L2(l); else if (bsp2) Mod_LoadNodes_L1(l); else Mod_LoadNodes_S(l); Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } void Mod_ProcessLeafs_S (dsleaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz if (count > 32767) Host_Error ("Mod_LoadLeafs: %i leafs exceeds limit of 32767.\n", count); //johnfitz loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; //johnfitz -- removed code to mark surfaces as SURF_UNDERWATER } } void Mod_ProcessLeafs_L1 (dl1leaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; //johnfitz -- removed code to mark surfaces as SURF_UNDERWATER } } void Mod_ProcessLeafs_L2 (dl2leaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; //johnfitz -- removed code to mark surfaces as SURF_UNDERWATER } } /* ================= Mod_LoadLeafs ================= */ void Mod_LoadLeafs (lump_t *l, int bsp2) { void *in = (void *)(mod_base + l->fileofs); if (bsp2 == 2) Mod_ProcessLeafs_L2 ((dl2leaf_t *)in, l->filelen); else if (bsp2) Mod_ProcessLeafs_L1 ((dl1leaf_t *)in, l->filelen); else Mod_ProcessLeafs_S ((dsleaf_t *) in, l->filelen); } /* ================= Mod_LoadClipnodes ================= */ void Mod_LoadClipnodes (lump_t *l, qboolean bsp2) { dsclipnode_t *ins; dlclipnode_t *inl; mclipnode_t *out; //johnfitz -- was dclipnode_t int i, count; hull_t *hull; if (bsp2) { ins = NULL; inl = (dlclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*inl)) Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*inl); } else { ins = (dsclipnode_t *)(mod_base + l->fileofs); inl = NULL; if (l->filelen % sizeof(*ins)) Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*ins); } out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn about exceeding old limits if (count > 32767 && !bsp2) Con_DWarning ("%i clipnodes exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->clipnodes = out; loadmodel->numclipnodes = count; hull = &loadmodel->hulls[1]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; hull = &loadmodel->hulls[2]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -32; hull->clip_mins[1] = -32; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 32; hull->clip_maxs[1] = 32; hull->clip_maxs[2] = 64; if (bsp2) { for (i=0 ; i<count ; i++, out++, inl++) { out->planenum = LittleLong(inl->planenum); //johnfitz -- bounds check if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) Host_Error ("Mod_LoadClipnodes: planenum out of bounds"); //johnfitz out->children[0] = LittleLong(inl->children[0]); out->children[1] = LittleLong(inl->children[1]); //Spike: FIXME: bounds check } } else { for (i=0 ; i<count ; i++, out++, ins++) { out->planenum = LittleLong(ins->planenum); //johnfitz -- bounds check if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) Host_Error ("Mod_LoadClipnodes: planenum out of bounds"); //johnfitz //johnfitz -- support clipnodes > 32k out->children[0] = (unsigned short)LittleShort(ins->children[0]); out->children[1] = (unsigned short)LittleShort(ins->children[1]); if (out->children[0] >= count) out->children[0] -= 65536; if (out->children[1] >= count) out->children[1] -= 65536; //johnfitz } } } /* ================= Mod_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; //johnfitz -- was dclipnode_t int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i=0 ; i<count ; i++, out++, in++) { out->planenum = in->plane - loadmodel->planes; for (j=0 ; j<2 ; j++) { child = in->children[j]; if (child->contents < 0) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= Mod_LoadMarksurfaces ================= */ void Mod_LoadMarksurfaces (lump_t *l, int bsp2) { int i, j, count; msurface_t **out; if (bsp2) { unsigned int *in = (unsigned int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i=0 ; i<count ; i++) { j = LittleLong(in[i]); if (j >= loadmodel->numsurfaces) Host_Error ("Mod_LoadMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } else { short *in = (short *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; //johnfitz -- warn mappers about exceeding old limits if (count > 32767) Con_DWarning ("%i marksurfaces exceeds standard limit of 32767.\n", count); //johnfitz for (i=0 ; i<count ; i++) { j = (unsigned short)LittleShort(in[i]); //johnfitz -- explicit cast as unsigned short if (j >= loadmodel->numsurfaces) Sys_Error ("Mod_LoadMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } } /* ================= Mod_LoadSurfedges ================= */ void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (int *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for (i=0 ; i<count ; i++) out[i] = LittleLong (in[i]); } /* ================= Mod_LoadPlanes ================= */ void Mod_LoadPlanes (lump_t *l) { int i, j; mplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t *) Hunk_AllocName ( count*2*sizeof(*out), loadname); loadmodel->planes = out; loadmodel->numplanes = count; for (i=0 ; i<count ; i++, in++, out++) { bits = 0; for (j=0 ; j<3 ; j++) { out->normal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<<j; } out->dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= RadiusFromBounds ================= */ float RadiusFromBounds (vec3_t mins, vec3_t maxs) { int i; vec3_t corner; for (i=0 ; i<3 ; i++) { corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); } return VectorLength (corner); } /* ================= Mod_LoadSubmodels ================= */ void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->submodels = out; loadmodel->numsubmodels = count; for (i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<3 ; j++) { // spread the mins / maxs by a pixel out->mins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j=0 ; j<MAX_MAP_HULLS ; j++) out->headnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } // johnfitz -- check world visleafs -- adapted from bjp out = loadmodel->submodels; if (out->visleafs > 8192) Con_DWarning ("%i visleafs exceeds standard limit of 8192.\n", out->visleafs); //johnfitz } /* ================= Mod_BoundsFromClipNode -- johnfitz update the model's clipmins and clipmaxs based on each node's plane. This works because of the way brushes are expanded in hull generation. Each brush will include all six axial planes, which bound that brush. Therefore, the bounding box of the hull can be constructed entirely from axial planes found in the clipnodes for that hull. ================= */ void Mod_BoundsFromClipNode (qmodel_t *mod, int hull, int nodenum) { mplane_t *plane; mclipnode_t *node; if (nodenum < 0) return; //hit a leafnode node = &mod->clipnodes[nodenum]; plane = mod->hulls[hull].planes + node->planenum; switch (plane->type) { case PLANE_X: if (plane->signbits == 1) mod->clipmins[0] = q_min(mod->clipmins[0], -plane->dist - mod->hulls[hull].clip_mins[0]); else mod->clipmaxs[0] = q_max(mod->clipmaxs[0], plane->dist - mod->hulls[hull].clip_maxs[0]); break; case PLANE_Y: if (plane->signbits == 2) mod->clipmins[1] = q_min(mod->clipmins[1], -plane->dist - mod->hulls[hull].clip_mins[1]); else mod->clipmaxs[1] = q_max(mod->clipmaxs[1], plane->dist - mod->hulls[hull].clip_maxs[1]); break; case PLANE_Z: if (plane->signbits == 4) mod->clipmins[2] = q_min(mod->clipmins[2], -plane->dist - mod->hulls[hull].clip_mins[2]); else mod->clipmaxs[2] = q_max(mod->clipmaxs[2], plane->dist - mod->hulls[hull].clip_maxs[2]); break; default: //skip nonaxial planes; don't need them break; } Mod_BoundsFromClipNode (mod, hull, node->children[0]); Mod_BoundsFromClipNode (mod, hull, node->children[1]); } /* ================= Mod_LoadBrushModel ================= */ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer) { int i, j; int bsp2; dheader_t *header; dmodel_t *bm; float radius; //johnfitz loadmodel->type = mod_brush; header = (dheader_t *)buffer; mod->bspversion = LittleLong (header->version); switch(mod->bspversion) { case BSPVERSION: bsp2 = false; break; case BSP2VERSION_2PSB: bsp2 = 1; //first iteration break; case BSP2VERSION_BSP2: bsp2 = 2; //sanitised revision break; default: Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, mod->bspversion, BSPVERSION); break; } // swap all the lumps mod_base = (byte *)header; for (i = 0; i < (int) sizeof(dheader_t) / 4; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], bsp2); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2); Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // // johnfitz -- okay, so that i stop getting confused every time i look at this loop, here's how it works: // we're looping through the submodels starting at 0. Submodel 0 is the main model, so we don't have to // worry about clobbering data the first time through, since it's the same data. At the end of the loop, // we create a new copy of the data to use the next time through. for (i=0 ; i<mod->numsubmodels ; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j=1 ; j<MAX_MAP_HULLS ; j++) { mod->hulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); //johnfitz -- calculate rotate bounds and yaw bounds radius = RadiusFromBounds (mod->mins, mod->maxs); mod->rmaxs[0] = mod->rmaxs[1] = mod->rmaxs[2] = mod->ymaxs[0] = mod->ymaxs[1] = mod->ymaxs[2] = radius; mod->rmins[0] = mod->rmins[1] = mod->rmins[2] = mod->ymins[0] = mod->ymins[1] = mod->ymins[2] = -radius; //johnfitz //johnfitz -- correct physics cullboxes so that outlying clip brushes on doors and stuff are handled right if (i > 0 || strcmp(mod->name, sv.modelname) != 0) //skip submodel 0 of sv.worldmodel, which is the actual world { // start with the hull0 bounds VectorCopy (mod->maxs, mod->clipmaxs); VectorCopy (mod->mins, mod->clipmins); // process hull1 (we don't need to process hull2 becuase there's // no such thing as a brush that appears in hull2 but not hull1) //Mod_BoundsFromClipNode (mod, 1, mod->hulls[1].firstclipnode); // (disabled for now becuase it fucks up on rotating models) } //johnfitz mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; sprintf (name, "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } /* ============================================================================== ALIAS MODELS ============================================================================== */ aliashdr_t *pheader; stvert_t stverts[MAXALIASVERTS]; mtriangle_t triangles[MAXALIASTRIS]; // a pose is a single set of vertexes. a frame may be // an animating sequence of poses trivertx_t *poseverts[MAXALIASFRAMES]; int posenum; byte **player_8bit_texels_tbl; byte *player_8bit_texels; /* ================= Mod_LoadAliasFrame ================= */ void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) { trivertx_t *pinframe; int i; daliasframe_t *pdaliasframe; pdaliasframe = (daliasframe_t *)pin; strcpy (frame->name, pdaliasframe->name); frame->firstpose = posenum; frame->numposes = 1; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about // endianness frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; } pinframe = (trivertx_t *)(pdaliasframe + 1); poseverts[posenum] = pinframe; posenum++; pinframe += pheader->numverts; return (void *)pinframe; } /* ================= Mod_LoadAliasGroup ================= */ void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) { daliasgroup_t *pingroup; int i, numframes; daliasinterval_t *pin_intervals; void *ptemp; pingroup = (daliasgroup_t *)pin; numframes = LittleLong (pingroup->numframes); frame->firstpose = posenum; frame->numposes = numframes; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about endianness frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; } pin_intervals = (daliasinterval_t *)(pingroup + 1); frame->interval = LittleFloat (pin_intervals->interval); pin_intervals += numframes; ptemp = (void *)pin_intervals; for (i=0 ; i<numframes ; i++) { poseverts[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1); posenum++; ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts; } return ptemp; } //========================================================= /* ================= Mod_FloodFillSkin Fill background pixels so mipmapping doesn't have haloes - Ed ================= */ typedef struct { short x, y; } floodfill_t; // must be a power of 2 #define FLOODFILL_FIFO_SIZE 0x1000 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) #define FLOODFILL_STEP( off, dx, dy ) \ do { \ if (pos[off] == fillcolor) \ { \ pos[off] = 255; \ fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ } \ else if (pos[off] != 255) fdc = pos[off]; \ } while (0) void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) { byte fillcolor = *skin; // assume this is the pixel to fill floodfill_t fifo[FLOODFILL_FIFO_SIZE]; int inpt = 0, outpt = 0; int filledcolor = -1; int i; if (filledcolor == -1) { filledcolor = 0; // attempt to find opaque black for (i = 0; i < 256; ++i) if (d_8to24table[i] == (255 << 0)) // alpha 1.0 { filledcolor = i; break; } } // can't fill to filled color or to transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); return; } fifo[inpt].x = 0, fifo[inpt].y = 0; inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; while (outpt != inpt) { int x = fifo[outpt].x, y = fifo[outpt].y; int fdc = filledcolor; byte *pos = &skin[x + skinwidth * y]; outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); skin[x + skinwidth * y] = fdc; } } /* =============== Mod_LoadAllSkins =============== */ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) { int i, j, k, size, groupskins; char name[MAX_QPATH]; byte *skin, *texels; daliasskingroup_t *pinskingroup; daliasskininterval_t *pinskinintervals; char fbr_mask_name[MAX_QPATH]; //johnfitz -- added for fullbright support src_offset_t offset; //johnfitz unsigned int texflags = TEXPREF_PAD; skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); size = pheader->skinwidth * pheader->skinheight; if (loadmodel->flags & MF_HOLEY) texflags |= TEXPREF_ALPHA; for (i=0 ; i<numskins ; i++) { if (pskintype->type == ALIAS_SKIN_SINGLE) { Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); // save 8 bit texels for the player model to remap texels = (byte *) Hunk_AllocName(size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype + 1), size); //johnfitz -- rewritten q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, i); offset = (src_offset_t)(pskintype+1) - (src_offset_t)mod_base; if (Mod_CheckFullbrights ((byte *)(pskintype+1), size)) { pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags | TEXPREF_NOBRIGHT); q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_glow", loadmodel->name, i); pheader->fbtextures[i][0] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags | TEXPREF_FULLBRIGHT); } else { pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags); pheader->fbtextures[i][0] = NULL; } pheader->gltextures[i][3] = pheader->gltextures[i][2] = pheader->gltextures[i][1] = pheader->gltextures[i][0]; pheader->fbtextures[i][3] = pheader->fbtextures[i][2] = pheader->fbtextures[i][1] = pheader->fbtextures[i][0]; //johnfitz pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + size); } else { // animating skin group. yuck. pskintype++; pinskingroup = (daliasskingroup_t *)pskintype; groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); pskintype = (daliasskintype_t *)(pinskinintervals + groupskins); for (j=0 ; j<groupskins ; j++) { Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); if (j == 0) { texels = (byte *) Hunk_AllocName(size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype), size); } //johnfitz -- rewritten q_snprintf (name, sizeof(name), "%s:frame%i_%i", loadmodel->name, i,j); offset = (src_offset_t)(pskintype) - (src_offset_t)mod_base; //johnfitz if (Mod_CheckFullbrights ((byte *)(pskintype), size)) { pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags | TEXPREF_NOBRIGHT); q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_%i_glow", loadmodel->name, i,j); pheader->fbtextures[i][j&3] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags | TEXPREF_FULLBRIGHT); } else { pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags); pheader->fbtextures[i][j&3] = NULL; } //johnfitz pskintype = (daliasskintype_t *)((byte *)(pskintype) + size); } k = j; for (/**/; j < 4; j++) pheader->gltextures[i][j&3] = pheader->gltextures[i][j - k]; } } return (void *)pskintype; } //========================================================================= /* ================= Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases ================= */ void Mod_CalcAliasBounds (aliashdr_t *a) { int i,j,k; float dist, yawradius, radius; vec3_t v; //clear out all data for (i=0; i<3;i++) { loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = 999999; loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -999999; radius = yawradius = 0; } //process verts for (i=0 ; i<a->numposes; i++) for (j=0; j<a->numverts; j++) { for (k=0; k<3;k++) v[k] = poseverts[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k]; for (k=0; k<3;k++) { loadmodel->mins[k] = q_min(loadmodel->mins[k], v[k]); loadmodel->maxs[k] = q_max(loadmodel->maxs[k], v[k]); } dist = v[0] * v[0] + v[1] * v[1]; if (yawradius < dist) yawradius = dist; dist += v[2] * v[2]; if (radius < dist) radius = dist; } //rbounds will be used when entity has nonzero pitch or roll radius = sqrt(radius); loadmodel->rmins[0] = loadmodel->rmins[1] = loadmodel->rmins[2] = -radius; loadmodel->rmaxs[0] = loadmodel->rmaxs[1] = loadmodel->rmaxs[2] = radius; //ybounds will be used when entity has nonzero yaw yawradius = sqrt(yawradius); loadmodel->ymins[0] = loadmodel->ymins[1] = -yawradius; loadmodel->ymaxs[0] = loadmodel->ymaxs[1] = yawradius; loadmodel->ymins[2] = loadmodel->mins[2]; loadmodel->ymaxs[2] = loadmodel->maxs[2]; } static qboolean nameInList(const char *list, const char *name) { const char *s; char tmp[MAX_QPATH]; int i; s = list; while (*s) { // make a copy until the next comma or end of string i = 0; while (*s && *s != ',') { if (i < MAX_QPATH - 1) tmp[i++] = *s; s++; } tmp[i] = '\0'; //compare it to the model name if (!strcmp(name, tmp)) { return true; } //search forwards to the next comma or end of string while (*s && *s == ',') s++; } return false; } /* ================= Mod_SetExtraFlags -- johnfitz -- set up extra flags that aren't in the mdl ================= */ void Mod_SetExtraFlags (qmodel_t *mod) { extern cvar_t r_nolerp_list, r_noshadow_list; if (!mod || mod->type != mod_alias) return; mod->flags &= (0xFF | MF_HOLEY); //only preserve first byte, plus MF_HOLEY // nolerp flag if (nameInList(r_nolerp_list.string, mod->name)) mod->flags |= MOD_NOLERP; // noshadow flag if (nameInList(r_noshadow_list.string, mod->name)) mod->flags |= MOD_NOSHADOW; // fullbright hack (TODO: make this a cvar list) if (!strcmp (mod->name, "progs/flame2.mdl") || !strcmp (mod->name, "progs/flame.mdl") || !strcmp (mod->name, "progs/boss.mdl")) mod->flags |= MOD_FBRIGHTHACK; } /* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; int version, numframes; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; mod_base = (byte *)buffer; //johnfitz version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof(aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_LBM_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i=0 ; i<3 ; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = (daliasskintype_t *) Mod_LoadAllSkins (pheader->numskins, pskintype); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i=0 ; i<pheader->numverts ; i++) { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; for (i=0 ; i<pheader->numtris ; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j=0 ; j<3 ; j++) { triangles[i].vertindex[j] = LittleLong (pintriangles[i].vertindex[j]); } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; for (i=0 ; i<numframes ; i++) { aliasframetype_t frametype; frametype = (aliasframetype_t) LittleLong (pframetype->type); if (frametype == ALIAS_SINGLE) pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); else pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } pheader->numposes = posenum; mod->type = mod_alias; Mod_SetExtraFlags (mod); //johnfitz Mod_CalcAliasBounds (pheader); //johnfitz // // build the draw lists // GL_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } //============================================================================= /* ================= Mod_LoadSpriteFrame ================= */ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int width, height, size, origin[2]; char name[64]; src_offset_t offset; //johnfitz pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof (mspriteframe_t),loadname); *ppframe = pspriteframe; pspriteframe->width = width; pspriteframe->height = height; origin[0] = LittleLong (pinframe->origin[0]); origin[1] = LittleLong (pinframe->origin[1]); pspriteframe->up = origin[1]; pspriteframe->down = origin[1] - height; pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; //johnfitz -- image might be padded pspriteframe->smax = (float)width/(float)TexMgr_PadConditional(width); pspriteframe->tmax = (float)height/(float)TexMgr_PadConditional(height); //johnfitz q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, framenum); offset = (src_offset_t)(pinframe+1) - (src_offset_t)mod_base; //johnfitz pspriteframe->gltexture = TexMgr_LoadImage (loadmodel, name, width, height, SRC_INDEXED, (byte *)(pinframe + 1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); } /* ================= Mod_LoadSpriteGroup ================= */ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; int i, numframes; dspriteinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (dspritegroup_t *)pin; numframes = LittleLong (pingroup->numframes); pspritegroup = (mspritegroup_t *) Hunk_AllocName (sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); pspritegroup->numframes = numframes; *ppframe = (mspriteframe_t *)pspritegroup; pin_intervals = (dspriteinterval_t *)(pingroup + 1); poutintervals = (float *) Hunk_AllocName (numframes * sizeof (float), loadname); pspritegroup->intervals = poutintervals; for (i=0 ; i<numframes ; i++) { *poutintervals = LittleFloat (pin_intervals->interval); if (*poutintervals <= 0.0) Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i=0 ; i<numframes ; i++) { ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i); } return ptemp; } /* ================= Mod_LoadSpriteModel ================= */ void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer) { int i; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; mod_base = (byte *)buffer; //johnfitz version = LittleLong (pin->version); if (version != SPRITE_VERSION) Sys_Error ("%s has wrong version number " "(%i should be %i)", mod->name, version, SPRITE_VERSION); numframes = LittleLong (pin->numframes); size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); psprite = (msprite_t *) Hunk_AllocName (size, loadname); mod->cache.data = psprite; psprite->type = LittleLong (pin->type); psprite->maxwidth = LittleLong (pin->width); psprite->maxheight = LittleLong (pin->height); psprite->beamlength = LittleFloat (pin->beamlength); mod->synctype = (synctype_t) LittleLong (pin->synctype); psprite->numframes = numframes; mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; mod->mins[2] = -psprite->maxheight/2; mod->maxs[2] = psprite->maxheight/2; // // load the frames // if (numframes < 1) Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); mod->numframes = numframes; pframetype = (dspriteframetype_t *)(pin + 1); for (i=0 ; i<numframes ; i++) { spriteframetype_t frametype; frametype = (spriteframetype_t) LittleLong (pframetype->type); psprite->frames[i].type = frametype; if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i); } else { pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i); } } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ void Mod_Print (void) { int i; qmodel_t *mod; Con_SafePrintf ("Cached models:\n"); //johnfitz -- safeprint instead of print for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { Con_SafePrintf ("%8p : %s\n", mod->cache.data, mod->name); //johnfitz -- safeprint instead of print } Con_Printf ("%i models\n",mod_numknown); //johnfitz -- print the total too } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/progdefs.h������������������������������������������������������������������0000644�0000000�0000000�00000001553�11340073704�015506� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __PROGDEFS_H #define __PROGDEFS_H #include "progdefs.q1" #endif /* __PROGDEFS_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_model.h������������������������������������������������������������������0000644�0000000�0000000�00000027712�13141147307�015466� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __MODEL__ #define __MODEL__ #include "modelgen.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ // entity effects #define EF_BRIGHTFIELD 1 #define EF_MUZZLEFLASH 2 #define EF_BRIGHTLIGHT 4 #define EF_DIMLIGHT 8 /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; // ericw -- each texture has two chains, so we can clear the model chains // without affecting the world typedef enum { chain_world = 0, chain_model = 1 } texchain_t; typedef struct texture_s { char name[16]; unsigned width, height; struct gltexture_s *gltexture; //johnfitz -- pointer to gltexture struct gltexture_s *fullbright; //johnfitz -- fullbright mask texture struct gltexture_s *warpimage; //johnfitz -- for water animation qboolean update_warp; //johnfitz -- update warp this frame struct msurface_s *texturechains[2]; // for texture chains int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these unsigned offsets[MIPLEVELS]; // four mip maps stored } texture_t; #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWSPRITE 8 #define SURF_DRAWTURB 0x10 #define SURF_DRAWTILED 0x20 #define SURF_DRAWBACKGROUND 0x40 #define SURF_UNDERWATER 0x80 #define SURF_NOTEXTURE 0x100 //johnfitz #define SURF_DRAWFENCE 0x200 #define SURF_DRAWLAVA 0x400 #define SURF_DRAWSLIME 0x800 #define SURF_DRAWTELE 0x1000 #define SURF_DRAWWATER 0x2000 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { unsigned int v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct { float vecs[2][4]; float mipadjust; texture_t *texture; int flags; } mtexinfo_t; #define VERTEXSIZE 7 typedef struct glpoly_s { struct glpoly_s *next; struct glpoly_s *chain; int numverts; float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) } glpoly_t; typedef struct msurface_s { int visframe; // should be drawn when node is crossed qboolean culled; // johnfitz -- for frustum culling float mins[3]; // johnfitz -- for frustum culling float maxs[3]; // johnfitz -- for frustum culling mplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges short texturemins[2]; short extents[2]; int light_s, light_t; // gl lightmap coordinates glpoly_t *polys; // multiple if warped struct msurface_s *texturechain; mtexinfo_t *texinfo; int vbo_firstvert; // index of this surface's first vert in the VBO // lighting info int dlightframe; unsigned int dlightbits[(MAX_DLIGHTS + 31) >> 5]; // int is 32 bits, need an array for MAX_DLIGHTS > 32 int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap qboolean cached_dlight; // true if dynamic light in cache byte *samples; // [numstyles*surfsize] } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned int firstsurface; unsigned int numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // leaf specific byte *compressed_vis; efrag_t *efrags; msurface_t **firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; //johnfitz -- for clipnodes>32k typedef struct mclipnode_s { int planenum; int children[2]; // negative numbers are contents } mclipnode_t; //johnfitz // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { mclipnode_t *clipnodes; //johnfitz -- was dclipnode_t mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; /* ============================================================================== SPRITE MODELS ============================================================================== */ // FIXME: shorten these? typedef struct mspriteframe_s { int width, height; float up, down, left, right; float smax, tmax; //johnfitz -- image might be padded struct gltexture_s *gltexture; } mspriteframe_t; typedef struct { int numframes; float *intervals; mspriteframe_t *frames[1]; } mspritegroup_t; typedef struct { spriteframetype_t type; mspriteframe_t *frameptr; } mspriteframedesc_t; typedef struct { int type; int maxwidth; int maxheight; int numframes; float beamlength; // remove? void *cachespot; // remove? mspriteframedesc_t frames[1]; } msprite_t; /* ============================================================================== ALIAS MODELS Alias models are position independent, so the cache manager can move them. ============================================================================== */ //-- from RMQEngine // split out to keep vertex sizes down typedef struct aliasmesh_s { float st[2]; unsigned short vertindex; } aliasmesh_t; typedef struct meshxyz_s { byte xyz[4]; signed char normal[4]; } meshxyz_t; typedef struct meshst_s { float st[2]; } meshst_t; //-- typedef struct { int firstpose; int numposes; float interval; trivertx_t bboxmin; trivertx_t bboxmax; int frame; char name[16]; } maliasframedesc_t; typedef struct { trivertx_t bboxmin; trivertx_t bboxmax; int frame; } maliasgroupframedesc_t; typedef struct { int numframes; int intervals; maliasgroupframedesc_t frames[1]; } maliasgroup_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct mtriangle_s { int facesfront; int vertindex[3]; } mtriangle_t; #define MAX_SKINS 32 typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; //ericw -- used to populate vbo int numverts_vbo; // number of verts with unique x,y,z,s,t intptr_t meshdesc; // offset into extradata: numverts_vbo aliasmesh_t int numindexes; intptr_t indexes; // offset into extradata: numindexes unsigned shorts intptr_t vertexes; // offset into extradata: numposes*vertsperframe trivertx_t //ericw -- int numposes; int poseverts; int posedata; // numposes*poseverts trivert_t int commands; // gl command list with embedded s/t struct gltexture_s *gltextures[MAX_SKINS][4]; //johnfitz struct gltexture_s *fbtextures[MAX_SKINS][4]; //johnfitz int texels[MAX_SKINS]; // only for player skins maliasframedesc_t frames[1]; // variable sized } aliashdr_t; #define MAXALIASVERTS 2000 //johnfitz -- was 1024 #define MAXALIASFRAMES 256 #define MAXALIASTRIS 2048 extern aliashdr_t *pheader; extern stvert_t stverts[MAXALIASVERTS]; extern mtriangle_t triangles[MAXALIASTRIS]; extern trivertx_t *poseverts[MAXALIASFRAMES]; //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; #define EF_ROCKET 1 // leave a trail #define EF_GRENADE 2 // leave a trail #define EF_GIB 4 // leave a trail #define EF_ROTATE 8 // rotate (bonus items) #define EF_TRACER 16 // green split trail #define EF_ZOMGIB 32 // small blood trail #define EF_TRACER2 64 // orange split trail + rotate #define EF_TRACER3 128 // purple trail #define MF_HOLEY (1u<<14) // MarkV/QSS -- make index 255 transparent on mdl's //johnfitz -- extra flags for rendering #define MOD_NOLERP 256 //don't lerp when animating #define MOD_NOSHADOW 512 //don't cast a shadow #define MOD_FBRIGHTHACK 1024 //when fullbrights are disabled, use a hack to render this model brighter //johnfitz typedef struct qmodel_s { char name[MAX_QPATH]; unsigned int path_id; // path id of the game directory // that this model came from qboolean needload; // bmodels and sprites don't cache normally modtype_t type; int numframes; synctype_t synctype; int flags; // // volume occupied by the model graphics // vec3_t mins, maxs; vec3_t ymins, ymaxs; //johnfitz -- bounds for entities with nonzero yaw vec3_t rmins, rmaxs; //johnfitz -- bounds for entities with nonzero pitch or roll //johnfitz -- removed float radius; // // solid volume for clipping // qboolean clipbox; vec3_t clipmins, clipmaxs; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numedges; medge_t *edges; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int numclipnodes; mclipnode_t *clipnodes; //johnfitz -- was dclipnode_t int nummarksurfaces; msurface_t **marksurfaces; hull_t hulls[MAX_MAP_HULLS]; int numtextures; texture_t **textures; byte *visdata; byte *lightdata; char *entities; int bspversion; // // alias model // GLuint meshvbo; GLuint meshindexesvbo; int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t // // additional model data // cache_user_t cache; // only access through Mod_Extradata } qmodel_t; //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); void Mod_ResetAll (void); // for gamedir changes (Host_Game_f) qmodel_t *Mod_ForName (const char *name, qboolean crash); void *Mod_Extradata (qmodel_t *mod); // handles caching void Mod_TouchModel (const char *name); mleaf_t *Mod_PointInLeaf (float *p, qmodel_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model); byte *Mod_NoVisPVS (qmodel_t *model); void Mod_SetExtraFlags (qmodel_t *mod); #endif // __MODEL__ ������������������������������������������������������quakespasm-0.93.0/Quake/host.c����������������������������������������������������������������������0000644�0000000�0000000�00000052316�13157453126�014660� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // host.c -- coordinates spawning and killing of local servers #include "quakedef.h" #include "bgmusic.h" #include <setjmp.h> /* A server can allways be started, even if the system started out as a client to a remote system. A client can NOT be started if the system started as a dedicated server. Memory is cleared / released when a server or client begins, not when they end. */ quakeparms_t *host_parms; qboolean host_initialized; // true if into command execution double host_frametime; double realtime; // without any filtering or bounding double oldrealtime; // last frame run int host_framecount; int host_hunklevel; int minimum_memory; client_t *host_client; // current client jmp_buf host_abortserver; byte *host_colormap; cvar_t host_framerate = {"host_framerate","0",CVAR_NONE}; // set for slow motion cvar_t host_speeds = {"host_speeds","0",CVAR_NONE}; // set for running times cvar_t host_maxfps = {"host_maxfps", "72", CVAR_ARCHIVE}; //johnfitz cvar_t host_timescale = {"host_timescale", "0", CVAR_NONE}; //johnfitz cvar_t max_edicts = {"max_edicts", "8192", CVAR_NONE}; //johnfitz //ericw -- changed from 2048 to 8192, removed CVAR_ARCHIVE cvar_t sys_ticrate = {"sys_ticrate","0.05",CVAR_NONE}; // dedicated server cvar_t serverprofile = {"serverprofile","0",CVAR_NONE}; cvar_t fraglimit = {"fraglimit","0",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t timelimit = {"timelimit","0",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t teamplay = {"teamplay","0",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t samelevel = {"samelevel","0",CVAR_NONE}; cvar_t noexit = {"noexit","0",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t skill = {"skill","1",CVAR_NONE}; // 0 - 3 cvar_t deathmatch = {"deathmatch","0",CVAR_NONE}; // 0, 1, or 2 cvar_t coop = {"coop","0",CVAR_NONE}; // 0 or 1 cvar_t pausable = {"pausable","1",CVAR_NONE}; cvar_t developer = {"developer","0",CVAR_NONE}; cvar_t temp1 = {"temp1","0",CVAR_NONE}; cvar_t devstats = {"devstats","0",CVAR_NONE}; //johnfitz -- track developer statistics that vary every frame devstats_t dev_stats, dev_peakstats; overflowtimes_t dev_overflows; //this stores the last time overflow messages were displayed, not the last time overflows occured /* ================ Max_Edicts_f -- johnfitz ================ */ static void Max_Edicts_f (cvar_t *var) { //TODO: clamp it here? if (cls.state == ca_connected || sv.active) Con_Printf ("Changes to max_edicts will not take effect until the next time a map is loaded.\n"); } /* ================ Max_Fps_f -- ericw ================ */ static void Max_Fps_f (cvar_t *var) { if (var->value > 72) Con_Warning ("host_maxfps above 72 breaks physics.\n"); } /* ================ Host_EndGame ================ */ void Host_EndGame (const char *message, ...) { va_list argptr; char string[1024]; va_start (argptr,message); q_vsnprintf (string, sizeof(string), message, argptr); va_end (argptr); Con_DPrintf ("Host_EndGame: %s\n",string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit if (cls.demonum != -1) CL_NextDemo (); else CL_Disconnect (); longjmp (host_abortserver, 1); } /* ================ Host_Error This shuts down both the client and server ================ */ void Host_Error (const char *error, ...) { va_list argptr; char string[1024]; static qboolean inerror = false; if (inerror) Sys_Error ("Host_Error: recursively entered"); inerror = true; SCR_EndLoadingPlaque (); // reenable screen updates va_start (argptr,error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); Con_Printf ("Host_Error: %s\n",string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit CL_Disconnect (); cls.demonum = -1; cl.intermission = 0; //johnfitz -- for errors during intermissions (changelevel with no map found, etc.) inerror = false; longjmp (host_abortserver, 1); } /* ================ Host_FindMaxClients ================ */ void Host_FindMaxClients (void) { int i; svs.maxclients = 1; i = COM_CheckParm ("-dedicated"); if (i) { cls.state = ca_dedicated; if (i != (com_argc - 1)) { svs.maxclients = Q_atoi (com_argv[i+1]); } else svs.maxclients = 8; } else cls.state = ca_disconnected; i = COM_CheckParm ("-listen"); if (i) { if (cls.state == ca_dedicated) Sys_Error ("Only one of -dedicated or -listen can be specified"); if (i != (com_argc - 1)) svs.maxclients = Q_atoi (com_argv[i+1]); else svs.maxclients = 8; } if (svs.maxclients < 1) svs.maxclients = 8; else if (svs.maxclients > MAX_SCOREBOARD) svs.maxclients = MAX_SCOREBOARD; svs.maxclientslimit = svs.maxclients; if (svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = (struct client_s *) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); if (svs.maxclients > 1) Cvar_SetQuick (&deathmatch, "1"); else Cvar_SetQuick (&deathmatch, "0"); } void Host_Version_f (void) { Con_Printf ("Quake Version %1.2f\n", VERSION); Con_Printf ("QuakeSpasm Version " QUAKESPASM_VER_STRING "\n"); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); } /* cvar callback functions : */ void Host_Callback_Notify (cvar_t *var) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } /* ======================= Host_InitLocal ====================== */ void Host_InitLocal (void) { Cmd_AddCommand ("version", Host_Version_f); Host_InitCommands (); Cvar_RegisterVariable (&host_framerate); Cvar_RegisterVariable (&host_speeds); Cvar_RegisterVariable (&host_maxfps); //johnfitz Cvar_SetCallback (&host_maxfps, Max_Fps_f); Cvar_RegisterVariable (&host_timescale); //johnfitz Cvar_RegisterVariable (&max_edicts); //johnfitz Cvar_SetCallback (&max_edicts, Max_Edicts_f); Cvar_RegisterVariable (&devstats); //johnfitz Cvar_RegisterVariable (&sys_ticrate); Cvar_RegisterVariable (&sys_throttle); Cvar_RegisterVariable (&serverprofile); Cvar_RegisterVariable (&fraglimit); Cvar_RegisterVariable (&timelimit); Cvar_RegisterVariable (&teamplay); Cvar_SetCallback (&fraglimit, Host_Callback_Notify); Cvar_SetCallback (&timelimit, Host_Callback_Notify); Cvar_SetCallback (&teamplay, Host_Callback_Notify); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&noexit); Cvar_SetCallback (&noexit, Host_Callback_Notify); Cvar_RegisterVariable (&skill); Cvar_RegisterVariable (&developer); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&pausable); Cvar_RegisterVariable (&temp1); Host_FindMaxClients (); } /* =============== Host_WriteConfiguration Writes key bindings and archived cvars to config.cfg =============== */ void Host_WriteConfiguration (void) { FILE *f; // dedicated servers initialize the host but don't parse and set the // config.cfg cvars if (host_initialized & !isDedicated) { f = fopen (va("%s/config.cfg", com_gamedir), "w"); if (!f) { Con_Printf ("Couldn't write config.cfg.\n"); return; } VID_SyncCvars (); //johnfitz -- write actual current mode to config file, in case cvars were messed with Key_WriteBindings (f); Cvar_WriteVariables (f); //johnfitz -- extra commands to preserve state fprintf (f, "vid_restart\n"); if (in_mlook.state & 1) fprintf (f, "+mlook\n"); //johnfitz fclose (f); //johnfitz -- also save fitzquake.rc #if 0 f = fopen (va("%s/fitzquake.rc", GAMENAME), "w"); //always save in id1 if (!f) { Con_Printf ("Couldn't write fitzquake.rc.\n"); return; } Cvar_WriteVariables (f); fprintf (f, "vid_restart\n"); if (in_mlook.state & 1) fprintf (f, "+mlook\n"); fclose (f); #endif //johnfitz } } /* ================= SV_ClientPrintf Sends text across to be displayed FIXME: make this just a stuffed echo? ================= */ void SV_ClientPrintf (const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof(string), fmt,argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_print); MSG_WriteString (&host_client->message, string); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (const char *fmt, ...) { va_list argptr; char string[1024]; int i; va_start (argptr,fmt); q_vsnprintf (string, sizeof(string), fmt, argptr); va_end (argptr); for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte (&svs.clients[i].message, svc_print); MSG_WriteString (&svs.clients[i].message, string); } } } /* ================= Host_ClientCommands Send text over to the client to be executed ================= */ void Host_ClientCommands (const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof(string), fmt, argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_stufftext); MSG_WriteString (&host_client->message, string); } /* ===================== SV_DropClient Called when the player is getting totally kicked off the host if (crash = true), don't bother sending signofs ===================== */ void SV_DropClient (qboolean crash) { int saveSelf; int i; client_t *client; if (!crash) { // send any final messages (don't check for errors) if (NET_CanSendMessage (host_client->netconnection)) { MSG_WriteByte (&host_client->message, svc_disconnect); NET_SendMessage (host_client->netconnection, &host_client->message); } if (host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = pr_global_struct->self; pr_global_struct->self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (pr_global_struct->ClientDisconnect); pr_global_struct->self = saveSelf; } Sys_Printf ("Client %s removed\n",host_client->name); } // break the net connection NET_Close (host_client->netconnection); host_client->netconnection = NULL; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = -999999; net_activeconnections--; // send notification to all clients for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatename); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteString (&client->message, ""); MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteShort (&client->message, 0); MSG_WriteByte (&client->message, svc_updatecolors); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteByte (&client->message, 0); } } /* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer(qboolean crash) { int i; int count; sizebuf_t buf; byte message[4]; double start; if (!sv.active) return; sv.active = false; // stop all client sounds immediately if (cls.state == ca_connected) CL_Disconnect (); // flush any pending messages - like the score!!! start = Sys_DoubleTime(); do { count = 0; for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (host_client->active && host_client->message.cursize) { if (NET_CanSendMessage (host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear (&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if ((Sys_DoubleTime() - start) > 3.0) break; } while (count); // make sure all the clients know we're disconnecting buf.data = message; buf.maxsize = 4; buf.cursize = 0; MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll(&buf, 5.0); if (count) Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count); for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) if (host_client->active) SV_DropClient(crash); // // clear structures // // memset (&sv, 0, sizeof(sv)); // ServerSpawn already do this by Host_ClearMemory memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); } /* ================ Host_ClearMemory This clears all the memory used by both the client and server, but does not reinitialize anything. ================ */ void Host_ClearMemory (void) { Con_DPrintf ("Clearing memory\n"); D_FlushCaches (); Mod_ClearAll (); /* host_hunklevel MUST be set at this point */ Hunk_FreeToLowMark (host_hunklevel); cls.signon = 0; free(sv.edicts); // ericw -- sv.edicts switched to use malloc() memset (&sv, 0, sizeof(sv)); memset (&cl, 0, sizeof(cl)); } //============================================================================== // // Host Frame // //============================================================================== /* =================== Host_FilterTime Returns false if the time is too short to run a frame =================== */ qboolean Host_FilterTime (float time) { float maxfps; //johnfitz realtime += time; //johnfitz -- max fps cvar maxfps = CLAMP (10.0, host_maxfps.value, 1000.0); if (!cls.timedemo && realtime - oldrealtime < 1.0/maxfps) return false; // framerate is too high //johnfitz host_frametime = realtime - oldrealtime; oldrealtime = realtime; //johnfitz -- host_timescale is more intuitive than host_framerate if (host_timescale.value > 0) host_frametime *= host_timescale.value; //johnfitz else if (host_framerate.value > 0) host_frametime = host_framerate.value; else // don't allow really long or short frames host_frametime = CLAMP (0.001, host_frametime, 0.1); //johnfitz -- use CLAMP return true; } /* =================== Host_GetConsoleCommands Add them exactly as if they had been typed at the console =================== */ void Host_GetConsoleCommands (void) { const char *cmd; if (!isDedicated) return; // no stdin necessary in graphical mode while (1) { cmd = Sys_ConsoleInput (); if (!cmd) break; Cbuf_AddText (cmd); } } /* ================== Host_ServerFrame ================== */ void Host_ServerFrame (void) { int i, active; //johnfitz edict_t *ent; //johnfitz // run the world state pr_global_struct->frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) SV_Physics (); //johnfitz -- devstats if (cls.signon == SIGNONS) { for (i=0, active=0; i<sv.num_edicts; i++) { ent = EDICT_NUM(i); if (!ent->free) active++; } if (active > 600 && dev_peakstats.edicts <= 600) Con_DWarning ("%i edicts exceeds standard limit of 600 (max = %d).\n", active, sv.max_edicts); dev_stats.edicts = active; dev_peakstats.edicts = q_max(active, dev_peakstats.edicts); } //johnfitz // send all messages to the clients SV_SendClientMessages (); } /* ================== Host_Frame Runs all active servers ================== */ void _Host_Frame (float time) { static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; if (setjmp (host_abortserver) ) return; // something bad happened, or the server disconnected // keep the random time dependent rand (); // decide the simulation time if (!Host_FilterTime (time)) return; // don't run too fast, or packets will flood out // get new key events Key_UpdateForDest (); IN_UpdateInputMode (); Sys_SendKeyEvents (); // allow mice or other external controllers to add commands IN_Commands (); // process console commands Cbuf_Execute (); NET_Poll(); // if running the server locally, make intentions now if (sv.active) CL_SendCmd (); //------------------- // // server operations // //------------------- // check for commands typed to the host Host_GetConsoleCommands (); if (sv.active) Host_ServerFrame (); //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!sv.active) CL_SendCmd (); // fetch results from server if (cls.state == ca_connected) CL_ReadFromServer (); // update video if (host_speeds.value) time1 = Sys_DoubleTime (); SCR_UpdateScreen (); CL_RunParticles (); //johnfitz -- seperated from rendering if (host_speeds.value) time2 = Sys_DoubleTime (); // update audio BGM_Update(); // adds music raw samples and/or advances midi driver if (cls.signon == SIGNONS) { S_Update (r_origin, vpn, vright, vup); CL_DecayLights (); } else S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); CDAudio_Update(); if (host_speeds.value) { pass1 = (time1 - time3)*1000; time3 = Sys_DoubleTime (); pass2 = (time2 - time1)*1000; pass3 = (time3 - time2)*1000; Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", pass1+pass2+pass3, pass1, pass2, pass3); } host_framecount++; } void Host_Frame (float time) { double time1, time2; static double timetotal; static int timecount; int i, c, m; if (!serverprofile.value) { _Host_Frame (time); return; } time1 = Sys_DoubleTime (); _Host_Frame (time); time2 = Sys_DoubleTime (); timetotal += time2 - time1; timecount++; if (timecount < 1000) return; m = timetotal*1000/timecount; timecount = 0; timetotal = 0; c = 0; for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active) c++; } Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m); } /* ==================== Host_Init ==================== */ void Host_Init (void) { if (standard_quake) minimum_memory = MINIMUM_MEMORY; else minimum_memory = MINIMUM_MEMORY_LEVELPAK; if (COM_CheckParm ("-minmemory")) host_parms->memsize = minimum_memory; if (host_parms->memsize < minimum_memory) Sys_Error ("Only %4.1f megs of memory available, can't execute game", host_parms->memsize / (float)0x100000); com_argc = host_parms->argc; com_argv = host_parms->argv; Memory_Init (host_parms->membase, host_parms->memsize); Cbuf_Init (); Cmd_Init (); LOG_Init (host_parms); Cvar_Init (); //johnfitz COM_Init (); COM_InitFilesystem (); Host_InitLocal (); W_LoadWadFile (); //johnfitz -- filename is now hard-coded for honesty if (cls.state != ca_dedicated) { Key_Init (); Con_Init (); } PR_Init (); Mod_Init (); NET_Init (); SV_Init (); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/ (1024*1024.0)); if (cls.state != ca_dedicated) { host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp", NULL); if (!host_colormap) Sys_Error ("Couldn't load gfx/colormap.lmp"); V_Init (); Chase_Init (); M_Init (); ExtraMaps_Init (); //johnfitz Modlist_Init (); //johnfitz DemoList_Init (); //ericw VID_Init (); IN_Init (); TexMgr_Init (); //johnfitz Draw_Init (); SCR_Init (); R_Init (); S_Init (); CDAudio_Init (); BGM_Init(); Sbar_Init (); CL_Init (); } Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Con_Printf ("\n========= Quake Initialized =========\n\n"); if (cls.state != ca_dedicated) { Cbuf_InsertText ("exec quake.rc\n"); // johnfitz -- in case the vid mode was locked during vid_init, we can unlock it now. // note: two leading newlines because the command buffer swallows one of them. Cbuf_AddText ("\n\nvid_unlock\n"); } if (cls.state == ca_dedicated) { Cbuf_AddText ("exec autoexec.cfg\n"); Cbuf_AddText ("stuffcmds"); Cbuf_Execute (); if (!sv.active) Cbuf_AddText ("map start\n"); } } /* =============== Host_Shutdown FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better to run quit through here before the final handoff to the sys code. =============== */ void Host_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; // keep Con_Printf from trying to update the screen scr_disabled_for_loading = true; Host_WriteConfiguration (); NET_Shutdown (); if (cls.state != ca_dedicated) { if (con_initialized) History_Shutdown (); BGM_Shutdown(); CDAudio_Shutdown (); S_Shutdown (); IN_Shutdown (); VID_Shutdown(); } LOG_Close (); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cvar.h����������������������������������������������������������������������0000644�0000000�0000000�00000011615�12407762022�014633� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __CVAR_H__ #define __CVAR_H__ /* cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly in C code. it is sufficient to initialize a cvar_t with just the first two fields, or you can add a ,true flag for variables that you want saved to the configuration file when the game is quit: cvar_t r_draworder = {"r_draworder","1"}; cvar_t scr_screensize = {"screensize","1",true}; Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: Cvar_RegisterVariable (&host_framerate); C code usually just references a cvar in place: if ( r_draworder.value ) It could optionally ask for the value to be looked up for a string name: if (Cvar_VariableValue ("r_draworder")) Interpreted prog code can access cvars with the cvar(name) or cvar_set (name, value) internal functions: teamplay = cvar("teamplay"); cvar_set ("registered", "1"); The user can access cvars from the console in two ways: r_draworder prints the current value r_draworder 0 sets the current value to 0 Cvars are restricted from having the same names as commands to keep this interface from being ambiguous. */ #define CVAR_NONE 0 #define CVAR_ARCHIVE (1U << 0) // if set, causes it to be saved to config #define CVAR_NOTIFY (1U << 1) // changes will be broadcasted to all players (q1) #define CVAR_SERVERINFO (1U << 2) // added to serverinfo will be sent to clients (q1/net_dgrm.c and qwsv) #define CVAR_USERINFO (1U << 3) // added to userinfo, will be sent to server (qwcl) #define CVAR_CHANGED (1U << 4) #define CVAR_ROM (1U << 6) #define CVAR_LOCKED (1U << 8) // locked temporarily #define CVAR_REGISTERED (1U << 10) // the var is added to the list of variables #define CVAR_CALLBACK (1U << 16) // var has a callback typedef void (*cvarcallback_t) (struct cvar_s *); typedef struct cvar_s { const char *name; const char *string; unsigned int flags; float value; const char *default_string; //johnfitz -- remember defaults for reset function cvarcallback_t callback; struct cvar_s *next; } cvar_t; void Cvar_RegisterVariable (cvar_t *variable); // registers a cvar that already has the name, string, and optionally // the archive elements set. void Cvar_SetCallback (cvar_t *var, cvarcallback_t func); // set a callback function to the var void Cvar_Set (const char *var_name, const char *value); // equivelant to "<name> <variable>" typed at the console void Cvar_SetValue (const char *var_name, const float value); // expands value to a string and calls Cvar_Set void Cvar_SetROM (const char *var_name, const char *value); void Cvar_SetValueROM (const char *var_name, const float value); // sets a CVAR_ROM variable from within the engine void Cvar_SetQuick (cvar_t *var, const char *value); void Cvar_SetValueQuick (cvar_t *var, const float value); // these two accept a cvar pointer instead of a var name, // but are otherwise identical to the "non-Quick" versions. // the cvar MUST be registered. float Cvar_VariableValue (const char *var_name); // returns 0 if not defined or non numeric const char *Cvar_VariableString (const char *var_name); // returns an empty string if not defined qboolean Cvar_Command (void); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) void Cvar_WriteVariables (FILE *f); // Writes lines containing "set variable value" for all variables // with the CVAR_ARCHIVE flag set cvar_t *Cvar_FindVar (const char *var_name); cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags); void Cvar_LockVar (const char *var_name); void Cvar_UnlockVar (const char *var_name); void Cvar_UnlockAll (void); void Cvar_Init (void); const char *Cvar_CompleteVariable (const char *partial); // attempts to match a partial variable name for command line completion // returns NULL if nothing fits #endif /* __CVAR_H__ */ �������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/strlcpy.c�������������������������������������������������������������������0000644�0000000�0000000�00000003064�11676323013�015372� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/types.h> #include <string.h> #include "strl_fn.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t q_strlcpy (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/quakedef.h������������������������������������������������������������������0000644�0000000�0000000�00000021354�13136715127�015472� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef QUAKEDEFS_H #define QUAKEDEFS_H // quakedef.h -- primary header for client #define QUAKE_GAME // as opposed to utilities #define VERSION 1.09 #define GLQUAKE_VERSION 1.00 #define D3DQUAKE_VERSION 0.01 #define WINQUAKE_VERSION 0.996 #define LINUX_VERSION 1.30 #define X11_VERSION 1.10 #define FITZQUAKE_VERSION 0.85 //johnfitz #define QUAKESPASM_VERSION 0.93 #define QUAKESPASM_VER_PATCH 0 // helper to print a string like 0.92.1 #ifndef QUAKESPASM_VER_SUFFIX #define QUAKESPASM_VER_SUFFIX // optional version suffix string literal like "-beta1" #endif #define QS_STRINGIFY_(x) #x #define QS_STRINGIFY(x) QS_STRINGIFY_(x) // combined version string like "0.92.1-beta1" #define QUAKESPASM_VER_STRING QS_STRINGIFY(QUAKESPASM_VERSION) "." QS_STRINGIFY(QUAKESPASM_VER_PATCH) QUAKESPASM_VER_SUFFIX //define PARANOID // speed sapping error checking #define GAMENAME "id1" // directory to look in by default #include "q_stdinc.h" // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define CACHE_SIZE 32 // used to align key data structures #define Q_UNUSED(x) (x = x) // for pesky compiler / lint warnings #define MINIMUM_MEMORY 0x550000 #define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000) #define MAX_NUM_ARGVS 50 // up / down #define PITCH 0 // left / right #define YAW 1 // fall over #define ROLL 2 #define MAX_QPATH 64 // max length of a quake game pathname #define ON_EPSILON 0.1 // point on plane side epsilon #define DIST_EPSILON (0.03125) // 1/32 epsilon to keep floating point happy (moved from world.c) #define MAX_MSGLEN 64000 // max length of a reliable message //ericw -- was 32000 #define MAX_DATAGRAM 32000 // max length of unreliable message //johnfitz -- was 1024 #define DATAGRAM_MTU 1400 // johnfitz -- actual limit for unreliable messages to nonlocal clients // // per-level limits // #define MIN_EDICTS 256 // johnfitz -- lowest allowed value for max_edicts cvar #define MAX_EDICTS 32000 // johnfitz -- highest allowed value for max_edicts cvar // ents past 8192 can't play sounds in the standard protocol #define MAX_LIGHTSTYLES 64 #define MAX_MODELS 2048 // johnfitz -- was 256 #define MAX_SOUNDS 2048 // johnfitz -- was 256 #define SAVEGAME_COMMENT_LENGTH 39 #define MAX_STYLESTRING 64 // // stats are integers communicated to the client by the server // #define MAX_CL_STATS 32 #define STAT_HEALTH 0 #define STAT_FRAGS 1 #define STAT_WEAPON 2 #define STAT_AMMO 3 #define STAT_ARMOR 4 #define STAT_WEAPONFRAME 5 #define STAT_SHELLS 6 #define STAT_NAILS 7 #define STAT_ROCKETS 8 #define STAT_CELLS 9 #define STAT_ACTIVEWEAPON 10 #define STAT_TOTALSECRETS 11 #define STAT_TOTALMONSTERS 12 #define STAT_SECRETS 13 // bumped on client side by svc_foundsecret #define STAT_MONSTERS 14 // bumped by svc_killedmonster // stock defines // #define IT_SHOTGUN 1 #define IT_SUPER_SHOTGUN 2 #define IT_NAILGUN 4 #define IT_SUPER_NAILGUN 8 #define IT_GRENADE_LAUNCHER 16 #define IT_ROCKET_LAUNCHER 32 #define IT_LIGHTNING 64 #define IT_SUPER_LIGHTNING 128 #define IT_SHELLS 256 #define IT_NAILS 512 #define IT_ROCKETS 1024 #define IT_CELLS 2048 #define IT_AXE 4096 #define IT_ARMOR1 8192 #define IT_ARMOR2 16384 #define IT_ARMOR3 32768 #define IT_SUPERHEALTH 65536 #define IT_KEY1 131072 #define IT_KEY2 262144 #define IT_INVISIBILITY 524288 #define IT_INVULNERABILITY 1048576 #define IT_SUIT 2097152 #define IT_QUAD 4194304 #define IT_SIGIL1 (1<<28) #define IT_SIGIL2 (1<<29) #define IT_SIGIL3 (1<<30) #define IT_SIGIL4 (1<<31) //=========================================== //rogue changed and added defines #define RIT_SHELLS 128 #define RIT_NAILS 256 #define RIT_ROCKETS 512 #define RIT_CELLS 1024 #define RIT_AXE 2048 #define RIT_LAVA_NAILGUN 4096 #define RIT_LAVA_SUPER_NAILGUN 8192 #define RIT_MULTI_GRENADE 16384 #define RIT_MULTI_ROCKET 32768 #define RIT_PLASMA_GUN 65536 #define RIT_ARMOR1 8388608 #define RIT_ARMOR2 16777216 #define RIT_ARMOR3 33554432 #define RIT_LAVA_NAILS 67108864 #define RIT_PLASMA_AMMO 134217728 #define RIT_MULTI_ROCKETS 268435456 #define RIT_SHIELD 536870912 #define RIT_ANTIGRAV 1073741824 #define RIT_SUPERHEALTH 2147483648 //MED 01/04/97 added hipnotic defines //=========================================== //hipnotic added defines #define HIT_PROXIMITY_GUN_BIT 16 #define HIT_MJOLNIR_BIT 7 #define HIT_LASER_CANNON_BIT 23 #define HIT_PROXIMITY_GUN (1<<HIT_PROXIMITY_GUN_BIT) #define HIT_MJOLNIR (1<<HIT_MJOLNIR_BIT) #define HIT_LASER_CANNON (1<<HIT_LASER_CANNON_BIT) #define HIT_WETSUIT (1<<(23+2)) #define HIT_EMPATHY_SHIELDS (1<<(23+3)) //=========================================== #define MAX_SCOREBOARD 16 #define MAX_SCOREBOARDNAME 32 #define SOUND_CHANNELS 8 typedef struct { const char *basedir; const char *userdir; // user's directory on UNIX platforms. // if user directories are enabled, basedir // and userdir will point to different // memory locations, otherwise to the same. int argc; char **argv; void *membase; int memsize; int numcpus; } quakeparms_t; #include "common.h" #include "bspfile.h" #include "sys.h" #include "zone.h" #include "mathlib.h" #include "cvar.h" #include "protocol.h" #include "net.h" #include "cmd.h" #include "crc.h" #include "progs.h" #include "server.h" #include "platform.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #include <SDL2/SDL_opengl.h> #else #include <SDL/SDL.h> #include <SDL/SDL_opengl.h> #endif #else #include "SDL.h" #include "SDL_opengl.h" #endif #ifndef APIENTRY #define APIENTRY #endif #include "console.h" #include "wad.h" #include "vid.h" #include "screen.h" #include "draw.h" #include "render.h" #include "view.h" #include "sbar.h" #include "q_sound.h" #include "client.h" #include "gl_model.h" #include "world.h" #include "image.h" //johnfitz #include "gl_texmgr.h" //johnfitz #include "input.h" #include "keys.h" #include "menu.h" #include "cdaudio.h" #include "glquake.h" //============================================================================= // the host system specifies the base of the directory tree, the // command line parms passed to the program, and the amount of memory // available for the program to use extern qboolean noclip_anglehack; // // host // extern quakeparms_t *host_parms; extern cvar_t sys_ticrate; extern cvar_t sys_throttle; extern cvar_t sys_nostdout; extern cvar_t developer; extern cvar_t max_edicts; //johnfitz extern qboolean host_initialized; // true if into command execution extern double host_frametime; extern byte *host_colormap; extern int host_framecount; // incremented every frame, never reset extern double realtime; // not bounded in any way, changed at // start of every frame, never reset typedef struct filelist_item_s { char name[32]; struct filelist_item_s *next; } filelist_item_t; extern filelist_item_t *modlist; extern filelist_item_t *extralevels; extern filelist_item_t *demolist; void Host_ClearMemory (void); void Host_ServerFrame (void); void Host_InitCommands (void); void Host_Init (void); void Host_Shutdown(void); void Host_Callback_Notify (cvar_t *var); /* callback function for CVAR_NOTIFY */ FUNC_NORETURN void Host_Error (const char *error, ...) FUNC_PRINTF(1,2); FUNC_NORETURN void Host_EndGame (const char *message, ...) FUNC_PRINTF(1,2); #ifdef __WATCOMC__ #pragma aux Host_Error aborts; #pragma aux Host_EndGame aborts; #endif void Host_Frame (float time); void Host_Quit_f (void); void Host_ClientCommands (const char *fmt, ...) FUNC_PRINTF(1,2); void Host_ShutdownServer (qboolean crash); void Host_WriteConfiguration (void); void ExtraMaps_Init (void); void Modlist_Init (void); void DemoList_Init (void); void DemoList_Rebuild (void); extern int current_skill; // skill level for currently loaded level (in case // the user changes the cvar while the level is // running, this reflects the level actually in use) extern qboolean isDedicated; extern int minimum_memory; #endif /* QUAKEDEFS_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/Makefile.w32����������������������������������������������������������������0000644�0000000�0000000�00000013730�13142575645�015613� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# GNU Makefile for compiling Win32 quakespasm.exe using MinGW or MinGW-w64. # Usage: "make -f Makefile.w32" # To cross-compile on Linux hosts, see the build_cross_win32*.sh scripts. # "make DEBUG=1" to build a debug client. # "make SDL_CONFIG=/path/to/sdl-config" to override the locally included SDL versions. # "make WINSOCK2=1" to use WinSock2 api instead of the old WinSock 1.1. ### Enable/disable SDL2 USE_SDL2=0 ### Enable/disable codecs for streaming music support USE_CODEC_WAVE=1 USE_CODEC_FLAC=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 USE_CODEC_OPUS=1 # either mikmod or xmp USE_CODEC_MIKMOD=1 USE_CODEC_XMP=0 USE_CODEC_UMX=1 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # --------------------------- # Helper functions # --------------------------- check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) # --------------------------- DEBUG ?= 0 WINSOCK2?= 0 # --------------------------- # build variables # --------------------------- CC = gcc LINKER = $(CC) WINDRES = windres STRIP = strip #CPUFLAGS= -mtune=i686 #CPUFLAGS= -march=pentium4 CPUFLAGS= LDFLAGS = -m32 -mwindows DFLAGS ?= CFLAGS ?= -m32 -Wall -Wno-trigraphs CFLAGS += $(CPUFLAGS) ifneq ($(DEBUG),0) DFLAGS += -DDEBUG CFLAGS += -g do_strip= else DFLAGS += -DNDEBUG CFLAGS += -O2 CFLAGS += $(call check_gcc,-fweb,) CFLAGS += $(call check_gcc,-frename-registers,) cmd_strip=$(STRIP) $(1) define do_strip $(call cmd_strip,$(1)); endef endif ifeq ($(USE_SDL2),1) CFLAGS += -DUSE_SDL2 endif # default to our local SDL[2] for build ifeq ($(USE_SDL2),1) SDL_CONFIG ?=../Windows/SDL2/bin/sdl2-config --prefix=../Windows/SDL2 else SDL_CONFIG ?=../Windows/SDL/bin/sdl-config --prefix=../Windows/SDL endif SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags) SDL_LIBS := $(shell $(SDL_CONFIG) --libs) ifeq ($(WINSOCK2),1) DEFWINSOCK :=-D_USE_WINSOCK2 LIBWINSOCK := -lws2_32 else DEFWINSOCK := LIBWINSOCK := -lwsock32 endif CFLAGS += $(DEFWINSOCK) NET_LIBS := $(LIBWINSOCK) ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3.o lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123.o lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif CODECLIBS := ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_FLAC),1) CFLAGS+= -DUSE_CODEC_FLAC CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= -lFLAC endif ifeq ($(USE_CODEC_OPUS),1) CFLAGS+= -DUSE_CODEC_OPUS CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= -lopusfile -lopus -logg endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),1) CFLAGS+= -DUSE_CODEC_MP3 CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),1) CFLAGS+= -DUSE_CODEC_MIKMOD CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),1) CFLAGS+= -DUSE_CODEC_XMP CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 CODECLIBS+= -lxmp endif ifeq ($(USE_CODEC_UMX),1) CFLAGS+= -DUSE_CODEC_UMX endif CFLAGS+= $(CODEC_INC) COMMON_LIBS:= -lm -lopengl32 -lwinmm LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS) # --------------------------- # targets # --------------------------- .PHONY: clean debug release DEFAULT_TARGET := quakespasm.exe # --------------------------- # rules # --------------------------- %.o: %.c $(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $< %.res: ../Windows/%.rc $(WINDRES) -I../Windows --output-format=coff --target=pe-i386 -o $@ $< # ---------------------------------------------------------------------------- # objects # ---------------------------------------------------------------------------- MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj) \ snd_mikmod.o \ snd_xmp.o \ snd_umx.o COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o SYSOBJ_CDA := cd_sdl.o SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := pl_win.o sys_sdl_win.o SYSOBJ_MAIN:= main_sdl.o SYSOBJ_RES := QuakeSpasm.res GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_fog.o \ gl_rmisc.o \ r_part.o \ r_world.o \ gl_screen.o \ gl_sky.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ image.o \ gl_texmgr.o \ gl_mesh.o \ r_sprite.o \ r_alias.o \ r_brush.o \ gl_model.o OBJS := strlcat.o \ strlcpy.o \ $(GLOBJS) \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_tent.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ common.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ world.o \ zone.o \ $(SYSOBJ_SYS) $(SYSOBJ_MAIN) $(SYSOBJ_RES) # ------------------------ # MinGW build rules # ------------------------ quakespasm.exe: $(OBJS) $(LINKER) $(OBJS) $(LDFLAGS) $(LIBS) $(SDL_LIBS) -o $@ $(call do_strip,$@) image.o: lodepng.c lodepng.h stb_image_write.h release: quakespasm.exe debug: $(error Use "make DEBUG=1") clean: rm -f $(shell find . \( -name '*~' -o -name '#*#' -o -name '*.o' -o -name '*.res' -o -name $(DEFAULT_TARGET) \) -print) ����������������������������������������quakespasm-0.93.0/Quake/keys.h����������������������������������������������������������������������0000644�0000000�0000000�00000010701�12756363321�014654� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_KEYS_H #define _QUAKE_KEYS_H // // these are the key numbers that should be passed to Key_Event // #define K_TAB 9 #define K_ENTER 13 #define K_ESCAPE 27 #define K_SPACE 32 // normal keys should be passed as lowercased ascii #define K_BACKSPACE 127 #define K_UPARROW 128 #define K_DOWNARROW 129 #define K_LEFTARROW 130 #define K_RIGHTARROW 131 #define K_ALT 132 #define K_CTRL 133 #define K_SHIFT 134 #define K_F1 135 #define K_F2 136 #define K_F3 137 #define K_F4 138 #define K_F5 139 #define K_F6 140 #define K_F7 141 #define K_F8 142 #define K_F9 143 #define K_F10 144 #define K_F11 145 #define K_F12 146 #define K_INS 147 #define K_DEL 148 #define K_PGDN 149 #define K_PGUP 150 #define K_HOME 151 #define K_END 152 #define K_KP_NUMLOCK 153 #define K_KP_SLASH 154 #define K_KP_STAR 155 #define K_KP_MINUS 156 #define K_KP_HOME 157 #define K_KP_UPARROW 158 #define K_KP_PGUP 159 #define K_KP_PLUS 160 #define K_KP_LEFTARROW 161 #define K_KP_5 162 #define K_KP_RIGHTARROW 163 #define K_KP_END 164 #define K_KP_DOWNARROW 165 #define K_KP_PGDN 166 #define K_KP_ENTER 167 #define K_KP_INS 168 #define K_KP_DEL 169 #define K_COMMAND 170 #define K_PAUSE 255 // // mouse buttons generate virtual keys // #define K_MOUSE1 200 #define K_MOUSE2 201 #define K_MOUSE3 202 // // joystick buttons // #define K_JOY1 203 #define K_JOY2 204 #define K_JOY3 205 #define K_JOY4 206 // aux keys are for multi-buttoned joysticks to generate so they can use // the normal binding process // aux29-32: reserved for the HAT (POV) switch motion #define K_AUX1 207 #define K_AUX2 208 #define K_AUX3 209 #define K_AUX4 210 #define K_AUX5 211 #define K_AUX6 212 #define K_AUX7 213 #define K_AUX8 214 #define K_AUX9 215 #define K_AUX10 216 #define K_AUX11 217 #define K_AUX12 218 #define K_AUX13 219 #define K_AUX14 220 #define K_AUX15 221 #define K_AUX16 222 #define K_AUX17 223 #define K_AUX18 224 #define K_AUX19 225 #define K_AUX20 226 #define K_AUX21 227 #define K_AUX22 228 #define K_AUX23 229 #define K_AUX24 230 #define K_AUX25 231 #define K_AUX26 232 #define K_AUX27 233 #define K_AUX28 234 #define K_AUX29 235 #define K_AUX30 236 #define K_AUX31 237 #define K_AUX32 238 // JACK: Intellimouse(c) Mouse Wheel Support #define K_MWHEELUP 239 #define K_MWHEELDOWN 240 // thumb buttons #define K_MOUSE4 241 #define K_MOUSE5 242 // SDL2 game controller keys #define K_LTHUMB 243 #define K_RTHUMB 244 #define K_LSHOULDER 245 #define K_RSHOULDER 246 #define K_ABUTTON 247 #define K_BBUTTON 248 #define K_XBUTTON 249 #define K_YBUTTON 250 #define K_LTRIGGER 251 #define K_RTRIGGER 252 #define MAX_KEYS 256 #define MAXCMDLINE 256 typedef enum {key_game, key_console, key_message, key_menu} keydest_t; extern keydest_t key_dest; extern char *keybindings[MAX_KEYS]; extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; extern int key_insert; extern double key_blinktime; extern qboolean chat_team; void Key_Init (void); void Key_ClearStates (void); void Key_UpdateForDest (void); void Key_BeginInputGrab (void); void Key_EndInputGrab (void); void Key_GetGrabbedInput (int *lastkey, int *lastchar); void Key_Event (int key, qboolean down); void Char_Event (int key); qboolean Key_TextEntry (void); void Key_SetBinding (int keynum, const char *binding); const char *Key_KeynumToString (int keynum); void Key_WriteBindings (FILE *f); void Key_EndChat (void); const char *Key_GetChatBuffer (void); int Key_GetChatMsgLen (void); void History_Init (void); void History_Shutdown (void); #endif /* _QUAKE_KEYS_H */ ���������������������������������������������������������������quakespasm-0.93.0/Quake/sys_sdl_unix.c��������������������������������������������������������������0000644�0000000�0000000�00000022474�12665410352�016425� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "arch_def.h" #include "quakedef.h" #include <sys/types.h> #include <errno.h> #include <unistd.h> #ifdef PLATFORM_OSX #include <libgen.h> /* dirname() and basename() */ #endif #include <sys/stat.h> #include <sys/time.h> #include <fcntl.h> #include <time.h> #ifdef DO_USERDIRS #include <pwd.h> #endif #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif qboolean isDedicated; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; #define MAX_HANDLES 32 /* johnfitz -- was 10 */ static FILE *sys_handles[MAX_HANDLES]; static int findhandle (void) { int i; for (i = 1; i < MAX_HANDLES; i++) { if (!sys_handles[i]) return i; } Sys_Error ("out of handles"); return -1; } long Sys_filelength (FILE *f) { long pos, end; pos = ftell (f); fseek (f, 0, SEEK_END); end = ftell (f); fseek (f, pos, SEEK_SET); return end; } int Sys_FileOpenRead (const char *path, int *hndl) { FILE *f; int i, retval; i = findhandle (); f = fopen(path, "rb"); if (!f) { *hndl = -1; retval = -1; } else { sys_handles[i] = f; *hndl = i; retval = Sys_filelength(f); } return retval; } int Sys_FileOpenWrite (const char *path) { FILE *f; int i; i = findhandle (); f = fopen(path, "wb"); if (!f) Sys_Error ("Error opening %s: %s", path, strerror(errno)); sys_handles[i] = f; return i; } void Sys_FileClose (int handle) { fclose (sys_handles[handle]); sys_handles[handle] = NULL; } void Sys_FileSeek (int handle, int position) { fseek (sys_handles[handle], position, SEEK_SET); } int Sys_FileRead (int handle, void *dest, int count) { return fread (dest, 1, count, sys_handles[handle]); } int Sys_FileWrite (int handle, const void *data, int count) { return fwrite (data, 1, count, sys_handles[handle]); } int Sys_FileTime (const char *path) { FILE *f; f = fopen(path, "rb"); if (f) { fclose(f); return 1; } return -1; } #if defined(__linux__) || defined(__sun) || defined(sun) || defined(_AIX) static int Sys_NumCPUs (void) { int numcpus = sysconf(_SC_NPROCESSORS_ONLN); return (numcpus < 1) ? 1 : numcpus; } #elif defined(PLATFORM_OSX) #include <sys/sysctl.h> #if !defined(HW_AVAILCPU) /* using an ancient SDK? */ #define HW_AVAILCPU 25 /* needs >= 10.2 */ #endif static int Sys_NumCPUs (void) { int numcpus; int mib[2]; size_t len; #if defined(_SC_NPROCESSORS_ONLN) /* needs >= 10.5 */ numcpus = sysconf(_SC_NPROCESSORS_ONLN); if (numcpus != -1) return (numcpus < 1) ? 1 : numcpus; #endif len = sizeof(numcpus); mib[0] = CTL_HW; mib[1] = HW_AVAILCPU; sysctl(mib, 2, &numcpus, &len, NULL, 0); if (sysctl(mib, 2, &numcpus, &len, NULL, 0) == -1) { mib[1] = HW_NCPU; if (sysctl(mib, 2, &numcpus, &len, NULL, 0) == -1) return 1; } return (numcpus < 1) ? 1 : numcpus; } #elif defined(__sgi) || defined(sgi) || defined(__sgi__) /* IRIX */ static int Sys_NumCPUs (void) { int numcpus = sysconf(_SC_NPROC_ONLN); if (numcpus < 1) numcpus = 1; return numcpus; } #elif defined(PLATFORM_BSD) #include <sys/sysctl.h> static int Sys_NumCPUs (void) { int numcpus; int mib[2]; size_t len; #if defined(_SC_NPROCESSORS_ONLN) numcpus = sysconf(_SC_NPROCESSORS_ONLN); if (numcpus != -1) return (numcpus < 1) ? 1 : numcpus; #endif len = sizeof(numcpus); mib[0] = CTL_HW; mib[1] = HW_NCPU; if (sysctl(mib, 2, &numcpus, &len, NULL, 0) == -1) return 1; return (numcpus < 1) ? 1 : numcpus; } #elif defined(__hpux) || defined(__hpux__) || defined(_hpux) #include <sys/mpctl.h> static int Sys_NumCPUs (void) { int numcpus = mpctl(MPC_GETNUMSPUS, NULL, NULL); return numcpus; } #else /* unknown OS */ static int Sys_NumCPUs (void) { return -2; } #endif static char cwd[MAX_OSPATH]; #ifdef DO_USERDIRS static char userdir[MAX_OSPATH]; #ifdef PLATFORM_OSX #define SYS_USERDIR "Library/Application Support/QuakeSpasm" #else #define SYS_USERDIR ".quakespasm" #endif static void Sys_GetUserdir (char *dst, size_t dstsize) { size_t n; const char *home_dir = NULL; struct passwd *pwent; pwent = getpwuid( getuid() ); if (pwent == NULL) perror("getpwuid"); else home_dir = pwent->pw_dir; if (home_dir == NULL) home_dir = getenv("HOME"); if (home_dir == NULL) Sys_Error ("Couldn't determine userspace directory"); /* what would be a maximum path for a file in the user's directory... * $HOME/SYS_USERDIR/game_dir/dirname1/dirname2/dirname3/filename.ext * still fits in the MAX_OSPATH == 256 definition, but just in case : */ n = strlen(home_dir) + strlen(SYS_USERDIR) + 50; if (n >= dstsize) Sys_Error ("Insufficient array size for userspace directory"); q_snprintf (dst, dstsize, "%s/%s", home_dir, SYS_USERDIR); } #endif /* DO_USERDIRS */ #ifdef PLATFORM_OSX static char *OSX_StripAppBundle (char *dir) { /* based on the ioquake3 project at icculus.org. */ static char osx_path[MAX_OSPATH]; q_strlcpy (osx_path, dir, sizeof(osx_path)); if (strcmp(basename(osx_path), "MacOS")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); if (strcmp(basename(osx_path), "Contents")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); if (!strstr(basename(osx_path), ".app")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); return osx_path; } static void Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (realpath(argv0, dst) == NULL) { perror("realpath"); if (getcwd(dst, dstsize - 1) == NULL) _fail: Sys_Error ("Couldn't determine current directory"); } else { /* strip off the binary name */ if (! (tmp = strdup (dst))) goto _fail; q_strlcpy (dst, dirname(tmp), dstsize); free (tmp); } tmp = OSX_StripAppBundle(dst); if (tmp != dst) q_strlcpy (dst, tmp, dstsize); } #else static void Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) Sys_Error ("Couldn't determine current directory"); tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && *tmp == '/') *tmp = 0; } } #endif void Sys_Init (void) { memset (cwd, 0, sizeof(cwd)); Sys_GetBasedir(host_parms->argv[0], cwd, sizeof(cwd)); host_parms->basedir = cwd; #ifndef DO_USERDIRS host_parms->userdir = host_parms->basedir; /* code elsewhere relies on this ! */ #else memset (userdir, 0, sizeof(userdir)); Sys_GetUserdir(userdir, sizeof(userdir)); Sys_mkdir (userdir); host_parms->userdir = userdir; #endif host_parms->numcpus = Sys_NumCPUs (); Sys_Printf("Detected %d CPUs.\n", host_parms->numcpus); } void Sys_mkdir (const char *path) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) { struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) rc = 0; } if (rc != 0) { rc = errno; Sys_Error("Unable to create directory %s: %s", path, strerror(rc)); } } static const char errortxt1[] = "\nERROR-OUT BEGIN\n\n"; static const char errortxt2[] = "\nQUAKE ERROR: "; void Sys_Error (const char *error, ...) { va_list argptr; char text[1024]; fputs (errortxt1, stderr); Host_Shutdown (); va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); fputs (errortxt2, stderr); fputs (text, stderr); fputs ("\n\n", stderr); if (!isDedicated) PL_ErrorDialog(text); exit (1); } void Sys_Printf (const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); vprintf(fmt, argptr); va_end(argptr); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } double Sys_DoubleTime (void) { return SDL_GetTicks() / 1000.0; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; fd_set set; struct timeval timeout; FD_ZERO (&set); FD_SET (0, &set); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; while (select (1, &set, NULL, NULL, &timeout)) { read (0, &c, 1); if (c == '\n' || c == '\r') { con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) con_text[textlen] = '\0'; else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_Printf("\nConsole input too long!\n"); break; } } return NULL; } void Sys_Sleep (unsigned long msecs) { /* usleep (msecs * 1000);*/ SDL_Delay (msecs); } void Sys_SendKeyEvents (void) { IN_Commands(); //ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage IN_SendKeyEvents(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/menu.h����������������������������������������������������������������������0000644�0000000�0000000�00000003327�12415301271�014637� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_MENU_H #define _QUAKE_MENU_H enum m_state_e { m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, m_lanconfig, m_gameoptions, m_search, m_slist }; extern enum m_state_e m_state; extern enum m_state_e m_return_state; extern qboolean m_entersound; // // menus // void M_Init (void); void M_Keydown (int key); void M_Charinput (int key); qboolean M_TextEntry (void); void M_ToggleMenu_f (void); void M_Menu_Main_f (void); void M_Menu_Options_f (void); void M_Menu_Quit_f (void); void M_Print (int cx, int cy, const char *str); void M_PrintWhite (int cx, int cy, const char *str); void M_Draw (void); void M_DrawCharacter (int cx, int line, int num); void M_DrawPic (int x, int y, qpic_t *pic); void M_DrawTransPic (int x, int y, qpic_t *pic); void M_DrawCheckbox (int x, int y, int on); #endif /* _QUAKE_MENU_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_bsd.c�������������������������������������������������������������������0000644�0000000�0000000�00000004146�12407762022�015312� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" #include "net_loop.h" net_driver_t net_drivers[] = { { "Loopback", false, Loop_Init, Loop_Listen, Loop_SearchForHosts, Loop_Connect, Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown }, { "Datagram", false, Datagram_Init, Datagram_Listen, Datagram_SearchForHosts, Datagram_Connect, Datagram_CheckNewConnections, Datagram_GetMessage, Datagram_SendMessage, Datagram_SendUnreliableMessage, Datagram_CanSendMessage, Datagram_CanSendUnreliableMessage, Datagram_Close, Datagram_Shutdown } }; const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0])); #include "net_udp.h" net_landriver_t net_landrivers[] = { { "UDP", false, 0, UDP_Init, UDP_Shutdown, UDP_Listen, UDP_OpenSocket, UDP_CloseSocket, UDP_Connect, UDP_CheckNewConnections, UDP_Read, UDP_Write, UDP_Broadcast, UDP_AddrToString, UDP_StringToAddr, UDP_GetSocketAddr, UDP_GetNameFromAddr, UDP_GetAddrFromName, UDP_AddrCompare, UDP_GetSocketPort, UDP_SetSocketPort } }; const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0])); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sv_move.c�������������������������������������������������������������������0000644�0000000�0000000�00000022364�13136721621�015354� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_move.c -- monster movement #include "quakedef.h" #define STEPSIZE 18 /* ============= SV_CheckBottom Returns false if any part of the bottom of the entity is off an edge that is not a staircase. ============= */ int c_yes, c_no; qboolean SV_CheckBottom (edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = x ? maxs[0] : mins[0]; start[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (start) != CONTENTS_SOLID) goto realcheck; } c_yes++; return true; // we got out easy realcheck: c_no++; // // check it for real... // start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*STEPSIZE; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction == 1.0) return false; mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) return false; } c_yes++; return true; } /* ============= SV_movestep Called by monster program code. The move will be adjusted for slopes and stairs, but if the move isn't possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall ============= */ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; edict_t *enemy; // try the move VectorCopy (ent->v.origin, oldorg); VectorAdd (ent->v.origin, move, neworg); // flying monsters don't step up if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) { // try one move with vertical motion, then one without for (i=0 ; i<2 ; i++) { VectorAdd (ent->v.origin, move, neworg); enemy = PROG_TO_EDICT(ent->v.enemy); if (i == 0 && enemy != sv.edicts) { dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; if (dz > 40) neworg[2] -= 8; if (dz < 30) neworg[2] += 8; } trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (trace.fraction == 1) { if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water VectorCopy (trace.endpos, ent->v.origin); if (relink) SV_LinkEdict (ent, true); return true; } if (enemy == sv.edicts) break; } return false; } // push down from a step height above the wished position neworg[2] += STEPSIZE; VectorCopy (neworg, end); end[2] -= STEPSIZE*2; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) return false; if (trace.startsolid) { neworg[2] -= STEPSIZE; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid || trace.startsolid) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( (int)ent->v.flags & FL_PARTIALGROUND ) { VectorAdd (ent->v.origin, move, ent->v.origin); if (relink) SV_LinkEdict (ent, true); ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // Con_Printf ("fall down\n"); return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->v.origin); if (!SV_CheckBottom (ent)) { if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } VectorCopy (oldorg, ent->v.origin); return false; } if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // Con_Printf ("back on ground\n"); ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; } ent->v.groundentity = EDICT_TO_PROG(trace.ent); // the move is ok if (relink) SV_LinkEdict (ent, true); return true; } //============================================================================ /* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ void PF_changeyaw (void); qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; ent->v.ideal_yaw = yaw; PF_changeyaw(); yaw = yaw*M_PI*2 / 360; move[0] = cos(yaw)*dist; move[1] = sin(yaw)*dist; move[2] = 0; VectorCopy (ent->v.origin, oldorigin); if (SV_movestep (ent, move, false)) { delta = ent->v.angles[YAW] - ent->v.ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->v.origin); } SV_LinkEdict (ent, true); return true; } SV_LinkEdict (ent, true); return false; } /* ====================== SV_FixCheckBottom ====================== */ void SV_FixCheckBottom (edict_t *ent) { // Con_Printf ("SV_FixCheckBottom\n"); ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; } /* ================ SV_NewChaseDir ================ */ #define DI_NODIR -1 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax,deltay; float d[3]; float tdir, olddir, turnaround; olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); turnaround = anglemod(olddir - 180); deltax = enemy->v.origin[0] - actor->v.origin[0]; deltay = enemy->v.origin[1] - actor->v.origin[1]; if (deltax>10) d[1]= 0; else if (deltax<-10) d[1]= 180; else d[1]= DI_NODIR; if (deltay<-10) d[2]= 270; else if (deltay>10) d[2]= 90; else d[2]= DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if ( ((rand()&3) & 1) || abs((int)deltay)>abs((int)deltax)) // ericw -- explicit int cast to suppress clang suggestion to use fabsf { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]!=DI_NODIR && d[1]!=turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2]!=DI_NODIR && d[2]!=turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand()&1) /*randomly determine direction of search*/ { for (tdir=0 ; tdir<=315 ; tdir += 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } else { for (tdir=315 ; tdir >=0 ; tdir -= 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->v.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!SV_CheckBottom (actor)) SV_FixCheckBottom (actor); } /* ====================== SV_CloseEnough ====================== */ qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; for (i=0 ; i<3 ; i++) { if (goal->v.absmin[i] > ent->v.absmax[i] + dist) return false; if (goal->v.absmax[i] < ent->v.absmin[i] - dist) return false; } return true; } /* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; ent = PROG_TO_EDICT(pr_global_struct->self); goal = PROG_TO_EDICT(ent->v.goalentity); dist = G_FLOAT(OFS_PARM0); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) return; // bump around... if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) { SV_NewChaseDir (ent, goal, dist); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mem.c�������������������������������������������������������������������0000644�0000000�0000000�00000016051�12530666055�015322� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2011 O. Sezer <sezero@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_mem.c: sound caching #include "quakedef.h" /* ================ ResampleSfx ================ */ static void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) { int outcount; int srcsample; float stepscale; int i; int sample, samplefrac, fracstep; sfxcache_t *sc; sc = (sfxcache_t *) Cache_Check (&sfx->cache); if (!sc) return; stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 outcount = sc->length / stepscale; sc->length = outcount; if (sc->loopstart != -1) sc->loopstart = sc->loopstart / stepscale; sc->speed = shm->speed; if (loadas8bit.value) sc->width = 1; else sc->width = inwidth; sc->stereo = 0; // resample / decimate to the current source rate if (stepscale == 1 && inwidth == 1 && sc->width == 1) { // fast special case for (i = 0; i < outcount; i++) ((signed char *)sc->data)[i] = (int)( (unsigned char)(data[i]) - 128); } else { // general case samplefrac = 0; fracstep = stepscale*256; for (i = 0; i < outcount; i++) { srcsample = samplefrac >> 8; samplefrac += fracstep; if (inwidth == 2) sample = LittleShort ( ((short *)data)[srcsample] ); else sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; if (sc->width == 2) ((short *)sc->data)[i] = sample; else ((signed char *)sc->data)[i] = sample >> 8; } } } //============================================================================= /* ============== S_LoadSound ============== */ sfxcache_t *S_LoadSound (sfx_t *s) { char namebuffer[256]; byte *data; wavinfo_t info; int len; float stepscale; sfxcache_t *sc; byte stackbuf[1*1024]; // avoid dirtying the cache heap // see if still in memory sc = (sfxcache_t *) Cache_Check (&s->cache); if (sc) return sc; // Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); // load it in q_strlcpy(namebuffer, "sound/", sizeof(namebuffer)); q_strlcat(namebuffer, s->name, sizeof(namebuffer)); // Con_Printf ("loading %s\n",namebuffer); data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), NULL); if (!data) { Con_Printf ("Couldn't load %s\n", namebuffer); return NULL; } info = GetWavinfo (s->name, data, com_filesize); if (info.channels != 1) { Con_Printf ("%s is a stereo sample\n",s->name); return NULL; } if (info.width != 1 && info.width != 2) { Con_Printf("%s is not 8 or 16 bit\n", s->name); return NULL; } stepscale = (float)info.rate / shm->speed; len = info.samples / stepscale; len = len * info.width * info.channels; if (info.samples == 0 || len == 0) { Con_Printf("%s has zero samples\n", s->name); return NULL; } sc = (sfxcache_t *) Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); if (!sc) return NULL; sc->length = info.samples; sc->loopstart = info.loopstart; sc->speed = info.rate; sc->width = info.width; sc->stereo = info.channels; ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); return sc; } /* =============================================================================== WAV loading =============================================================================== */ static byte *data_p; static byte *iff_end; static byte *last_chunk; static byte *iff_data; static int iff_chunk_len; static short GetLittleShort (void) { short val = 0; val = *data_p; val = val + (*(data_p+1)<<8); data_p += 2; return val; } static int GetLittleLong (void) { int val = 0; val = *data_p; val = val + (*(data_p+1)<<8); val = val + (*(data_p+2)<<16); val = val + (*(data_p+3)<<24); data_p += 4; return val; } static void FindNextChunk (const char *name) { while (1) { // Need at least 8 bytes for a chunk if (last_chunk + 8 >= iff_end) { data_p = NULL; return; } data_p = last_chunk + 4; iff_chunk_len = GetLittleLong(); if (iff_chunk_len < 0 || iff_chunk_len > iff_end - data_p) { data_p = NULL; Con_DPrintf2("bad \"%s\" chunk length (%d)\n", name, iff_chunk_len); return; } last_chunk = data_p + ((iff_chunk_len + 1) & ~1); data_p -= 8; if (!Q_strncmp((char *)data_p, name, 4)) return; } } static void FindChunk (const char *name) { last_chunk = iff_data; FindNextChunk (name); } #if 0 static void DumpChunks (void) { char str[5]; str[4] = 0; data_p = iff_data; do { memcpy (str, data_p, 4); data_p += 4; iff_chunk_len = GetLittleLong(); Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); data_p += (iff_chunk_len + 1) & ~1; } while (data_p < iff_end); } #endif /* ============ GetWavinfo ============ */ wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength) { wavinfo_t info; int i; int format; int samples; memset (&info, 0, sizeof(info)); if (!wav) return info; iff_data = wav; iff_end = wav + wavlength; // find "RIFF" chunk FindChunk("RIFF"); if (!(data_p && !Q_strncmp((char *)data_p + 8, "WAVE", 4))) { Con_Printf("%s missing RIFF/WAVE chunks\n", name); return info; } // get "fmt " chunk iff_data = data_p + 12; #if 0 DumpChunks (); #endif FindChunk("fmt "); if (!data_p) { Con_Printf("%s is missing fmt chunk\n", name); return info; } data_p += 8; format = GetLittleShort(); if (format != WAV_FORMAT_PCM) { Con_Printf("%s is not Microsoft PCM format\n", name); return info; } info.channels = GetLittleShort(); info.rate = GetLittleLong(); data_p += 4 + 2; i = GetLittleShort(); if (i != 8 && i != 16) return info; info.width = i / 8; // get cue chunk FindChunk("cue "); if (data_p) { data_p += 32; info.loopstart = GetLittleLong(); // Con_Printf("loopstart=%d\n", sfx->loopstart); // if the next chunk is a LIST chunk, look for a cue length marker FindNextChunk ("LIST"); if (data_p) { if (!strncmp((char *)data_p + 28, "mark", 4)) { // this is not a proper parse, but it works with cooledit... data_p += 24; i = GetLittleLong(); // samples in loop info.samples = info.loopstart + i; // Con_Printf("looped length: %i\n", i); } } } else info.loopstart = -1; // find data chunk FindChunk("data"); if (!data_p) { Con_Printf("%s is missing data chunk\n", name); return info; } data_p += 4; samples = GetLittleLong() / info.width; if (info.samples) { if (samples < info.samples) Sys_Error ("%s has a bad loop length", name); } else info.samples = samples; info.dataofs = data_p - wav; return info; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_loop.c������������������������������������������������������������������0000644�0000000�0000000�00000012422�12407762022�015507� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_loop.h" static qboolean localconnectpending = false; static qsocket_t *loop_client = NULL; static qsocket_t *loop_server = NULL; int Loop_Init (void) { if (cls.state == ca_dedicated) return -1; return 0; } void Loop_Shutdown (void) { } void Loop_Listen (qboolean state) { } void Loop_SearchForHosts (qboolean xmit) { if (!sv.active) return; hostCacheCount = 1; if (Q_strcmp(hostname.string, "UNNAMED") == 0) Q_strcpy(hostcache[0].name, "local"); else Q_strcpy(hostcache[0].name, hostname.string); Q_strcpy(hostcache[0].map, sv.name); hostcache[0].users = net_activeconnections; hostcache[0].maxusers = svs.maxclients; hostcache[0].driver = net_driverlevel; Q_strcpy(hostcache[0].cname, "local"); } qsocket_t *Loop_Connect (const char *host) { if (Q_strcmp(host,"local") != 0) return NULL; localconnectpending = true; if (!loop_client) { if ((loop_client = NET_NewQSocket ()) == NULL) { Con_Printf("Loop_Connect: no qsocket available\n"); return NULL; } Q_strcpy (loop_client->address, "localhost"); } loop_client->receiveMessageLength = 0; loop_client->sendMessageLength = 0; loop_client->canSend = true; if (!loop_server) { if ((loop_server = NET_NewQSocket ()) == NULL) { Con_Printf("Loop_Connect: no qsocket available\n"); return NULL; } Q_strcpy (loop_server->address, "LOCAL"); } loop_server->receiveMessageLength = 0; loop_server->sendMessageLength = 0; loop_server->canSend = true; loop_client->driverdata = (void *)loop_server; loop_server->driverdata = (void *)loop_client; return loop_client; } qsocket_t *Loop_CheckNewConnections (void) { if (!localconnectpending) return NULL; localconnectpending = false; loop_server->sendMessageLength = 0; loop_server->receiveMessageLength = 0; loop_server->canSend = true; loop_client->sendMessageLength = 0; loop_client->receiveMessageLength = 0; loop_client->canSend = true; return loop_server; } static int IntAlign(int value) { return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1)); } int Loop_GetMessage (qsocket_t *sock) { int ret; int length; if (sock->receiveMessageLength == 0) return 0; ret = sock->receiveMessage[0]; length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8); // alignment byte skipped here SZ_Clear (&net_message); SZ_Write (&net_message, &sock->receiveMessage[4], length); length = IntAlign(length + 4); sock->receiveMessageLength -= length; if (sock->receiveMessageLength) memmove (sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength); if (sock->driverdata && ret == 1) ((qsocket_t *)sock->driverdata)->canSend = true; return ret; } int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE) Sys_Error("Loop_SendMessage: overflow"); buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 1; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message Q_memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); sock->canSend = false; return 1; } int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE) return 0; buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 2; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message Q_memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); return 1; } qboolean Loop_CanSendMessage (qsocket_t *sock) { if (!sock->driverdata) return false; return sock->canSend; } qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock) { return true; } void Loop_Close (qsocket_t *sock) { if (sock->driverdata) ((qsocket_t *)sock->driverdata)->driverdata = NULL; sock->receiveMessageLength = 0; sock->sendMessageLength = 0; sock->canSend = true; if (sock == loop_client) loop_client = NULL; else loop_server = NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cdaudio.h�������������������������������������������������������������������0000644�0000000�0000000�00000002116�12407762022�015304� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __CDAUDIO_H #define __CDAUDIO_H int CDAudio_Init (void); int CDAudio_Play (byte track, qboolean looping); /* returns 0 for success, -1 for failure. */ void CDAudio_Stop (void); void CDAudio_Pause (void); void CDAudio_Resume (void); void CDAudio_Shutdown (void); void CDAudio_Update (void); #endif /* __CDAUDIO_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mikmod.h����������������������������������������������������������������0000644�0000000�0000000�00000000353�12220541170�016011� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* module tracker decoding support using libmikmod */ #if !defined(_SND_MIKMOD_H_) #define _SND_MIKMOD_H_ #if defined(USE_CODEC_MIKMOD) extern snd_codec_t mikmod_codec; #endif /* USE_CODEC_MIKMOD */ #endif /* ! _SND_MIKMOD_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/protocol.h������������������������������������������������������������������0000644�0000000�0000000�00000022743�12733153600�015543� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_PROTOCOL_H #define _QUAKE_PROTOCOL_H // protocol.h -- communications protocols #define PROTOCOL_NETQUAKE 15 //johnfitz -- standard quake protocol #define PROTOCOL_FITZQUAKE 666 //johnfitz -- added new protocol for fitzquake 0.85 #define PROTOCOL_RMQ 999 // PROTOCOL_RMQ protocol flags #define PRFL_SHORTANGLE (1 << 1) #define PRFL_FLOATANGLE (1 << 2) #define PRFL_24BITCOORD (1 << 3) #define PRFL_FLOATCOORD (1 << 4) #define PRFL_EDICTSCALE (1 << 5) #define PRFL_ALPHASANITY (1 << 6) // cleanup insanity with alpha #define PRFL_INT32COORD (1 << 7) #define PRFL_MOREFLAGS (1 << 31) // not supported // if the high bit of the servercmd is set, the low bits are fast update flags: #define U_MOREBITS (1<<0) #define U_ORIGIN1 (1<<1) #define U_ORIGIN2 (1<<2) #define U_ORIGIN3 (1<<3) #define U_ANGLE2 (1<<4) #define U_STEP (1<<5) //johnfitz -- was U_NOLERP, renamed since it's only used for MOVETYPE_STEP #define U_FRAME (1<<6) #define U_SIGNAL (1<<7) // just differentiates from other updates // svc_update can pass all of the fast update bits, plus more #define U_ANGLE1 (1<<8) #define U_ANGLE3 (1<<9) #define U_MODEL (1<<10) #define U_COLORMAP (1<<11) #define U_SKIN (1<<12) #define U_EFFECTS (1<<13) #define U_LONGENTITY (1<<14) //johnfitz -- PROTOCOL_FITZQUAKE -- new bits #define U_EXTEND1 (1<<15) #define U_ALPHA (1<<16) // 1 byte, uses ENTALPHA_ENCODE, not sent if equal to baseline #define U_FRAME2 (1<<17) // 1 byte, this is .frame & 0xFF00 (second byte) #define U_MODEL2 (1<<18) // 1 byte, this is .modelindex & 0xFF00 (second byte) #define U_LERPFINISH (1<<19) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 0.1, this is ent->v.nextthink - sv.time, used for lerping #define U_SCALE (1<<20) // 1 byte, for PROTOCOL_RMQ PRFL_EDICTSCALE, currently read but ignored #define U_UNUSED21 (1<<21) #define U_UNUSED22 (1<<22) #define U_EXTEND2 (1<<23) // another byte to follow, future expansion //johnfitz //johnfitz -- PROTOCOL_NEHAHRA transparency #define U_TRANS (1<<15) //johnfitz #define SU_VIEWHEIGHT (1<<0) #define SU_IDEALPITCH (1<<1) #define SU_PUNCH1 (1<<2) #define SU_PUNCH2 (1<<3) #define SU_PUNCH3 (1<<4) #define SU_VELOCITY1 (1<<5) #define SU_VELOCITY2 (1<<6) #define SU_VELOCITY3 (1<<7) #define SU_UNUSED8 (1<<8) //AVAILABLE BIT #define SU_ITEMS (1<<9) #define SU_ONGROUND (1<<10) // no data follows, the bit is it #define SU_INWATER (1<<11) // no data follows, the bit is it #define SU_WEAPONFRAME (1<<12) #define SU_ARMOR (1<<13) #define SU_WEAPON (1<<14) //johnfitz -- PROTOCOL_FITZQUAKE -- new bits #define SU_EXTEND1 (1<<15) // another byte to follow #define SU_WEAPON2 (1<<16) // 1 byte, this is .weaponmodel & 0xFF00 (second byte) #define SU_ARMOR2 (1<<17) // 1 byte, this is .armorvalue & 0xFF00 (second byte) #define SU_AMMO2 (1<<18) // 1 byte, this is .currentammo & 0xFF00 (second byte) #define SU_SHELLS2 (1<<19) // 1 byte, this is .ammo_shells & 0xFF00 (second byte) #define SU_NAILS2 (1<<20) // 1 byte, this is .ammo_nails & 0xFF00 (second byte) #define SU_ROCKETS2 (1<<21) // 1 byte, this is .ammo_rockets & 0xFF00 (second byte) #define SU_CELLS2 (1<<22) // 1 byte, this is .ammo_cells & 0xFF00 (second byte) #define SU_EXTEND2 (1<<23) // another byte to follow #define SU_WEAPONFRAME2 (1<<24) // 1 byte, this is .weaponframe & 0xFF00 (second byte) #define SU_WEAPONALPHA (1<<25) // 1 byte, this is alpha for weaponmodel, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT #define SU_UNUSED26 (1<<26) #define SU_UNUSED27 (1<<27) #define SU_UNUSED28 (1<<28) #define SU_UNUSED29 (1<<29) #define SU_UNUSED30 (1<<30) #define SU_EXTEND3 (1<<31) // another byte to follow, future expansion //johnfitz // a sound with no channel is a local only sound #define SND_VOLUME (1<<0) // a byte #define SND_ATTENUATION (1<<1) // a byte #define SND_LOOPING (1<<2) // a long #define DEFAULT_SOUND_PACKET_VOLUME 255 #define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 //johnfitz -- PROTOCOL_FITZQUAKE -- new bits #define SND_LARGEENTITY (1<<3) // a short + byte (instead of just a short) #define SND_LARGESOUND (1<<4) // a short soundindex (instead of a byte) //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE -- flags for entity baseline messages #define B_LARGEMODEL (1<<0) // modelindex is short instead of byte #define B_LARGEFRAME (1<<1) // frame is short instead of byte #define B_ALPHA (1<<2) // 1 byte, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE -- alpha encoding #define ENTALPHA_DEFAULT 0 //entity's alpha is "default" (i.e. water obeys r_wateralpha) -- must be zero so zeroed out memory works #define ENTALPHA_ZERO 1 //entity is invisible (lowest possible alpha) #define ENTALPHA_ONE 255 //entity is fully opaque (highest possible alpha) #define ENTALPHA_ENCODE(a) (((a)==0)?ENTALPHA_DEFAULT:Q_rint(CLAMP(1,(a)*254.0f+1,255))) //server convert to byte to send to client #define ENTALPHA_DECODE(a) (((a)==ENTALPHA_DEFAULT)?1.0f:((float)(a)-1)/(254)) //client convert to float for rendering #define ENTALPHA_TOSAVE(a) (((a)==ENTALPHA_DEFAULT)?0.0f:(((a)==ENTALPHA_ZERO)?-1.0f:((float)(a)-1)/(254))) //server convert to float for savegame //johnfitz // defaults for clientinfo messages #define DEFAULT_VIEWHEIGHT 22 // game types sent by serverinfo // these determine which intermission screen plays #define GAME_COOP 0 #define GAME_DEATHMATCH 1 //================== // note that there are some defs.qc that mirror to these numbers // also related to svc_strings[] in cl_parse //================== // // server to client // #define svc_bad 0 #define svc_nop 1 #define svc_disconnect 2 #define svc_updatestat 3 // [byte] [long] #define svc_version 4 // [long] server version #define svc_setview 5 // [short] entity number #define svc_sound 6 // <see code> #define svc_time 7 // [float] server time #define svc_print 8 // [string] null terminated string #define svc_stufftext 9 // [string] stuffed into client's console buffer // the string should be \n terminated #define svc_setangle 10 // [angle3] set the view angle to this absolute value #define svc_serverinfo 11 // [long] version // [string] signon string // [string]..[0]model cache // [string]...[0]sounds cache #define svc_lightstyle 12 // [byte] [string] #define svc_updatename 13 // [byte] [string] #define svc_updatefrags 14 // [byte] [short] #define svc_clientdata 15 // <shortbits + data> #define svc_stopsound 16 // <see code> #define svc_updatecolors 17 // [byte] [byte] #define svc_particle 18 // [vec3] <variable> #define svc_damage 19 #define svc_spawnstatic 20 //#define svc_spawnbinary 21 #define svc_spawnbaseline 22 #define svc_temp_entity 23 #define svc_setpause 24 // [byte] on / off #define svc_signonnum 25 // [byte] used for the signon sequence #define svc_centerprint 26 // [string] to put in center of the screen #define svc_killedmonster 27 #define svc_foundsecret 28 #define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten #define svc_intermission 30 // [string] music #define svc_finale 31 // [string] music [string] text #define svc_cdtrack 32 // [byte] track [byte] looptrack #define svc_sellscreen 33 #define svc_cutscene 34 //johnfitz -- PROTOCOL_FITZQUAKE -- new server messages #define svc_skybox 37 // [string] name #define svc_bf 40 #define svc_fog 41 // [byte] density [byte] red [byte] green [byte] blue [float] time #define svc_spawnbaseline2 42 // support for large modelindex, large framenum, alpha, using flags #define svc_spawnstatic2 43 // support for large modelindex, large framenum, alpha, using flags #define svc_spawnstaticsound2 44 // [coord3] [short] samp [byte] vol [byte] aten //johnfitz // // client to server // #define clc_bad 0 #define clc_nop 1 #define clc_disconnect 2 #define clc_move 3 // [usercmd_t] #define clc_stringcmd 4 // [string] message // // temp entity events // #define TE_SPIKE 0 #define TE_SUPERSPIKE 1 #define TE_GUNSHOT 2 #define TE_EXPLOSION 3 #define TE_TAREXPLOSION 4 #define TE_LIGHTNING1 5 #define TE_LIGHTNING2 6 #define TE_WIZSPIKE 7 #define TE_KNIGHTSPIKE 8 #define TE_LIGHTNING3 9 #define TE_LAVASPLASH 10 #define TE_TELEPORT 11 #define TE_EXPLOSION2 12 // PGM 01/21/97 #define TE_BEAM 13 // PGM 01/21/97 typedef struct { vec3_t origin; vec3_t angles; unsigned short modelindex; //johnfitz -- was int unsigned short frame; //johnfitz -- was int unsigned char colormap; //johnfitz -- was int unsigned char skin; //johnfitz -- was int unsigned char alpha; //johnfitz -- added int effects; } entity_state_t; typedef struct { vec3_t viewangles; // intended velocities float forwardmove; float sidemove; float upmove; } usercmd_t; #endif /* _QUAKE_PROTOCOL_H */ �����������������������������quakespasm-0.93.0/Quake/gl_draw.c�������������������������������������������������������������������0000644�0000000�0000000�00000044503�13157425640�015321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // draw.c -- 2d drawing #include "quakedef.h" //extern unsigned char d_15to8table[65536]; //johnfitz -- never used cvar_t scr_conalpha = {"scr_conalpha", "0.5", CVAR_ARCHIVE}; //johnfitz qpic_t *draw_disc; qpic_t *draw_backtile; gltexture_t *char_texture; //johnfitz qpic_t *pic_ovr, *pic_ins; //johnfitz -- new cursor handling qpic_t *pic_nul; //johnfitz -- for missing gfx, don't crash //johnfitz -- new pics byte pic_ovr_data[8][8] = { {255,255,255,255,255,255,255,255}, {255, 15, 15, 15, 15, 15, 15,255}, {255, 15, 15, 15, 15, 15, 15, 2}, {255, 15, 15, 15, 15, 15, 15, 2}, {255, 15, 15, 15, 15, 15, 15, 2}, {255, 15, 15, 15, 15, 15, 15, 2}, {255, 15, 15, 15, 15, 15, 15, 2}, {255,255, 2, 2, 2, 2, 2, 2}, }; byte pic_ins_data[9][8] = { { 15, 15,255,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, { 15, 15, 2,255,255,255,255,255}, {255, 2, 2,255,255,255,255,255}, }; byte pic_nul_data[8][8] = { {252,252,252,252, 0, 0, 0, 0}, {252,252,252,252, 0, 0, 0, 0}, {252,252,252,252, 0, 0, 0, 0}, {252,252,252,252, 0, 0, 0, 0}, { 0, 0, 0, 0,252,252,252,252}, { 0, 0, 0, 0,252,252,252,252}, { 0, 0, 0, 0,252,252,252,252}, { 0, 0, 0, 0,252,252,252,252}, }; byte pic_stipple_data[8][8] = { {255, 0, 0, 0,255, 0, 0, 0}, { 0, 0,255, 0, 0, 0,255, 0}, {255, 0, 0, 0,255, 0, 0, 0}, { 0, 0,255, 0, 0, 0,255, 0}, {255, 0, 0, 0,255, 0, 0, 0}, { 0, 0,255, 0, 0, 0,255, 0}, {255, 0, 0, 0,255, 0, 0, 0}, { 0, 0,255, 0, 0, 0,255, 0}, }; byte pic_crosshair_data[8][8] = { {255,255,255,255,255,255,255,255}, {255,255,255, 8, 9,255,255,255}, {255,255,255, 6, 8, 2,255,255}, {255, 6, 8, 8, 6, 8, 8,255}, {255,255, 2, 8, 8, 2, 2, 2}, {255,255,255, 7, 8, 2,255,255}, {255,255,255,255, 2, 2,255,255}, {255,255,255,255,255,255,255,255}, }; //johnfitz typedef struct { gltexture_t *gltexture; float sl, tl, sh, th; } glpic_t; canvastype currentcanvas = CANVAS_NONE; //johnfitz -- for GL_SetCanvas //============================================================================== // // PIC CACHING // //============================================================================== typedef struct cachepic_s { char name[MAX_QPATH]; qpic_t pic; byte padding[32]; // for appended glpic } cachepic_t; #define MAX_CACHED_PICS 128 cachepic_t menu_cachepics[MAX_CACHED_PICS]; int menu_numcachepics; byte menuplyr_pixels[4096]; // scrap allocation // Allocate all the little status bar obejcts into a single texture // to crutch up stupid hardware / drivers #define MAX_SCRAPS 2 #define BLOCK_WIDTH 256 #define BLOCK_HEIGHT 256 int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT]; //johnfitz -- removed *4 after BLOCK_HEIGHT qboolean scrap_dirty; gltexture_t *scrap_textures[MAX_SCRAPS]; //johnfitz /* ================ Scrap_AllocBlock returns an index into scrap_texnums[] and the position inside it ================ */ int Scrap_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++) { best = BLOCK_HEIGHT; for (i=0 ; i<BLOCK_WIDTH-w ; i++) { best2 = 0; for (j=0 ; j<w ; j++) { if (scrap_allocated[texnum][i+j] >= best) break; if (scrap_allocated[texnum][i+j] > best2) best2 = scrap_allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; i<w ; i++) scrap_allocated[texnum][*x + i] = best + h; return texnum; } Sys_Error ("Scrap_AllocBlock: full"); //johnfitz -- correct function name return 0; //johnfitz -- shut up compiler } /* ================ Scrap_Upload -- johnfitz -- now uses TexMgr ================ */ void Scrap_Upload (void) { char name[8]; int i; for (i=0; i<MAX_SCRAPS; i++) { sprintf (name, "scrap%i", i); scrap_textures[i] = TexMgr_LoadImage (NULL, name, BLOCK_WIDTH, BLOCK_HEIGHT, SRC_INDEXED, scrap_texels[i], "", (src_offset_t)scrap_texels[i], TEXPREF_ALPHA | TEXPREF_OVERWRITE | TEXPREF_NOPICMIP); } scrap_dirty = false; } /* ================ Draw_PicFromWad ================ */ qpic_t *Draw_PicFromWad (const char *name) { qpic_t *p; glpic_t gl; src_offset_t offset; //johnfitz p = (qpic_t *) W_GetLumpName (name); if (!p) return pic_nul; //johnfitz // load little ones into the scrap if (p->width < 64 && p->height < 64) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock (p->width, p->height, &x, &y); scrap_dirty = true; k = 0; for (i=0 ; i<p->height ; i++) { for (j=0 ; j<p->width ; j++, k++) scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; } gl.gltexture = scrap_textures[texnum]; //johnfitz -- changed to an array //johnfitz -- no longer go from 0.01 to 0.99 gl.sl = x/(float)BLOCK_WIDTH; gl.sh = (x+p->width)/(float)BLOCK_WIDTH; gl.tl = y/(float)BLOCK_WIDTH; gl.th = (y+p->height)/(float)BLOCK_WIDTH; } else { char texturename[64]; //johnfitz q_snprintf (texturename, sizeof(texturename), "%s:%s", WADFILENAME, name); //johnfitz offset = (src_offset_t)p - (src_offset_t)wad_base + sizeof(int)*2; //johnfitz gl.gltexture = TexMgr_LoadImage (NULL, texturename, p->width, p->height, SRC_INDEXED, p->data, WADFILENAME, offset, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr gl.sl = 0; gl.sh = (float)p->width/(float)TexMgr_PadConditional(p->width); //johnfitz gl.tl = 0; gl.th = (float)p->height/(float)TexMgr_PadConditional(p->height); //johnfitz } memcpy (p->data, &gl, sizeof(glpic_t)); return p; } /* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (const char *path) { cachepic_t *pic; int i; qpic_t *dat; glpic_t gl; for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++) { if (!strcmp (path, pic->name)) return &pic->pic; } if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); menu_numcachepics++; strcpy (pic->name, path); // // load the pic from disk // dat = (qpic_t *)COM_LoadTempFile (path, NULL); if (!dat) Sys_Error ("Draw_CachePic: failed to load %s", path); SwapPic (dat); // HACK HACK HACK --- we need to keep the bytes for // the translatable player picture just for the menu // configuration dialog if (!strcmp (path, "gfx/menuplyr.lmp")) memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); pic->pic.width = dat->width; pic->pic.height = dat->height; gl.gltexture = TexMgr_LoadImage (NULL, path, dat->width, dat->height, SRC_INDEXED, dat->data, path, sizeof(int)*2, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr gl.sl = 0; gl.sh = (float)dat->width/(float)TexMgr_PadConditional(dat->width); //johnfitz gl.tl = 0; gl.th = (float)dat->height/(float)TexMgr_PadConditional(dat->height); //johnfitz memcpy (pic->pic.data, &gl, sizeof(glpic_t)); return &pic->pic; } /* ================ Draw_MakePic -- johnfitz -- generate pics from internal data ================ */ qpic_t *Draw_MakePic (const char *name, int width, int height, byte *data) { int flags = TEXPREF_NEAREST | TEXPREF_ALPHA | TEXPREF_PERSIST | TEXPREF_NOPICMIP | TEXPREF_PAD; qpic_t *pic; glpic_t gl; pic = (qpic_t *) Hunk_Alloc (sizeof(qpic_t) - 4 + sizeof (glpic_t)); pic->width = width; pic->height = height; gl.gltexture = TexMgr_LoadImage (NULL, name, width, height, SRC_INDEXED, data, "", (src_offset_t)data, flags); gl.sl = 0; gl.sh = (float)width/(float)TexMgr_PadConditional(width); gl.tl = 0; gl.th = (float)height/(float)TexMgr_PadConditional(height); memcpy (pic->data, &gl, sizeof(glpic_t)); return pic; } //============================================================================== // // INIT // //============================================================================== /* =============== Draw_LoadPics -- johnfitz =============== */ void Draw_LoadPics (void) { byte *data; src_offset_t offset; data = (byte *) W_GetLumpName ("conchars"); if (!data) Sys_Error ("Draw_LoadPics: couldn't load conchars"); offset = (src_offset_t)data - (src_offset_t)wad_base; char_texture = TexMgr_LoadImage (NULL, WADFILENAME":conchars", 128, 128, SRC_INDEXED, data, WADFILENAME, offset, TEXPREF_ALPHA | TEXPREF_NEAREST | TEXPREF_NOPICMIP | TEXPREF_CONCHARS); draw_disc = Draw_PicFromWad ("disc"); draw_backtile = Draw_PicFromWad ("backtile"); } /* =============== Draw_NewGame -- johnfitz =============== */ void Draw_NewGame (void) { cachepic_t *pic; int i; // empty scrap and reallocate gltextures memset(scrap_allocated, 0, sizeof(scrap_allocated)); memset(scrap_texels, 255, sizeof(scrap_texels)); Scrap_Upload (); //creates 2 empty gltextures // reload wad pics W_LoadWadFile (); //johnfitz -- filename is now hard-coded for honesty Draw_LoadPics (); SCR_LoadPics (); Sbar_LoadPics (); // empty lmp cache for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++) pic->name[0] = 0; menu_numcachepics = 0; } /* =============== Draw_Init -- johnfitz -- rewritten =============== */ void Draw_Init (void) { Cvar_RegisterVariable (&scr_conalpha); // clear scrap and allocate gltextures memset(scrap_allocated, 0, sizeof(scrap_allocated)); memset(scrap_texels, 255, sizeof(scrap_texels)); Scrap_Upload (); //creates 2 empty textures // create internal pics pic_ins = Draw_MakePic ("ins", 8, 9, &pic_ins_data[0][0]); pic_ovr = Draw_MakePic ("ovr", 8, 8, &pic_ovr_data[0][0]); pic_nul = Draw_MakePic ("nul", 8, 8, &pic_nul_data[0][0]); // load game pics Draw_LoadPics (); } //============================================================================== // // 2D DRAWING // //============================================================================== /* ================ Draw_CharacterQuad -- johnfitz -- seperate function to spit out verts ================ */ void Draw_CharacterQuad (int x, int y, char num) { int row, col; float frow, fcol, size; row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; glTexCoord2f (fcol, frow); glVertex2f (x, y); glTexCoord2f (fcol + size, frow); glVertex2f (x+8, y); glTexCoord2f (fcol + size, frow + size); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + size); glVertex2f (x, y+8); } /* ================ Draw_Character -- johnfitz -- modified to call Draw_CharacterQuad ================ */ void Draw_Character (int x, int y, int num) { if (y <= -8) return; // totally off screen num &= 255; if (num == 32) return; //don't waste verts on spaces GL_Bind (char_texture); glBegin (GL_QUADS); Draw_CharacterQuad (x, y, (char) num); glEnd (); } /* ================ Draw_String -- johnfitz -- modified to call Draw_CharacterQuad ================ */ void Draw_String (int x, int y, const char *str) { if (y <= -8) return; // totally off screen GL_Bind (char_texture); glBegin (GL_QUADS); while (*str) { if (*str != 32) //don't waste verts on spaces Draw_CharacterQuad (x, y, *str); str++; x += 8; } glEnd (); } /* ============= Draw_Pic -- johnfitz -- modified ============= */ void Draw_Pic (int x, int y, qpic_t *pic) { glpic_t *gl; if (scrap_dirty) Scrap_Upload (); gl = (glpic_t *)pic->data; GL_Bind (gl->gltexture); glBegin (GL_QUADS); glTexCoord2f (gl->sl, gl->tl); glVertex2f (x, y); glTexCoord2f (gl->sh, gl->tl); glVertex2f (x+pic->width, y); glTexCoord2f (gl->sh, gl->th); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (gl->sl, gl->th); glVertex2f (x, y+pic->height); glEnd (); } /* ============= Draw_TransPicTranslate -- johnfitz -- rewritten to use texmgr to do translation Only used for the player color selection menu ============= */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, int top, int bottom) { static int oldtop = -2; static int oldbottom = -2; if (top != oldtop || bottom != oldbottom) { glpic_t *p = (glpic_t *)pic->data; gltexture_t *glt = p->gltexture; oldtop = top; oldbottom = bottom; TexMgr_ReloadImage (glt, top, bottom); } Draw_Pic (x, y, pic); } /* ================ Draw_ConsoleBackground -- johnfitz -- rewritten ================ */ void Draw_ConsoleBackground (void) { qpic_t *pic; float alpha; pic = Draw_CachePic ("gfx/conback.lmp"); pic->width = vid.conwidth; pic->height = vid.conheight; alpha = (con_forcedup) ? 1.0 : scr_conalpha.value; GL_SetCanvas (CANVAS_CONSOLE); //in case this is called from weird places if (alpha > 0.0) { if (alpha < 1.0) { glEnable (GL_BLEND); glColor4f (1,1,1,alpha); glDisable (GL_ALPHA_TEST); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } Draw_Pic (0, 0, pic); if (alpha < 1.0) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable (GL_ALPHA_TEST); glDisable (GL_BLEND); glColor4f (1,1,1,1); } } } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void Draw_TileClear (int x, int y, int w, int h) { glpic_t *gl; gl = (glpic_t *)draw_backtile->data; glColor3f (1,1,1); GL_Bind (gl->gltexture); glBegin (GL_QUADS); glTexCoord2f (x/64.0, y/64.0); glVertex2f (x, y); glTexCoord2f ( (x+w)/64.0, y/64.0); glVertex2f (x+w, y); glTexCoord2f ( (x+w)/64.0, (y+h)/64.0); glVertex2f (x+w, y+h); glTexCoord2f ( x/64.0, (y+h)/64.0 ); glVertex2f (x, y+h); glEnd (); } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c, float alpha) //johnfitz -- added alpha { byte *pal = (byte *)d_8to24table; //johnfitz -- use d_8to24table instead of host_basepal glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); //johnfitz -- for alpha glDisable (GL_ALPHA_TEST); //johnfitz -- for alpha glColor4f (pal[c*4]/255.0, pal[c*4+1]/255.0, pal[c*4+2]/255.0, alpha); //johnfitz -- added alpha glBegin (GL_QUADS); glVertex2f (x,y); glVertex2f (x+w, y); glVertex2f (x+w, y+h); glVertex2f (x, y+h); glEnd (); glColor3f (1,1,1); glDisable (GL_BLEND); //johnfitz -- for alpha glEnable (GL_ALPHA_TEST); //johnfitz -- for alpha glEnable (GL_TEXTURE_2D); } /* ================ Draw_FadeScreen -- johnfitz -- revised ================ */ void Draw_FadeScreen (void) { GL_SetCanvas (CANVAS_DEFAULT); glEnable (GL_BLEND); glDisable (GL_ALPHA_TEST); glDisable (GL_TEXTURE_2D); glColor4f (0, 0, 0, 0.5); glBegin (GL_QUADS); glVertex2f (0,0); glVertex2f (glwidth, 0); glVertex2f (glwidth, glheight); glVertex2f (0, glheight); glEnd (); glColor4f (1,1,1,1); glEnable (GL_TEXTURE_2D); glEnable (GL_ALPHA_TEST); glDisable (GL_BLEND); Sbar_Changed(); } /* ================ GL_SetCanvas -- johnfitz -- support various canvas types ================ */ void GL_SetCanvas (canvastype newcanvas) { extern vrect_t scr_vrect; float s; int lines; if (newcanvas == currentcanvas) return; currentcanvas = newcanvas; glMatrixMode(GL_PROJECTION); glLoadIdentity (); switch(newcanvas) { case CANVAS_DEFAULT: glOrtho (0, glwidth, glheight, 0, -99999, 99999); glViewport (glx, gly, glwidth, glheight); break; case CANVAS_CONSOLE: lines = vid.conheight - (scr_con_current * vid.conheight / glheight); glOrtho (0, vid.conwidth, vid.conheight + lines, lines, -99999, 99999); glViewport (glx, gly, glwidth, glheight); break; case CANVAS_MENU: s = q_min((float)glwidth / 320.0, (float)glheight / 200.0); s = CLAMP (1.0, scr_menuscale.value, s); // ericw -- doubled width to 640 to accommodate long keybindings glOrtho (0, 640, 200, 0, -99999, 99999); glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 640*s, 200*s); break; case CANVAS_SBAR: s = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); if (cl.gametype == GAME_DEATHMATCH) { glOrtho (0, glwidth / s, 48, 0, -99999, 99999); glViewport (glx, gly, glwidth, 48*s); } else { glOrtho (0, 320, 48, 0, -99999, 99999); glViewport (glx + (glwidth - 320*s) / 2, gly, 320*s, 48*s); } break; case CANVAS_WARPIMAGE: glOrtho (0, 128, 0, 128, -99999, 99999); glViewport (glx, gly+glheight-gl_warpimagesize, gl_warpimagesize, gl_warpimagesize); break; case CANVAS_CROSSHAIR: //0,0 is center of viewport s = CLAMP (1.0, scr_crosshairscale.value, 10.0); glOrtho (scr_vrect.width/-2/s, scr_vrect.width/2/s, scr_vrect.height/2/s, scr_vrect.height/-2/s, -99999, 99999); glViewport (scr_vrect.x, glheight - scr_vrect.y - scr_vrect.height, scr_vrect.width & ~1, scr_vrect.height & ~1); break; case CANVAS_BOTTOMLEFT: //used by devstats s = (float)glwidth/vid.conwidth; //use console scale glOrtho (0, 320, 200, 0, -99999, 99999); glViewport (glx, gly, 320*s, 200*s); break; case CANVAS_BOTTOMRIGHT: //used by fps/clock s = (float)glwidth/vid.conwidth; //use console scale glOrtho (0, 320, 200, 0, -99999, 99999); glViewport (glx+glwidth-320*s, gly, 320*s, 200*s); break; case CANVAS_TOPRIGHT: //used by disc s = 1; glOrtho (0, 320, 200, 0, -99999, 99999); glViewport (glx+glwidth-320*s, gly+glheight-200*s, 320*s, 200*s); break; default: Sys_Error ("GL_SetCanvas: bad canvas type"); } glMatrixMode(GL_MODELVIEW); glLoadIdentity (); } /* ================ GL_Set2D -- johnfitz -- rewritten ================ */ void GL_Set2D (void) { currentcanvas = CANVAS_INVALID; GL_SetCanvas (CANVAS_DEFAULT); glDisable (GL_DEPTH_TEST); glDisable (GL_CULL_FACE); glDisable (GL_BLEND); glEnable (GL_ALPHA_TEST); glColor4f (1,1,1,1); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_wins.c������������������������������������������������������������������0000644�0000000�0000000�00000031767�13136754067�015545� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static sys_socket_t net_broadcastsocket = 0; static struct sockaddr_in broadcastaddr; static in_addr_t myAddr; #include "net_wins.h" int winsock_initialized = 0; WSADATA winsockdata; #define __wsaerr_static /* not static: used by net_wipx.c too */ #include "wsaerror.h" //============================================================================= #if !defined(_USE_WINSOCK2) static double blocktime; static INT_PTR PASCAL FAR BlockingHook (void) { MSG msg; BOOL ret; if ((Sys_DoubleTime() - blocktime) > 2.0) { WSACancelBlockingCall(); return FALSE; } /* get the next message, if any */ ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); /* if we got one, process it */ if (ret) { TranslateMessage(&msg); DispatchMessage(&msg); } /* TRUE if we got a message */ return ret; } #endif /* ! _USE_WINSOCK2 */ static void WINS_GetLocalAddress (void) { struct hostent *local = NULL; char buff[MAXHOSTNAMELEN]; in_addr_t addr; int err; if (myAddr != INADDR_ANY) return; if (gethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) { err = SOCKETERRNO; Con_SafePrintf("WINS_GetLocalAddress: gethostname failed (%s)\n", socketerror(err)); return; } buff[MAXHOSTNAMELEN - 1] = 0; #ifndef _USE_WINSOCK2 blocktime = Sys_DoubleTime(); WSASetBlockingHook(BlockingHook); #endif local = gethostbyname(buff); err = WSAGetLastError(); #ifndef _USE_WINSOCK2 WSAUnhookBlockingHook(); #endif if (local == NULL) { Con_SafePrintf("WINS_GetLocalAddress: gethostbyname failed (%s)\n", __WSAE_StrError(err)); return; } myAddr = *(in_addr_t *)local->h_addr_list[0]; addr = ntohl(myAddr); sprintf(my_tcpip_address, "%ld.%ld.%ld.%ld", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); } sys_socket_t WINS_Init (void) { int i, err; char buff[MAXHOSTNAMELEN]; if (COM_CheckParm ("-noudp")) return -1; if (winsock_initialized == 0) { err = WSAStartup(MAKEWORD(1,1), &winsockdata); if (err != 0) { Con_SafePrintf("Winsock initialization failed (%s)\n", socketerror(err)); return INVALID_SOCKET; } } winsock_initialized++; // determine my name & address if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("WINS_Init: gethostname failed (%s)\n", socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; } i = COM_CheckParm ("-ip"); if (i) { if (i < com_argc-1) { myAddr = inet_addr(com_argv[i+1]); if (myAddr == INADDR_NONE) Sys_Error ("%s is not a valid IP address", com_argv[i+1]); strcpy(my_tcpip_address, com_argv[i+1]); } else { Sys_Error ("NET_Init: you must specify an IP address after -ip"); } } else { myAddr = INADDR_ANY; strcpy(my_tcpip_address, "INADDR_ANY"); } if ((net_controlsocket = WINS_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("WINS_Init: Unable to open control socket, UDP disabled\n"); if (--winsock_initialized == 0) WSACleanup (); return INVALID_SOCKET; } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST; broadcastaddr.sin_port = htons((unsigned short)net_hostport); Con_SafePrintf("UDP Initialized\n"); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void WINS_Shutdown (void) { WINS_Listen (false); WINS_CloseSocket (net_controlsocket); if (--winsock_initialized == 0) WSACleanup (); } //============================================================================= void WINS_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; WINS_GetLocalAddress(); if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("WINS_Listen: Unable to open accept socket"); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; WINS_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t WINS_OpenSocket (int port) { sys_socket_t newsocket; struct sockaddr_in address; u_long _true = 1; int err; if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_in)); address.sin_family = AF_INET; address.sin_addr.s_addr = myAddr; address.sin_port = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) return newsocket; if (tcpipAvailable) { err = SOCKETERRNO; Sys_Error ("Unable to bind to %s (%s)", WINS_AddrToString ((struct qsockaddr *) &address), socketerror(err)); return INVALID_SOCKET; /* not reached */ } /* else: we are still in init phase, no need to error */ ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err)); closesocket (newsocket); return INVALID_SOCKET; } //============================================================================= int WINS_CloseSocket (sys_socket_t socketid) { if (socketid == net_broadcastsocket) net_broadcastsocket = 0; return closesocket (socketid); } //============================================================================= /* ============ PartialIPAddress this lets you type only as much of the net address as required, using the local network components to fill in the rest ============ */ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr) { char buff[256]; char *b; int addr, mask, num, port, run; buff[0] = '.'; b = buff; strcpy(buff+1, in); if (buff[1] == '.') b++; addr = 0; mask = -1; while (*b == '.') { b++; num = 0; run = 0; while (!( *b < '0' || *b > '9')) { num = num*10 + *b++ - '0'; if (++run > 3) return -1; } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) return -1; if (num < 0 || num > 255) return -1; mask <<= 8; addr = (addr<<8) + num; } if (*b++ == ':') port = Q_atoi(b); else port = net_hostport; hostaddr->qsa_family = AF_INET; ((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port); ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); return 0; } //============================================================================= int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t WINS_CheckNewConnections (void) { char buf[4096]; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (recvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) != SOCKET_ERROR) { return net_acceptsocket; } return INVALID_SOCKET; } //============================================================================= int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); int ret; ret = recvfrom (socketid, (char *)buf, len, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafePrintf ("WINS_Read, recvfrom: %s\n", socketerror(err)); } return ret; } //============================================================================= static int WINS_MakeSocketBroadcastCapable (sys_socket_t socketid) { int i = 1; // make this socket broadcast capable if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR) { int err = SOCKETERRNO; Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err)); return -1; } net_broadcastsocket = socketid; return 0; } //============================================================================= int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len) { int ret; if (socketid != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets"); WINS_GetLocalAddress(); ret = WINS_MakeSocketBroadcastCapable (socketid); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return WINS_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sendto (socketid, (char *)buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafePrintf ("WINS_Write, sendto: %s\n", socketerror(err)); } return ret; } //============================================================================= const char *WINS_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); return buffer; } //============================================================================= int WINS_StringToAddr (const char *string, struct qsockaddr *addr) { int ha1, ha2, ha3, ha4, hp, ipaddr; sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); return 0; } //============================================================================= int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); in_addr_t a; memset(addr, 0, sizeof(struct qsockaddr)); getsockname(socketid, (struct sockaddr *)addr, &addrlen); a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; if (a == 0 || a == htonl(INADDR_LOOPBACK)) ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; return 0; } //============================================================================= int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name) { struct hostent *hostentry; hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); if (hostentry) { Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); return 0; } Q_strcpy (name, WINS_AddrToString (addr)); return 0; } //============================================================================= int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr) { struct hostent *hostentry; if (name[0] >= '0' && name[0] <= '9') return PartialIPAddress (name, addr); hostentry = gethostbyname (name); if (!hostentry) return -1; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(in_addr_t *)hostentry->h_addr_list[0]; return 0; } //============================================================================= int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) return -1; if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) return 1; return 0; } //============================================================================= int WINS_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_in *)addr)->sin_port); } int WINS_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); return 0; } //============================================================================= ���������quakespasm-0.93.0/Quake/resource.h������������������������������������������������������������������0000644�0000000�0000000�00000001274�11340073704�015524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef _QUAKE_RESOURCE_H #define _QUAKE_RESOURCE_H //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by winquake.rc // #define IDS_STRING1 1 #define IDI_ICON2 1 #define IDD_DIALOG1 108 #define IDD_PROGRESS 109 #define IDC_PROGRESS 1000 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 113 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1004 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif #endif /* _QUAKE_RESOURCE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/lodepng.c�������������������������������������������������������������������0000644�0000000�0000000�00000656375�13157453245�015354� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* LodePNG version 20170917 Copyright (c) 2005-2017 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* The manual and changelog are in the header file "lodepng.h" Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. */ #include "lodepng.h" #include <limits.h> #include <stdio.h> #include <stdlib.h> #if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ const char* LODEPNG_VERSION_STRING = "20170917"; /* This source file is built up in the following large parts. The code sections with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. -Tools for C and common code for PNG and Zlib -C Code for Zlib (huffman, deflate, ...) -C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) -The C++ wrapper around all of the above */ /*The malloc, realloc and free functions defined here with "lodepng_" in front of the name, so that you can easily change them to others related to your platform if needed. Everything else in the code calls these. Pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out #define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and define them in your own project's source files without needing to change lodepng source code. Don't forget to remove "static" if you copypaste them from here.*/ #ifdef LODEPNG_COMPILE_ALLOCATORS static void* lodepng_malloc(size_t size) { return malloc(size); } static void* lodepng_realloc(void* ptr, size_t new_size) { return realloc(ptr, new_size); } static void lodepng_free(void* ptr) { free(ptr); } #else /*LODEPNG_COMPILE_ALLOCATORS*/ void* lodepng_malloc(size_t size); void* lodepng_realloc(void* ptr, size_t new_size); void lodepng_free(void* ptr); #endif /*LODEPNG_COMPILE_ALLOCATORS*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // Tools for C, and common code for PNG and Zlib. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. It makes the error handling code shorter and more readable. Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); */ #define CERROR_BREAK(errorvar, code)\ {\ errorvar = code;\ break;\ } /*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ #define ERROR_BREAK(code) CERROR_BREAK(error, code) /*Set error var to the error code, and return it.*/ #define CERROR_RETURN_ERROR(errorvar, code)\ {\ errorvar = code;\ return code;\ } /*Try the code, if it returns error, also return the error.*/ #define CERROR_TRY_RETURN(call)\ {\ unsigned error = call;\ if(error) return error;\ } /*Set error var to the error code, and return from the void function.*/ #define CERROR_RETURN(errorvar, code)\ {\ errorvar = code;\ return;\ } /* About uivector, ucvector and string: -All of them wrap dynamic arrays or text strings in a similar way. -LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. -The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. -They're not used in the interface, only internally in this file as static functions. -As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. */ #ifdef LODEPNG_COMPILE_ZLIB /*dynamic vector of unsigned ints*/ typedef struct uivector { unsigned* data; size_t size; /*size in number of unsigned longs*/ size_t allocsize; /*allocated size in bytes*/ } uivector; static void uivector_cleanup(void* p) { ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; lodepng_free(((uivector*)p)->data); ((uivector*)p)->data = NULL; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_reserve(uivector* p, size_t allocsize) { if(allocsize > p->allocsize) { size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned*)data; } else return 0; /*error: not enough memory*/ } return 1; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_resize(uivector* p, size_t size) { if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; p->size = size; return 1; /*success*/ } /*resize and give all new elements the value*/ static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) { size_t oldsize = p->size, i; if(!uivector_resize(p, size)) return 0; for(i = oldsize; i < size; ++i) p->data[i] = value; return 1; } static void uivector_init(uivector* p) { p->data = NULL; p->size = p->allocsize = 0; } #ifdef LODEPNG_COMPILE_ENCODER /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_push_back(uivector* p, unsigned c) { if(!uivector_resize(p, p->size + 1)) return 0; p->data[p->size - 1] = c; return 1; } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* /////////////////////////////////////////////////////////////////////////// */ /*dynamic vector of unsigned chars*/ typedef struct ucvector { unsigned char* data; size_t size; /*used size*/ size_t allocsize; /*allocated size*/ } ucvector; /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_reserve(ucvector* p, size_t allocsize) { if(allocsize > p->allocsize) { size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned char*)data; } else return 0; /*error: not enough memory*/ } return 1; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_resize(ucvector* p, size_t size) { if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; p->size = size; return 1; /*success*/ } #ifdef LODEPNG_COMPILE_PNG static void ucvector_cleanup(void* p) { ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; lodepng_free(((ucvector*)p)->data); ((ucvector*)p)->data = NULL; } static void ucvector_init(ucvector* p) { p->data = NULL; p->size = p->allocsize = 0; } #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB /*you can both convert from vector to buffer&size and vica versa. If you use init_buffer to take over a buffer and size, it is not needed to use cleanup*/ static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) { p->data = buffer; p->allocsize = p->size = size; } #endif /*LODEPNG_COMPILE_ZLIB*/ #if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_push_back(ucvector* p, unsigned char c) { if(!ucvector_resize(p, p->size + 1)) return 0; p->data[p->size - 1] = c; return 1; } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned string_resize(char** out, size_t size) { char* data = (char*)lodepng_realloc(*out, size + 1); if(data) { data[size] = 0; /*null termination char*/ *out = data; } return data != 0; } /*init a {char*, size_t} pair for use as string*/ static void string_init(char** out) { *out = NULL; string_resize(out, 0); } /*free the above pair again*/ static void string_cleanup(char** out) { lodepng_free(*out); *out = NULL; } static void string_set(char** out, const char* in) { size_t insize = strlen(in), i; if(string_resize(out, insize)) { for(i = 0; i != insize; ++i) { (*out)[i] = in[i]; } } } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned lodepng_read32bitInt(const unsigned char* buffer) { return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); } #if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) /*buffer must have at least 4 allocated bytes available*/ static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) { buffer[0] = (unsigned char)((value >> 24) & 0xff); buffer[1] = (unsigned char)((value >> 16) & 0xff); buffer[2] = (unsigned char)((value >> 8) & 0xff); buffer[3] = (unsigned char)((value ) & 0xff); } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ #ifdef LODEPNG_COMPILE_ENCODER static void lodepng_add32bitInt(ucvector* buffer, unsigned value) { ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); } #endif /*LODEPNG_COMPILE_ENCODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / File IO / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DISK /* returns negative value on error. This should be pure C compatible, so no fstat. */ static long lodepng_filesize(const char* filename) { FILE* file; long size; file = fopen(filename, "rb"); if(!file) return -1; if(fseek(file, 0, SEEK_END) != 0) { fclose(file); return -1; } size = ftell(file); /* It may give LONG_MAX as directory size, this is invalid for us. */ if(size == LONG_MAX) size = -1; fclose(file); return size; } /* load file into buffer that already has the correct allocated size. Returns error code.*/ static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) { FILE* file; size_t readsize; file = fopen(filename, "rb"); if(!file) return 78; readsize = fread(out, 1, size, file); fclose(file); if (readsize != size) return 78; return 0; } unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { long size = lodepng_filesize(filename); if (size < 0) return 78; *outsize = (size_t)size; *out = (unsigned char*)lodepng_malloc((size_t)size); if(!(*out) && size > 0) return 83; /*the above malloc failed*/ return lodepng_buffer_file(*out, (size_t)size, filename); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) { FILE* file; file = fopen(filename, "wb" ); if(!file) return 79; fwrite(buffer , 1 , buffersize, file); fclose(file); return 0; } #endif /*LODEPNG_COMPILE_DISK*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of common code and tools. Begin of Zlib related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_ENCODER /*TODO: this ignores potential out of memory errors*/ #define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ {\ /*add a new byte at the end*/\ if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ ++(*bitpointer);\ } static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); } static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); } #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER #define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); ++(*bitpointer); return result; } static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0, i; for(i = 0; i != nbits; ++i) { result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; ++(*bitpointer); } return result; } #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflate - Huffman / */ /* ////////////////////////////////////////////////////////////////////////// */ #define FIRST_LENGTH_CODE_INDEX 257 #define LAST_LENGTH_CODE_INDEX 285 /*256 literals, the end code, some length codes, and 2 unused codes*/ #define NUM_DEFLATE_CODE_SYMBOLS 288 /*the distance codes have their own symbols, 30 used, 2 unused*/ #define NUM_DISTANCE_SYMBOLS 32 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ #define NUM_CODE_LENGTH_CODES 19 /*the base lengths represented by codes 257-285*/ static const unsigned LENGTHBASE[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; /*the extra bits used by codes 257-285 (added to base length)*/ static const unsigned LENGTHEXTRA[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ static const unsigned DISTANCEBASE[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; /*the extra bits of backwards distances (added to base)*/ static const unsigned DISTANCEEXTRA[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /*the order in which "code length alphabet code lengths" are stored, out of this the huffman tree of the dynamic huffman tree lengths is generated*/ static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* ////////////////////////////////////////////////////////////////////////// */ /* Huffman tree struct, containing multiple representations of the tree */ typedef struct HuffmanTree { unsigned* tree2d; unsigned* tree1d; unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ unsigned maxbitlen; /*maximum number of bits a single code can get*/ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ } HuffmanTree; /*function used for debug purposes to draw the tree in ascii art with C++*/ /* static void HuffmanTree_draw(HuffmanTree* tree) { std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; for(size_t i = 0; i != tree->tree1d.size; ++i) { if(tree->lengths.data[i]) std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; } std::cout << std::endl; }*/ static void HuffmanTree_init(HuffmanTree* tree) { tree->tree2d = 0; tree->tree1d = 0; tree->lengths = 0; } static void HuffmanTree_cleanup(HuffmanTree* tree) { lodepng_free(tree->tree2d); lodepng_free(tree->tree1d); lodepng_free(tree->lengths); } /*the tree representation used by the decoder. return value is error*/ static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) { unsigned nodefilled = 0; /*up to which node it is filled*/ unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ unsigned n, i; tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); if(!tree->tree2d) return 83; /*alloc fail*/ /* convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1. A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen */ for(n = 0; n < tree->numcodes * 2; ++n) { tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ } for(n = 0; n < tree->numcodes; ++n) /*the codes*/ { for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ { unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); /*oversubscribed, see comment in lodepng_error_text*/ if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ { if(i + 1 == tree->lengths[n]) /*last bit*/ { tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ treepos = 0; } else { /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/ ++nodefilled; /*addresses encoded with numcodes added to it*/ tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; treepos = nodefilled; } } else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; } } for(n = 0; n < tree->numcodes * 2; ++n) { if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ } return 0; } /* Second step for the ...makeFromLengths and ...makeFromFrequencies functions. numcodes, lengths and maxbitlen must already be filled in correctly. return value is error. */ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { uivector blcount; uivector nextcode; unsigned error = 0; unsigned bits, n; uivector_init(&blcount); uivector_init(&nextcode); tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); if(!tree->tree1d) error = 83; /*alloc fail*/ if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) error = 83; /*alloc fail*/ if(!error) { /*step 1: count number of instances of each code length*/ for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; } /*step 3: generate all the codes*/ for(n = 0; n != tree->numcodes; ++n) { if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; } } uivector_cleanup(&blcount); uivector_cleanup(&nextcode); if(!error) return HuffmanTree_make2DTree(tree); else return error; } /* given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error. */ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) { unsigned i; tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); } #ifdef LODEPNG_COMPILE_ENCODER /*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ /*chain node for boundary package merge*/ typedef struct BPMNode { int weight; /*the sum of all weights in this chain*/ unsigned index; /*index of this leaf node (called "count" in the paper)*/ struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ int in_use; } BPMNode; /*lists of chains*/ typedef struct BPMLists { /*memory pool*/ unsigned memsize; BPMNode* memory; unsigned numfree; unsigned nextfree; BPMNode** freelist; /*two heads of lookahead chains per list*/ unsigned listsize; BPMNode** chains0; BPMNode** chains1; } BPMLists; /*creates a new chain node with the given parameters, from the memory in the lists */ static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { unsigned i; BPMNode* result; /*memory full, so garbage collect*/ if(lists->nextfree >= lists->numfree) { /*mark only those that are in use*/ for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; for(i = 0; i != lists->listsize; ++i) { BPMNode* node; for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; } /*collect those that are free*/ lists->numfree = 0; for(i = 0; i != lists->memsize; ++i) { if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; } lists->nextfree = 0; } result = lists->freelist[lists->nextfree++]; result->weight = weight; result->index = index; result->tail = tail; return result; } /*sort the leaves with stable mergesort*/ static void bpmnode_sort(BPMNode* leaves, size_t num) { BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); size_t width, counter = 0; for(width = 1; width < num; width *= 2) { BPMNode* a = (counter & 1) ? mem : leaves; BPMNode* b = (counter & 1) ? leaves : mem; size_t p; for(p = 0; p < num; p += 2 * width) { size_t q = (p + width > num) ? num : (p + width); size_t r = (p + 2 * width > num) ? num : (p + 2 * width); size_t i = p, j = q, k; for(k = p; k < r; k++) { if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; else b[k] = a[j++]; } } counter++; } if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); lodepng_free(mem); } /*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { unsigned lastindex = lists->chains1[c]->index; if(c == 0) { if(lastindex >= numpresent) return; lists->chains0[c] = lists->chains1[c]; lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); } else { /*sum of the weights of the head nodes of the previous lookahead chains.*/ int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; lists->chains0[c] = lists->chains1[c]; if(lastindex < numpresent && sum > leaves[lastindex].weight) { lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); return; } lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); /*in the end we are only interested in the chain of the last list, so no need to recurse if we're at the last one (this gives measurable speedup)*/ if(num + 1 < (int)(2 * numpresent - 2)) { boundaryPM(lists, leaves, numpresent, c - 1, num); boundaryPM(lists, leaves, numpresent, c - 1, num); } } } unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; unsigned i; size_t numpresent = 0; /*number of symbols with non-zero frequency*/ BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); if(!leaves) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) { if(frequencies[i] > 0) { leaves[numpresent].weight = (int)frequencies[i]; leaves[numpresent].index = i; ++numpresent; } } for(i = 0; i != numcodes; ++i) lengths[i] = 0; /*ensure at least two present symbols. There should be at least one symbol according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To make these work as well ensure there are at least two symbols. The Package-Merge code below also doesn't work correctly if there's only one symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ if(numpresent == 0) { lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ } else if(numpresent == 1) { lengths[leaves[0].index] = 1; lengths[leaves[0].index == 0 ? 1 : 0] = 1; } else { BPMLists lists; BPMNode* node; bpmnode_sort(leaves, numpresent); lists.listsize = maxbitlen; lists.memsize = 2 * maxbitlen * (maxbitlen + 1); lists.nextfree = 0; lists.numfree = lists.memsize; lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ if(!error) { for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; bpmnode_create(&lists, leaves[0].weight, 1, 0); bpmnode_create(&lists, leaves[1].weight, 2, 0); for(i = 0; i != lists.listsize; ++i) { lists.chains0[i] = &lists.memory[0]; lists.chains1[i] = &lists.memory[1]; } /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; } } lodepng_free(lists.memory); lodepng_free(lists.freelist); lodepng_free(lists.chains0); lodepng_free(lists.chains1); } lodepng_free(leaves); return error; } /*Create the Huffman tree given the symbol frequencies*/ static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, size_t mincodes, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ /*initialize all lengths to 0*/ memset(tree->lengths, 0, numcodes * sizeof(unsigned)); error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); if(!error) error = HuffmanTree_makeFromLengths2(tree); return error; } static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) { return tree->tree1d[index]; } static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) { return tree->lengths[index]; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ static unsigned generateFixedLitLenTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ for(i = 0; i <= 143; ++i) bitlen[i] = 8; for(i = 144; i <= 255; ++i) bitlen[i] = 9; for(i = 256; i <= 279; ++i) bitlen[i] = 7; for(i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); lodepng_free(bitlen); return error; } /*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ static unsigned generateFixedDistanceTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); lodepng_free(bitlen); return error; } #ifdef LODEPNG_COMPILE_DECODER /* returns the code, or (unsigned)(-1) if error happened inbitlength is the length of the complete buffer, in bits (so its byte length times 8) */ static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, const HuffmanTree* codetree, size_t inbitlength) { unsigned treepos = 0, ct; for(;;) { if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ /* decode the symbol from the tree. The "readBitFromStream" code is inlined in the expression below because this is the biggest bottleneck while decoding */ ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; ++(*bp); if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Inflator (Decompressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ /*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { /*TODO: check for out of memory errors*/ generateFixedLitLenTree(tree_ll); generateFixedDistanceTree(tree_d); } /*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, const unsigned char* in, size_t* bp, size_t inlength) { /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ unsigned error = 0; unsigned n, HLIT, HDIST, HCLEN, i; size_t inbitlength = inlength * 8; /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ unsigned* bitlen_ll = 0; /*lit,len code lengths*/ unsigned* bitlen_d = 0; /*dist code lengths*/ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBitsFromStream(bp, in, 5) + 257; /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ HDIST = readBitsFromStream(bp, in, 5) + 1; /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBitsFromStream(bp, in, 4) + 4; if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ HuffmanTree_init(&tree_cl); while(!error) { /*read the code length codes out of 3 * (amount of code length codes) bits*/ bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) { if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ } error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); if(error) break; /*now we can use this tree to read the lengths for the tree that this function will return*/ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; while(i < HLIT + HDIST) { unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); if(code <= 15) /*a length code*/ { if(i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; ++i; } else if(code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 2); if(i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 3); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 7); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { if(code == (unsigned)(-1)) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = (*bp) > inbitlength ? 10 : 11; } else error = 16; /*unexisting code, this can never happen*/ break; } } if(error) break; if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); if(error) break; error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); break; /*end of error-while*/ } lodepng_free(bitlen_cl); lodepng_free(bitlen_ll); lodepng_free(bitlen_d); HuffmanTree_cleanup(&tree_cl); return error; } /*inflate a block with dynamic of fixed Huffman tree*/ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength, unsigned btype) { unsigned error = 0; HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/ size_t inbitlength = inlength * 8; HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); while(!error) /*decode all symbols until end reached, breaks at end code*/ { /*code_ll is literal, length or end code*/ unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); if(code_ll <= 255) /*literal symbol*/ { /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[*pos] = (unsigned char)code_ll; ++(*pos); } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { unsigned code_d, distance; unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ size_t start, forward, backward, length; /*part 1: get length base*/ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ length += readBitsFromStream(bp, in, numextrabits_l); /*part 3: get distance code*/ code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); if(code_d > 29) { if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = (*bp) > inlength * 8 ? 10 : 11; } else error = 18; /*error: invalid distance code (30-31 are never used)*/ break; } distance = DISTANCEBASE[code_d]; /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ distance += readBitsFromStream(bp, in, numextrabits_d); /*part 5: fill in all the out[n] values based on the length and dist*/ start = (*pos); if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); if (distance < length) { for(forward = 0; forward < length; ++forward) { out->data[(*pos)++] = out->data[backward++]; } } else { memcpy(out->data + *pos, out->data + backward, length); *pos += length; } } else if(code_ll == 256) { break; /*end code, break the loop*/ } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = ((*bp) > inlength * 8) ? 10 : 11; break; } } HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) { size_t p; unsigned LEN, NLEN, n, error = 0; /*go to first boundary of byte*/ while(((*bp) & 0x7) != 0) ++(*bp); p = (*bp) / 8; /*byte position*/ /*read LEN (2 bytes) and NLEN (2 bytes)*/ if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ LEN = in[p] + 256u * in[p + 1]; p += 2; NLEN = in[p] + 256u * in[p + 1]; p += 2; /*check if 16-bit NLEN is really the one's complement of LEN*/ if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; (*bp) = p * 8; return error; } static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ size_t bp = 0; unsigned BFINAL = 0; size_t pos = 0; /*byte position in the out buffer*/ unsigned error = 0; (void)settings; while(!BFINAL) { unsigned BTYPE; if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ BFINAL = readBitFromStream(&bp, in); BTYPE = 1u * readBitFromStream(&bp, in); BTYPE += 2u * readBitFromStream(&bp, in); if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ if(error) return error; } return error; } unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error; ucvector v; ucvector_init_buffer(&v, *out, *outsize); error = lodepng_inflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_inflate) { return settings->custom_inflate(out, outsize, in, insize, settings); } else { return lodepng_inflate(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflator (Compressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; /*bitlen is the size in bits of the code*/ static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) { addBitsToStreamReversed(bp, compressed, code, bitlen); } /*search the index in the array, that has the largest value smaller than or equal to the given value, given array must be sorted (if no value is smaller, it returns the size of the given array)*/ static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ size_t left = 1; size_t right = array_size - 1; while(left <= right) { size_t mid = (left + right) >> 1; if (array[mid] >= value) right = mid - 1; else left = mid + 1; } if(left >= array_size || array[left] > value) left--; return left; } static void addLengthDistance(uivector* values, size_t length, size_t distance) { /*values in encoded vector are those used by deflate: 0-255: literal bytes 256: end 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) 286-287: invalid*/ unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); uivector_push_back(values, extra_length); uivector_push_back(values, dist_code); uivector_push_back(values, extra_distance); } /*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 bytes as input because 3 is the minimum match length for deflate*/ static const unsigned HASH_NUM_VALUES = 65536; static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ typedef struct Hash { int* head; /*hash value to head circular pos - can be outdated if went around window*/ /*circular pos to prev circular pos*/ unsigned short* chain; int* val; /*circular pos to hash value*/ /*TODO: do this not only for zeros but for any repeated byte. However for PNG it's always going to be the zeros that dominate, so not important for PNG*/ int* headz; /*similar to head, but for chainz*/ unsigned short* chainz; /*those with same amount of zeros*/ unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ } Hash; static unsigned hash_init(Hash* hash, unsigned windowsize) { unsigned i; hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) { return 83; /*alloc fail*/ } /*initialize hash table*/ for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; for(i = 0; i != windowsize; ++i) hash->val[i] = -1; for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ return 0; } static void hash_cleanup(Hash* hash) { lodepng_free(hash->head); lodepng_free(hash->val); lodepng_free(hash->chain); lodepng_free(hash->zeros); lodepng_free(hash->headz); lodepng_free(hash->chainz); } static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { unsigned result = 0; if(pos + 2 < size) { /*A simple shift and xor hash is used. Since the data of PNGs is dominated by zeroes due to the filters, a better hash does not have a significant effect on speed in traversing the chain, and causes more time spend on calculating the hash.*/ result ^= (unsigned)(data[pos + 0] << 0u); result ^= (unsigned)(data[pos + 1] << 4u); result ^= (unsigned)(data[pos + 2] << 8u); } else { size_t amount, i; if(pos >= size) return 0; amount = size - pos; for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); } return result & HASH_BIT_MASK; } static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) { const unsigned char* start = data + pos; const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; if(end > data + size) end = data + size; data = start; while(data != end && *data == 0) ++data; /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ return (unsigned)(data - start); } /*wpos = pos & (windowsize - 1)*/ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; hash->head[hashval] = wpos; hash->zeros[wpos] = numzeros; if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; hash->headz[numzeros] = wpos; } /* LZ77-encode the data. Return value is error code. The input are raw bytes, the output is in the form of unsigned integers with codes representing for example literal bytes, or length/distance pairs. It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a sliding window (of windowsize) is used, and all past bytes in that window can be used as the "dictionary". A brute force search through all possible distances would be slow, and this hash technique is one out of several ways to speed this up. */ static unsigned encodeLZ77(uivector* out, Hash* hash, const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, unsigned minmatch, unsigned nicematch, unsigned lazymatching) { size_t pos; unsigned i, error = 0; /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ unsigned numzeros = 0; unsigned offset; /*the offset represents the distance in LZ77 terminology*/ unsigned length; unsigned lazy = 0; unsigned lazylength = 0, lazyoffset = 0; unsigned hashval; unsigned current_offset, current_length; unsigned prev_offset; const unsigned char *lastptr, *foreptr, *backptr; unsigned hashpos; if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; for(pos = inpos; pos < insize; ++pos) { size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ unsigned chainlength = 0; hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); /*the length and offset found for the current position*/ length = 0; offset = 0; hashpos = hash->chain[wpos]; lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; /*search for the longest string*/ prev_offset = 0; for(;;) { if(chainlength++ >= maxchainlength) break; current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize; if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ prev_offset = current_offset; if(current_offset > 0) { /*test the next characters*/ foreptr = &in[pos]; backptr = &in[pos - current_offset]; /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ if(numzeros >= 3) { unsigned skip = hash->zeros[hashpos]; if(skip > numzeros) skip = numzeros; backptr += skip; foreptr += skip; } while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ { ++backptr; ++foreptr; } current_length = (unsigned)(foreptr - &in[pos]); if(current_length > length) { length = current_length; /*the longest length*/ offset = current_offset; /*the offset that is related to this longest length*/ /*jump out once a length of max length is found (speed gain). This also jumps out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ if(current_length >= nicematch) break; } } if(hashpos == hash->chain[hashpos]) break; if(numzeros >= 3 && length > numzeros) { hashpos = hash->chainz[hashpos]; if(hash->zeros[hashpos] != numzeros) break; } else { hashpos = hash->chain[hashpos]; /*outdated hash value, happens if particular value was not encountered in whole last window*/ if(hash->val[hashpos] != (int)hashval) break; } } if(lazymatching) { if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) { lazy = 1; lazylength = length; lazyoffset = offset; continue; /*try the next byte*/ } if(lazy) { lazy = 0; if(pos == 0) ERROR_BREAK(81); if(length > lazylength + 1) { /*push the previous character as literal*/ if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); } else { length = lazylength; offset = lazyoffset; hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ hash->headz[numzeros] = -1; /*idem*/ --pos; } } } if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); /*encode it as length/distance pair or literal value*/ if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ { if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else if(length < minmatch || (length == 3 && offset > 4096)) { /*compensate for the fact that longer offsets have more extra bits, a length of only 3 may be not worth it then*/ if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else { addLengthDistance(out, length, offset); for(i = 1; i < length; ++i) { ++pos; wpos = pos & (windowsize - 1); hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); } } } /*end of the loop through each character of input*/ return error; } /* /////////////////////////////////////////////////////////////////////////// */ static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) { /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; unsigned datapos = 0; for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; BFINAL = (i == numdeflateblocks - 1); BTYPE = 0; firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); ucvector_push_back(out, firstbyte); LEN = 65535; if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; ucvector_push_back(out, (unsigned char)(LEN & 255)); ucvector_push_back(out, (unsigned char)(LEN >> 8)); ucvector_push_back(out, (unsigned char)(NLEN & 255)); ucvector_push_back(out, (unsigned char)(NLEN >> 8)); /*Decompressed data*/ for(j = 0; j < 65535 && datapos < datasize; ++j) { ucvector_push_back(out, data[datapos++]); } } return 0; } /* write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. tree_ll: the tree for lit and len codes. tree_d: the tree for distance codes. */ static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { size_t i = 0; for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); if(val > 256) /*for a length code, 3 more things have to be added*/ { unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; unsigned length_extra_bits = lz77_encoded->data[++i]; unsigned distance_code = lz77_encoded->data[++i]; unsigned distance_index = distance_code; unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; unsigned distance_extra_bits = lz77_encoded->data[++i]; addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), HuffmanTree_getLength(tree_d, distance_code)); addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); } } } /*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { unsigned error = 0; /* A block is compressed as follows: The PNG data is lz77 encoded, resulting in literal bytes and length/distance pairs. This is then huffman compressed with two huffman trees. One huffman tree is used for the lit and len values ("ll"), another huffman tree is used for the dist values ("d"). These two trees are stored using their code lengths, and to compress even more these code lengths are also run-length encoded and huffman compressed. This gives a huffman tree of code lengths "cl". The code lenghts used to describe this third tree are the code length code lengths ("clcl"). */ /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ uivector lz77_encoded; HuffmanTree tree_ll; /*tree for lit,len values*/ HuffmanTree tree_d; /*tree for distance codes*/ HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ uivector frequencies_ll; /*frequency of lit,len codes*/ uivector frequencies_d; /*frequency of dist codes*/ uivector frequencies_cl; /*frequency of code length codes*/ uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl (these are written as is in the file, it would be crazy to compress these using yet another huffman tree that needs to be represented by yet another set of code lengths)*/ uivector bitlen_cl; size_t datasize = dataend - datapos; /* Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: bitlen_lld is to tree_cl what data is to tree_ll and tree_d. bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. */ unsigned BFINAL = final; size_t numcodes_ll, numcodes_d, i; unsigned HLIT, HDIST, HCLEN; uivector_init(&lz77_encoded); HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); HuffmanTree_init(&tree_cl); uivector_init(&frequencies_ll); uivector_init(&frequencies_d); uivector_init(&frequencies_cl); uivector_init(&bitlen_lld); uivector_init(&bitlen_lld_e); uivector_init(&bitlen_cl); /*This while loop never loops due to a break at the end, it is here to allow breaking out of it to the cleanup phase on error conditions.*/ while(!error) { if(settings->use_lz77) { error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(error) break; } else { if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); /*Count the frequencies of lit, len and dist codes*/ for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; ++frequencies_ll.data[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; ++frequencies_d.data[dist]; i += 3; } } frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); if(error) break; /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); if(error) break; numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; /*store the code lengths of both generated trees in bitlen_lld*/ for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ for(i = 0; i != (unsigned)bitlen_lld.size; ++i) { unsigned j = 0; /*amount of repititions*/ while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ { ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { uivector_push_back(&bitlen_lld_e, 17); uivector_push_back(&bitlen_lld_e, j - 3); } else /*repeat code 18 supports max 138 zeroes*/ { if(j > 138) j = 138; uivector_push_back(&bitlen_lld_e, 18); uivector_push_back(&bitlen_lld_e, j - 11); } i += (j - 1); } else if(j >= 3) /*repeat code for value other than zero*/ { size_t k; unsigned num = j / 6, rest = j % 6; uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); for(k = 0; k < num; ++k) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, 6 - 3); } if(rest >= 3) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, rest - 3); } else j -= rest; i += j; } else /*too short to benefit from repeat code*/ { uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); } } /*generate tree_cl, the huffmantree of huffmantrees*/ if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != bitlen_lld_e.size; ++i) { ++frequencies_cl.data[bitlen_lld_e.data[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ if(bitlen_lld_e.data[i] >= 16) ++i; } error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, frequencies_cl.size, frequencies_cl.size, 7); if(error) break; if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != tree_cl.numcodes; ++i) { /*lenghts of code length tree is in the order as specified by deflate*/ bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); } while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) { /*remove zeros at the end, but minimum size must be 4*/ if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); } if(error) break; /* Write everything into the output After the BFINAL and BTYPE, the dynamic block consists out of the following: - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN - (HCLEN+4)*3 bits code lengths of code length alphabet - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - HDIST + 1 code lengths of distance alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - compressed data - 256 (end code) */ /*Write block type*/ addBitToStream(bp, out, BFINAL); addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ /*write the HLIT, HDIST and HCLEN values*/ HLIT = (unsigned)(numcodes_ll - 257); HDIST = (unsigned)(numcodes_d - 1); HCLEN = (unsigned)bitlen_cl.size - 4; /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; addBitsToStream(bp, out, HLIT, 5); addBitsToStream(bp, out, HDIST, 5); addBitsToStream(bp, out, HCLEN, 4); /*write the code lenghts of the code length alphabet*/ for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); /*write the lenghts of the lit/len AND the dist alphabet*/ for(i = 0; i != bitlen_lld_e.size; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); /*extra bits of repeat codes*/ if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); } /*write the compressed data symbols*/ writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); /*error: the length of the end code 256 must be larger than 0*/ if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); /*write the end code*/ addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); break; /*end of error-while*/ } /*cleanup*/ uivector_cleanup(&lz77_encoded); HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); HuffmanTree_cleanup(&tree_cl); uivector_cleanup(&frequencies_ll); uivector_cleanup(&frequencies_d); uivector_cleanup(&frequencies_cl); uivector_cleanup(&bitlen_lld_e); uivector_cleanup(&bitlen_lld); uivector_cleanup(&bitlen_cl); return error; } static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { HuffmanTree tree_ll; /*tree for literal values and length codes*/ HuffmanTree tree_d; /*tree for distance codes*/ unsigned BFINAL = final; unsigned error = 0; size_t i; HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); generateFixedLitLenTree(&tree_ll); generateFixedDistanceTree(&tree_d); addBitToStream(bp, out, BFINAL); addBitToStream(bp, out, 1); /*first bit of BTYPE*/ addBitToStream(bp, out, 0); /*second bit of BTYPE*/ if(settings->use_lz77) /*LZ77 encoded*/ { uivector lz77_encoded; uivector_init(&lz77_encoded); error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); uivector_cleanup(&lz77_encoded); } else /*no LZ77, but still will be Huffman compressed*/ { for(i = datapos; i < dataend; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); } } /*add END code*/ if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); /*cleanup*/ HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { unsigned error = 0; size_t i, blocksize, numdeflateblocks; size_t bp = 0; /*the bit pointer*/ Hash hash; if(settings->btype > 2) return 61; else if(settings->btype == 0) return deflateNoCompression(out, in, insize); else if(settings->btype == 1) blocksize = insize; else /*if(settings->btype == 2)*/ { /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ blocksize = insize / 8 + 8; if(blocksize < 65536) blocksize = 65536; if(blocksize > 262144) blocksize = 262144; } numdeflateblocks = (insize + blocksize - 1) / blocksize; if(numdeflateblocks == 0) numdeflateblocks = 1; error = hash_init(&hash, settings->windowsize); if(error) return error; for(i = 0; i != numdeflateblocks && !error; ++i) { unsigned final = (i == numdeflateblocks - 1); size_t start = i * blocksize; size_t end = start + blocksize; if(end > insize) end = insize; if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); } hash_cleanup(&hash); return error; } unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { unsigned error; ucvector v; ucvector_init_buffer(&v, *out, *outsize); error = lodepng_deflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_deflate) { return settings->custom_deflate(out, outsize, in, insize, settings); } else { return lodepng_deflate(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / Adler32 */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { unsigned s1 = adler & 0xffff; unsigned s2 = (adler >> 16) & 0xffff; while(len > 0) { /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ unsigned amount = len > 5550 ? 5550 : len; len -= amount; while(amount > 0) { s1 += (*data++); s2 += s1; --amount; } s1 %= 65521; s2 %= 65521; } return (s2 << 16) | s1; } /*Return the adler32 of the bytes data[0..len-1]*/ static unsigned adler32(const unsigned char* data, unsigned len) { return update_adler32(1L, data, len); } /* ////////////////////////////////////////////////////////////////////////// */ /* / Zlib / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DECODER unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error = 0; unsigned CM, CINFO, FDICT; if(insize < 2) return 53; /*error, size of zlib data too small*/ /*read information from zlib header*/ if((in[0] * 256 + in[1]) % 31 != 0) { /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ return 24; } CM = in[0] & 15; CINFO = (in[0] >> 4) & 15; /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ FDICT = (in[1] >> 5) & 1; /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ if(CM != 8 || CINFO > 7) { /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ return 25; } if(FDICT != 0) { /*error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary."*/ return 26; } error = inflate(out, outsize, in + 2, insize - 2, settings); if(error) return error; if(!settings->ignore_adler32) { unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); unsigned checksum = adler32(*out, (unsigned)(*outsize)); if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ } return 0; /*no error*/ } static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { return lodepng_zlib_decompress(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { /*initially, *out must be NULL and outsize 0, if you just give some random *out that's pointing to a non allocated buffer, this'll crash*/ ucvector outv; size_t i; unsigned error; unsigned char* deflatedata = 0; size_t deflatesize = 0; /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ unsigned FLEVEL = 0; unsigned FDICT = 0; unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; unsigned FCHECK = 31 - CMFFLG % 31; CMFFLG += FCHECK; /*ucvector-controlled version of the output buffer, for dynamic array*/ ucvector_init_buffer(&outv, *out, *outsize); ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); error = deflate(&deflatedata, &deflatesize, in, insize, settings); if(!error) { unsigned ADLER32 = adler32(in, (unsigned)insize); for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); lodepng_free(deflatedata); lodepng_add32bitInt(&outv, ADLER32); } *out = outv.data; *outsize = outv.size; return error; } /* compress using the default or custom zlib function */ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { return lodepng_zlib_compress(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_ENCODER*/ #else /*no LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DECODER static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ENCODER /*this is a good tradeoff between speed and compression ratio*/ #define DEFAULT_WINDOWSIZE 2048 void lodepng_compress_settings_init(LodePNGCompressSettings* settings) { /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ settings->btype = 2; settings->use_lz77 = 1; settings->windowsize = DEFAULT_WINDOWSIZE; settings->minmatch = 3; settings->nicematch = 128; settings->lazymatching = 1; settings->custom_zlib = 0; settings->custom_deflate = 0; settings->custom_context = 0; } const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { settings->ignore_adler32 = 0; settings->custom_zlib = 0; settings->custom_inflate = 0; settings->custom_context = 0; } const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of Zlib related code. Begin of PNG related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG /* ////////////////////////////////////////////////////////////////////////// */ /* / CRC32 / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifndef LODEPNG_NO_COMPILE_CRC /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; /*Return the CRC of the bytes buf[0..len-1].*/ unsigned lodepng_crc32(const unsigned char* data, size_t length) { unsigned r = 0xffffffffu; size_t i; for(i = 0; i < length; ++i) { r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); } return r ^ 0xffffffffu; } #else /* !LODEPNG_NO_COMPILE_CRC */ unsigned lodepng_crc32(const unsigned char* data, size_t length); #endif /* !LODEPNG_NO_COMPILE_CRC */ /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); ++(*bitpointer); return result; } static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0; size_t i; for(i = 0 ; i < nbits; ++i) { result <<= 1; result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); } return result; } #ifdef LODEPNG_COMPILE_DECODER static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream must be 0 for this to work*/ if(bit) { /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); } ++(*bitpointer); } #endif /*LODEPNG_COMPILE_DECODER*/ static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream may be 0 or 1 for this to work*/ if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG chunks / */ /* ////////////////////////////////////////////////////////////////////////// */ unsigned lodepng_chunk_length(const unsigned char* chunk) { return lodepng_read32bitInt(&chunk[0]); } void lodepng_chunk_type(char type[5], const unsigned char* chunk) { unsigned i; for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; type[4] = 0; /*null termination char*/ } unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { if(strlen(type) != 4) return 0; return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); } unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { return((chunk[4] & 32) != 0); } unsigned char lodepng_chunk_private(const unsigned char* chunk) { return((chunk[6] & 32) != 0); } unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) { return((chunk[7] & 32) != 0); } unsigned char* lodepng_chunk_data(unsigned char* chunk) { return &chunk[8]; } const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { return &chunk[8]; } unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ unsigned checksum = lodepng_crc32(&chunk[4], length + 4); if(CRC != checksum) return 1; else return 0; } void lodepng_chunk_generate_crc(unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_crc32(&chunk[4], length + 4); lodepng_set32bitInt(chunk + 8 + length, CRC); } unsigned char* lodepng_chunk_next(unsigned char* chunk) { unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; return &chunk[total_chunk_length]; } const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) { unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; return &chunk[total_chunk_length]; } unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) { unsigned i; unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; unsigned char *chunk_start, *new_buffer; size_t new_length = (*outlength) + total_chunk_length; if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; (*outlength) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; return 0; } unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data) { unsigned i; unsigned char *chunk, *new_buffer; size_t new_length = (*outlength) + length + 12; if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; (*outlength) = new_length; chunk = &(*out)[(*outlength) - length - 12]; /*1: length*/ lodepng_set32bitInt(chunk, (unsigned)length); /*2: chunk name (4 letters)*/ chunk[4] = (unsigned char)type[0]; chunk[5] = (unsigned char)type[1]; chunk[6] = (unsigned char)type[2]; chunk[7] = (unsigned char)type[3]; /*3: the data*/ for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); return 0; } /* ////////////////////////////////////////////////////////////////////////// */ /* / Color types and such / */ /* ////////////////////////////////////////////////////////////////////////// */ /*return type is a LodePNG error code*/ static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ { switch(colortype) { case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ default: return 31; } return 0; /*allowed color type / bits combination*/ } static unsigned getNumColorChannels(LodePNGColorType colortype) { switch(colortype) { case 0: return 1; /*grey*/ case 2: return 3; /*RGB*/ case 3: return 1; /*palette*/ case 4: return 2; /*grey + alpha*/ case 6: return 4; /*RGBA*/ } return 0; /*unexisting color type*/ } static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { /*bits per pixel is amount of channels * bits per channel*/ return getNumColorChannels(colortype) * bitdepth; } /* ////////////////////////////////////////////////////////////////////////// */ void lodepng_color_mode_init(LodePNGColorMode* info) { info->key_defined = 0; info->key_r = info->key_g = info->key_b = 0; info->colortype = LCT_RGBA; info->bitdepth = 8; info->palette = 0; info->palettesize = 0; } void lodepng_color_mode_cleanup(LodePNGColorMode* info) { lodepng_palette_clear(info); } unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { size_t i; lodepng_color_mode_cleanup(dest); *dest = *source; if(source->palette) { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; } return 0; } static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { size_t i; if(a->colortype != b->colortype) return 0; if(a->bitdepth != b->bitdepth) return 0; if(a->key_defined != b->key_defined) return 0; if(a->key_defined) { if(a->key_r != b->key_r) return 0; if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } /*if one of the palette sizes is 0, then we consider it to be the same as the other: it means that e.g. the palette was not given by the user and should be considered the same as the palette inside the PNG.*/ if(1/*a->palettesize != 0 && b->palettesize != 0*/) { if(a->palettesize != b->palettesize) return 0; for(i = 0; i != a->palettesize * 4; ++i) { if(a->palette[i] != b->palette[i]) return 0; } } return 1; } void lodepng_palette_clear(LodePNGColorMode* info) { if(info->palette) lodepng_free(info->palette); info->palette = 0; info->palettesize = 0; } unsigned lodepng_palette_add(LodePNGColorMode* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { unsigned char* data; /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with the max of 256 colors, it'll have the exact alloc size*/ if(!info->palette) /*allocate palette if empty*/ { /*room for 256 colors with 4 bytes each*/ data = (unsigned char*)lodepng_realloc(info->palette, 1024); if(!data) return 83; /*alloc fail*/ else info->palette = data; } info->palette[4 * info->palettesize + 0] = r; info->palette[4 * info->palettesize + 1] = g; info->palette[4 * info->palettesize + 2] = b; info->palette[4 * info->palettesize + 3] = a; ++info->palettesize; return 0; } unsigned lodepng_get_bpp(const LodePNGColorMode* info) { /*calculate bits per pixel out of colortype and bitdepth*/ return lodepng_get_bpp_lct(info->colortype, info->bitdepth); } unsigned lodepng_get_channels(const LodePNGColorMode* info) { return getNumColorChannels(info->colortype); } unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) { return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; } unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) { return (info->colortype & 4) != 0; /*4 or 6*/ } unsigned lodepng_is_palette_type(const LodePNGColorMode* info) { return info->colortype == LCT_PALETTE; } unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { size_t i; for(i = 0; i != info->palettesize; ++i) { if(info->palette[i * 4 + 3] < 255) return 1; } return 0; } unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) { return info->key_defined || lodepng_is_alpha_type(info) || lodepng_has_palette_alpha(info); } size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); size_t n = w * h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_DECODER /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; return h * line; } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static void LodePNGUnknownChunks_init(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; } static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); } static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) { unsigned i; LodePNGUnknownChunks_cleanup(dest); for(i = 0; i != 3; ++i) { size_t j; dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ for(j = 0; j < src->unknown_chunks_size[i]; ++j) { dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; } } return 0; } /******************************************************************************/ static void LodePNGText_init(LodePNGInfo* info) { info->text_num = 0; info->text_keys = NULL; info->text_strings = NULL; } static void LodePNGText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->text_num; ++i) { string_cleanup(&info->text_keys[i]); string_cleanup(&info->text_strings[i]); } lodepng_free(info->text_keys); lodepng_free(info->text_strings); } static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->text_keys = 0; dest->text_strings = 0; dest->text_num = 0; for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); } return 0; } void lodepng_clear_text(LodePNGInfo* info) { LodePNGText_cleanup(info); } unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); if(!new_keys || !new_strings) { lodepng_free(new_keys); lodepng_free(new_strings); return 83; /*alloc fail*/ } ++info->text_num; info->text_keys = new_keys; info->text_strings = new_strings; string_init(&info->text_keys[info->text_num - 1]); string_set(&info->text_keys[info->text_num - 1], key); string_init(&info->text_strings[info->text_num - 1]); string_set(&info->text_strings[info->text_num - 1], str); return 0; } /******************************************************************************/ static void LodePNGIText_init(LodePNGInfo* info) { info->itext_num = 0; info->itext_keys = NULL; info->itext_langtags = NULL; info->itext_transkeys = NULL; info->itext_strings = NULL; } static void LodePNGIText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->itext_num; ++i) { string_cleanup(&info->itext_keys[i]); string_cleanup(&info->itext_langtags[i]); string_cleanup(&info->itext_transkeys[i]); string_cleanup(&info->itext_strings[i]); } lodepng_free(info->itext_keys); lodepng_free(info->itext_langtags); lodepng_free(info->itext_transkeys); lodepng_free(info->itext_strings); } static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->itext_keys = 0; dest->itext_langtags = 0; dest->itext_transkeys = 0; dest->itext_strings = 0; dest->itext_num = 0; for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], source->itext_transkeys[i], source->itext_strings[i])); } return 0; } void lodepng_clear_itext(LodePNGInfo* info) { LodePNGIText_cleanup(info); } unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str) { char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); if(!new_keys || !new_langtags || !new_transkeys || !new_strings) { lodepng_free(new_keys); lodepng_free(new_langtags); lodepng_free(new_transkeys); lodepng_free(new_strings); return 83; /*alloc fail*/ } ++info->itext_num; info->itext_keys = new_keys; info->itext_langtags = new_langtags; info->itext_transkeys = new_transkeys; info->itext_strings = new_strings; string_init(&info->itext_keys[info->itext_num - 1]); string_set(&info->itext_keys[info->itext_num - 1], key); string_init(&info->itext_langtags[info->itext_num - 1]); string_set(&info->itext_langtags[info->itext_num - 1], langtag); string_init(&info->itext_transkeys[info->itext_num - 1]); string_set(&info->itext_transkeys[info->itext_num - 1], transkey); string_init(&info->itext_strings[info->itext_num - 1]); string_set(&info->itext_strings[info->itext_num - 1], str); return 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ void lodepng_info_init(LodePNGInfo* info) { lodepng_color_mode_init(&info->color); info->interlace_method = 0; info->compression_method = 0; info->filter_method = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS info->background_defined = 0; info->background_r = info->background_g = info->background_b = 0; LodePNGText_init(info); LodePNGIText_init(info); info->time_defined = 0; info->phys_defined = 0; LodePNGUnknownChunks_init(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } void lodepng_info_cleanup(LodePNGInfo* info) { lodepng_color_mode_cleanup(&info->color); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS LodePNGText_cleanup(info); LodePNGIText_cleanup(info); LodePNGUnknownChunks_cleanup(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { lodepng_info_cleanup(dest); *dest = *source; lodepng_color_mode_init(&dest->color); CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); LodePNGUnknownChunks_init(dest); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ return 0; } /* ////////////////////////////////////////////////////////////////////////// */ /*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ unsigned p = index & m; in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ in = in << (bits * (m - p)); if(p == 0) out[index * bits / 8] = in; else out[index * bits / 8] |= in; } typedef struct ColorTree ColorTree; /* One node of a color tree This is the data structure used to count the number of unique colors and to get a palette index for a color. It's like an octree, but because the alpha channel is used too, each node has 16 instead of 8 children. */ struct ColorTree { ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ int index; /*the payload. Only has a meaningful value if this is in the last level*/ }; static void color_tree_init(ColorTree* tree) { int i; for(i = 0; i != 16; ++i) tree->children[i] = 0; tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; for(i = 0; i != 16; ++i) { if(tree->children[i]) { color_tree_cleanup(tree->children[i]); lodepng_free(tree->children[i]); } } } /*returns -1 if color not present, its index otherwise*/ static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) return -1; else tree = tree->children[i]; } return tree ? tree->index : -1; } #ifdef LODEPNG_COMPILE_ENCODER static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { return color_tree_get(tree, r, g, b, a) >= 0; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*color is not allowed to already exist. Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ static void color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) { tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); color_tree_init(tree->children[i]); } tree = tree->children[i]; } tree->index = (int)index; } /*put a pixel, given its RGBA color, into image of any color type*/ static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { if(mode->colortype == LCT_GREY) { unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; if(mode->bitdepth == 8) out[i] = grey; else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; else { /*take the most significant bits of grey*/ grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); addColorBits(out, i, mode->bitdepth, grey); } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { out[i * 3 + 0] = r; out[i * 3 + 1] = g; out[i * 3 + 2] = b; } else { out[i * 6 + 0] = out[i * 6 + 1] = r; out[i * 6 + 2] = out[i * 6 + 3] = g; out[i * 6 + 4] = out[i * 6 + 5] = b; } } else if(mode->colortype == LCT_PALETTE) { int index = color_tree_get(tree, r, g, b, a); if(index < 0) return 82; /*color not in palette*/ if(mode->bitdepth == 8) out[i] = index; else addColorBits(out, i, mode->bitdepth, (unsigned)index); } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; if(mode->bitdepth == 8) { out[i * 2 + 0] = grey; out[i * 2 + 1] = a; } else if(mode->bitdepth == 16) { out[i * 4 + 0] = out[i * 4 + 1] = grey; out[i * 4 + 2] = out[i * 4 + 3] = a; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { out[i * 4 + 0] = r; out[i * 4 + 1] = g; out[i * 4 + 2] = b; out[i * 4 + 3] = a; } else { out[i * 8 + 0] = out[i * 8 + 1] = r; out[i * 8 + 2] = out[i * 8 + 3] = g; out[i * 8 + 4] = out[i * 8 + 5] = b; out[i * 8 + 6] = out[i * 8 + 7] = a; } } return 0; /*no error*/ } /*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a) { if(mode->colortype == LCT_GREY) { unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; out[i * 2 + 0] = (grey >> 8) & 255; out[i * 2 + 1] = grey & 255; } else if(mode->colortype == LCT_RGB) { out[i * 6 + 0] = (r >> 8) & 255; out[i * 6 + 1] = r & 255; out[i * 6 + 2] = (g >> 8) & 255; out[i * 6 + 3] = g & 255; out[i * 6 + 4] = (b >> 8) & 255; out[i * 6 + 5] = b & 255; } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; out[i * 4 + 0] = (grey >> 8) & 255; out[i * 4 + 1] = grey & 255; out[i * 4 + 2] = (a >> 8) & 255; out[i * 4 + 3] = a & 255; } else if(mode->colortype == LCT_RGBA) { out[i * 8 + 0] = (r >> 8) & 255; out[i * 8 + 1] = r & 255; out[i * 8 + 2] = (g >> 8) & 255; out[i * 8 + 3] = g & 255; out[i * 8 + 4] = (b >> 8) & 255; out[i * 8 + 5] = b & 255; out[i * 8 + 6] = (a >> 8) & 255; out[i * 8 + 7] = a & 255; } } /*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { *r = *g = *b = in[i]; if(mode->key_defined && *r == mode->key_r) *a = 0; else *a = 255; } else if(mode->bitdepth == 16) { *r = *g = *b = in[i * 2 + 0]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 255; } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = i * mode->bitdepth; unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); *r = *g = *b = (value * 255) / highest; if(mode->key_defined && value == mode->key_r) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; else *a = 255; } else { *r = in[i * 6 + 0]; *g = in[i * 6 + 2]; *b = in[i * 6 + 4]; if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_PALETTE) { unsigned index; if(mode->bitdepth == 8) index = in[i]; else { size_t j = i * mode->bitdepth; index = readBitsFromReversedStream(&j, in, mode->bitdepth); } if(index >= mode->palettesize) { /*This is an error according to the PNG spec, but common PNG decoders make it black instead. Done here too, slightly faster due to no error handling needed.*/ *r = *g = *b = 0; *a = 255; } else { *r = mode->palette[index * 4 + 0]; *g = mode->palette[index * 4 + 1]; *b = mode->palette[index * 4 + 2]; *a = mode->palette[index * 4 + 3]; } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { *r = *g = *b = in[i * 2 + 0]; *a = in[i * 2 + 1]; } else { *r = *g = *b = in[i * 4 + 0]; *a = in[i * 4 + 2]; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { *r = in[i * 4 + 0]; *g = in[i * 4 + 1]; *b = in[i * 4 + 2]; *a = in[i * 4 + 3]; } else { *r = in[i * 8 + 0]; *g = in[i * 8 + 2]; *b = in[i * 8 + 4]; *a = in[i * 8 + 6]; } } } /*Similar to getPixelColorRGBA8, but with all the for loops inside of the color mode test cases, optimized to convert the colors much faster, when converting to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with enough memory, if has_alpha is true the output is RGBA. mode has the color mode of the input buffer.*/ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, unsigned has_alpha, const unsigned char* in, const LodePNGColorMode* mode) { unsigned num_channels = has_alpha ? 4 : 3; size_t i; if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; } } else if(mode->bitdepth == 16) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; } } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 3 + 0]; buffer[1] = in[i * 3 + 1]; buffer[2] = in[i * 3 + 2]; if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; } } } else if(mode->colortype == LCT_PALETTE) { unsigned index; size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(mode->bitdepth == 8) index = in[i]; else index = readBitsFromReversedStream(&j, in, mode->bitdepth); if(index >= mode->palettesize) { /*This is an error according to the PNG spec, but most PNG decoders make it black instead. Done here too, slightly faster due to no error handling needed.*/ buffer[0] = buffer[1] = buffer[2] = 0; if(has_alpha) buffer[3] = 255; } else { buffer[0] = mode->palette[index * 4 + 0]; buffer[1] = mode->palette[index * 4 + 1]; buffer[2] = mode->palette[index * 4 + 2]; if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; } } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; if(has_alpha) buffer[3] = in[i * 2 + 1]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; if(has_alpha) buffer[3] = in[i * 4 + 2]; } } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 4 + 0]; buffer[1] = in[i * 4 + 1]; buffer[2] = in[i * 4 + 2]; if(has_alpha) buffer[3] = in[i * 4 + 3]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; if(has_alpha) buffer[3] = in[i * 8 + 6]; } } } } /*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with given color type, but the given color type must be 16-bit itself.*/ static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_RGB) { *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; if(mode->key_defined && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_GREY_ALPHA) { *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if(mode->colortype == LCT_RGBA) { *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } } unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) { size_t i; ColorTree tree; size_t numpixels = w * h; unsigned error = 0; if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); for(i = 0; i != numbytes; ++i) out[i] = in[i]; return 0; } if(mode_out->colortype == LCT_PALETTE) { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; size_t palsize = 1u << mode_out->bitdepth; /*if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ if(palettesize == 0) { palettesize = mode_in->palettesize; palette = mode_in->palette; } if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); for(i = 0; i != palsize; ++i) { const unsigned char* p = &palette[i * 4]; color_tree_add(&tree, p[0], p[1], p[2], p[3], i); } } if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { for(i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); rgba16ToPixel(out, i, mode_out, r, g, b, a); } } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); } else { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); if (error) break; } } if(mode_out->colortype == LCT_PALETTE) { color_tree_cleanup(&tree); } return error; } #ifdef LODEPNG_COMPILE_ENCODER void lodepng_color_profile_init(LodePNGColorProfile* profile) { profile->colored = 0; profile->key = 0; profile->key_r = profile->key_g = profile->key_b = 0; profile->alpha = 0; profile->numcolors = 0; profile->bits = 1; } /*function used for debug purposes with C++*/ /*void printColorProfile(LodePNGColorProfile* p) { std::cout << "colored: " << (int)p->colored << ", "; std::cout << "key: " << (int)p->key << ", "; std::cout << "key_r: " << (int)p->key_r << ", "; std::cout << "key_g: " << (int)p->key_g << ", "; std::cout << "key_b: " << (int)p->key_b << ", "; std::cout << "alpha: " << (int)p->alpha << ", "; std::cout << "numcolors: " << (int)p->numcolors << ", "; std::cout << "bits: " << (int)p->bits << std::endl; }*/ /*Returns how many bits needed to represent given value (max 8 bit)*/ static unsigned getValueRequiredBits(unsigned char value) { if(value == 0 || value == 255) return 1; /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; return 8; } /*profile must already have been inited with mode. It's ok to set some parameters of profile to done already.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* mode) { unsigned error = 0; size_t i; ColorTree tree; size_t numpixels = w * h; unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; unsigned numcolors_done = 0; unsigned bpp = lodepng_get_bpp(mode); unsigned bits_done = bpp == 1 ? 1 : 0; unsigned maxnumcolors = 257; unsigned sixteen = 0; if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); color_tree_init(&tree); /*Check if the 16-bit input is truly 16-bit*/ if(mode->bitdepth == 16) { unsigned short r, g, b, a; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { sixteen = 1; break; } } } if(sixteen) { unsigned short r = 0, g = 0, b = 0, a = 0; profile->bits = 16; bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if(!colored_done && (r != g || r != b)) { profile->colored = 1; colored_done = 1; } if(!alpha_done) { unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); if(a != 65535 && (a != 0 || (profile->key && !matchkey))) { profile->alpha = 1; profile->key = 0; alpha_done = 1; } else if(a == 0 && !profile->alpha && !profile->key) { profile->key = 1; profile->key_r = r; profile->key_g = g; profile->key_b = b; } else if(a == 65535 && profile->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(profile->key && !profile->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; } } } } else /* < 16-bit */ { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); if(!bits_done && profile->bits < 8) { /*only r is checked, < 8 bits is only relevant for greyscale*/ unsigned bits = getValueRequiredBits(r); if(bits > profile->bits) profile->bits = bits; } bits_done = (profile->bits >= bpp); if(!colored_done && (r != g || r != b)) { profile->colored = 1; colored_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ } if(!alpha_done) { unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); if(a != 255 && (a != 0 || (profile->key && !matchkey))) { profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } else if(a == 0 && !profile->alpha && !profile->key) { profile->key = 1; profile->key_r = r; profile->key_g = g; profile->key_b = b; } else if(a == 255 && profile->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } if(!numcolors_done) { if(!color_tree_has(&tree, r, g, b, a)) { color_tree_add(&tree, r, g, b, a, profile->numcolors); if(profile->numcolors < 256) { unsigned char* p = profile->palette; unsigned n = profile->numcolors; p[n * 4 + 0] = r; p[n * 4 + 1] = g; p[n * 4 + 2] = b; p[n * 4 + 3] = a; } ++profile->numcolors; numcolors_done = profile->numcolors >= maxnumcolors; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(profile->key && !profile->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } } /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ profile->key_r += (profile->key_r << 8); profile->key_g += (profile->key_g << 8); profile->key_b += (profile->key_b << 8); } color_tree_cleanup(&tree); return error; } /*Automatically chooses color type that gives smallest amount of bits in the output image, e.g. grey if there are only greyscale pixels, palette if there are less than 256 colors, ... Updates values of mode with a potentially smaller color model. mode_out should contain the user chosen color model, but will be overwritten with the new chosen one.*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in) { LodePNGColorProfile prof; unsigned error = 0; unsigned i, n, palettebits, palette_ok; lodepng_color_profile_init(&prof); error = lodepng_get_color_profile(&prof, image, w, h, mode_in); if(error) return error; mode_out->key_defined = 0; if(prof.key && w * h <= 16) { prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ prof.key = 0; if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } n = prof.numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); palette_ok = n <= 256 && prof.bits <= 8; if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ if(palette_ok) { unsigned char* p = prof.palette; lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ for(i = 0; i != prof.numcolors; ++i) { error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); if(error) break; } mode_out->colortype = LCT_PALETTE; mode_out->bitdepth = palettebits; if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize && mode_in->bitdepth == mode_out->bitdepth) { /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ lodepng_color_mode_cleanup(mode_out); lodepng_color_mode_copy(mode_out, mode_in); } } else /*8-bit or 16-bit per channel*/ { mode_out->bitdepth = prof.bits; mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) : (prof.colored ? LCT_RGB : LCT_GREY); if(prof.key) { unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ mode_out->key_r = prof.key_r & mask; mode_out->key_g = prof.key_g & mask; mode_out->key_b = prof.key_b & mask; mode_out->key_defined = 1; } } return error; } #endif /* #ifdef LODEPNG_COMPILE_ENCODER */ /* Paeth predicter, used by PNG filter type 4 The parameters are of type short, but should come from unsigned chars, the shorts are only needed to make the paeth calculation correct. */ static unsigned char paethPredictor(short a, short b, short c) { short pa = abs(b - c); short pb = abs(a - c); short pc = abs(a + b - c - c); if(pc < pa && pc < pb) return (unsigned char)c; else if(pb < pa) return (unsigned char)b; else return (unsigned char)a; } /*shared values used by multiple Adam7 related functions*/ static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ /* Outputs various dimensions and positions in the image related to the Adam7 reduced images. passw: output containing the width of the 7 passes passh: output containing the height of the 7 passes filter_passstart: output containing the index of the start and end of each reduced image with filter bytes padded_passstart output containing the index of the start and end of each reduced image when without filter bytes but with padded scanlines passstart: output containing the index of the start and end of each reduced image without padding between scanlines, but still padding between the images w, h: width and height of non-interlaced image bpp: bits per pixel "padded" is only relevant if bpp is less than 8 and a scanline or image does not end at a full byte */ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ unsigned i; /*calculate width and height in pixels of each pass*/ for(i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; if(passw[i] == 0) passh[i] = 0; if(passh[i] == 0) passw[i] = 0; } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; for(i = 0; i != 7; ++i) { /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); /*bits padded if needed to fill full byte at end of each scanline*/ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); /*only padded at end of reduced image*/ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; } } #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Decoder / */ /* ////////////////////////////////////////////////////////////////////////// */ /*read the information from the header and store it in the LodePNGInfo. return value is error*/ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { LodePNGInfo* info = &state->info_png; if(insize == 0 || in == 0) { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } if(insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ lodepng_info_cleanup(info); lodepng_info_init(info); if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } if(lodepng_chunk_length(in + 8) != 13) { CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ } if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } /*read the values given in the header*/ *w = lodepng_read32bitInt(&in[16]); *h = lodepng_read32bitInt(&in[20]); info->color.bitdepth = in[24]; info->color.colortype = (LodePNGColorType)in[25]; info->compression_method = in[26]; info->filter_method = in[27]; info->interlace_method = in[28]; if(*w == 0 || *h == 0) { CERROR_RETURN_ERROR(state->error, 93); } if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); unsigned checksum = lodepng_crc32(&in[12], 17); if(CRC != checksum) { CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ } } /*error: only compression method 0 is allowed in the specification*/ if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); /*error: only filter method 0 is allowed in the specification*/ if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); /*error: only interlace methods 0 and 1 exist in the specification*/ if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); return state->error; } static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) { /* For PNG filter method 0 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) precon is the previous unfiltered scanline, recon the result, scanline the current one the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead recon and scanline MAY be the same memory address! precon must be disjoint. */ size_t i; switch(filterType) { case 0: for(i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if(precon) { for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if(precon) { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); } else { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); } break; case 4: if(precon) { for(i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } for(i = bytewidth; i < length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } for(i = bytewidth; i < length; ++i) { /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ recon[i] = (scanline[i] + recon[i - bytewidth]); } } break; default: return 36; /*error: unexisting filter type given*/ } return 0; } static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { /* For PNG filter method 0 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */ unsigned y; unsigned char* prevline = 0; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7) / 8; size_t linebytes = (w * bpp + 7) / 8; for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ unsigned char filterType = in[inindex]; CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); prevline = &out[outindex]; } return 0; } /* in: Adam7 interlaced image, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h bpp: bits per pixel out has the following size in bits: w * h * bpp. in is possibly bigger due to padding bits between reduced images. out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster) NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ setBitOfReversedStream0(&obp, out, bit); } } } } } static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 only useful if (ilinebits - olinebits) is a value in the range 1..7 */ unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ for(y = 0; y < h; ++y) { size_t x; for(x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } ibp += diff; } } /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks (with filter index bytes and possible padding bits) return value is error*/ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png) { /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace NOTE: the in buffer will be overwritten with intermediate data! */ unsigned bpp = lodepng_get_bpp(&info_png->color); if(bpp == 0) return 31; /*error: invalid colortype*/ if(info_png->interlace_method == 0) { if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); } /*we can immediately filter into the out buffer, no other steps needed*/ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); for(i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/ if(bpp < 8) { /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]); } } Adam7_deinterlace(out, in, w, h, bpp); } return 0; } static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned pos = 0, i; if(color->palette) lodepng_free(color->palette); color->palettesize = chunkLength / 3; color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); if(!color->palette && color->palettesize) { color->palettesize = 0; return 83; /*alloc fail*/ } if(color->palettesize > 256) return 38; /*error: palette too big*/ for(i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ color->palette[4 * i + 2] = data[pos++]; /*B*/ color->palette[4 * i + 3] = 255; /*alpha*/ } return 0; /* OK */ } static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned i; if(color->colortype == LCT_PALETTE) { /*error: more alpha values given than there are palette entries*/ if(chunkLength > color->palettesize) return 38; for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if(color->colortype == LCT_GREY) { /*error: this chunk must be 2 bytes for greyscale image*/ if(chunkLength != 2) return 30; color->key_defined = 1; color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; } else if(color->colortype == LCT_RGB) { /*error: this chunk must be 6 bytes for RGB image*/ if(chunkLength != 6) return 41; color->key_defined = 1; color->key_r = 256u * data[0] + data[1]; color->key_g = 256u * data[2] + data[3]; color->key_b = 256u * data[4] + data[5]; } else return 42; /*error: tRNS chunk not allowed for other color models*/ return 0; /* OK */ } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(info->color.colortype == LCT_PALETTE) { /*error: this chunk must be 1 byte for indexed color image*/ if(chunkLength != 1) return 43; info->background_defined = 1; info->background_r = info->background_g = info->background_b = data[0]; } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { /*error: this chunk must be 2 bytes for greyscale image*/ if(chunkLength != 2) return 44; info->background_defined = 1; info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { /*error: this chunk must be 6 bytes for greyscale image*/ if(chunkLength != 6) return 45; info->background_defined = 1; info->background_r = 256u * data[0] + data[1]; info->background_g = 256u * data[2] + data[3]; info->background_b = 256u * data[4] + data[5]; } return 0; /* OK */ } /*text chunk (tEXt)*/ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { unsigned error = 0; char *key = 0, *str = 0; unsigned i; while(!error) /*not really a while loop, only used to break on error*/ { unsigned length, string2_begin; length = 0; while(length < chunkLength && data[length] != 0) ++length; /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; string2_begin = length + 1; /*skip keyword null terminator*/ length = chunkLength < string2_begin ? 0 : chunkLength - string2_begin; str = (char*)lodepng_malloc(length + 1); if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ str[length] = 0; for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; error = lodepng_add_text(info, key, str); break; } lodepng_free(key); lodepng_free(str); return error; } /*compressed text chunk (zTXt)*/ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; unsigned length, string2_begin; char *key = 0; ucvector decoded; ucvector_init(&decoded); while(!error) /*not really a while loop, only used to break on error*/ { for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ string2_begin = length + 2; if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ length = chunkLength - string2_begin; /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&decoded.data, &decoded.size, (const unsigned char*)(&data[string2_begin]), length, zlibsettings); if(error) break; ucvector_push_back(&decoded, 0); error = lodepng_add_text(info, key, (char*)decoded.data); break; } lodepng_free(key); ucvector_cleanup(&decoded); return error; } /*international text chunk (iTXt)*/ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; ucvector decoded; ucvector_init(&decoded); while(!error) /*not really a while loop, only used to break on error*/ { /*Quick check if the chunk length isn't too small. Even without check it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ /*read the key*/ for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; /*read the compression method*/ compressed = data[length + 1]; if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty for the next 3 texts*/ /*read the langtag*/ begin = length + 3; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ langtag[length] = 0; for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; /*read the transkey*/ begin += length + 1; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ transkey[length] = 0; for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; /*read the actual text*/ begin += length + 1; length = chunkLength < begin ? 0 : chunkLength - begin; if(compressed) { /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&decoded.data, &decoded.size, (const unsigned char*)(&data[begin]), length, zlibsettings); if(error) break; if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; ucvector_push_back(&decoded, 0); } else { if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); decoded.data[length] = 0; for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; } error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); break; } lodepng_free(key); lodepng_free(langtag); lodepng_free(transkey); ucvector_cleanup(&decoded); return error; } static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ info->time_defined = 1; info->time.year = 256u * data[0] + data[1]; info->time.month = data[2]; info->time.day = data[3]; info->time.hour = data[4]; info->time.minute = data[5]; info->time.second = data[6]; return 0; /* OK */ } static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ info->phys_defined = 1; info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; info->phys_unit = data[8]; return 0; /* OK */ } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned char IEND = 0; const unsigned char* chunk; size_t i; ucvector idat; /*the data from idat chunks*/ ucvector scanlines; size_t predict; size_t numpixels; size_t outsize = 0; /*for unknown chunk order*/ unsigned unknown = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*provide some proper output values if error will happen*/ *out = 0; state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; numpixels = *w * *h; /*multiplication overflow*/ if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); /*multiplication overflow possible further below. Allows up to 2^31-1 pixel bytes with 16-bit RGBA, the rest is room for filter bytes.*/ if(numpixels > 268435455) CERROR_RETURN(state->error, 92); ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/ while(!IEND && !state->error) { unsigned chunkLength; const unsigned char* data; /*the data in the chunk*/ /*error: size of the in buffer too small to contain next chunk*/ if((size_t)((chunk - in) + 12) > insize || chunk < in) CERROR_BREAK(state->error, 30); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ chunkLength = lodepng_chunk_length(chunk); /*error: chunk length larger than the max PNG chunk size*/ if(chunkLength > 2147483647) CERROR_BREAK(state->error, 63); if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ } data = lodepng_chunk_data_const(chunk); /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { size_t oldsize = idat.size; if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } /*IEND chunk*/ else if(lodepng_chunk_type_equals(chunk, "IEND")) { IEND = 1; } /*palette chunk (PLTE)*/ else if(lodepng_chunk_type_equals(chunk, "PLTE")) { state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 2; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } /*palette transparency chunk (tRNS)*/ else if(lodepng_chunk_type_equals(chunk, "tRNS")) { state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); if(state->error) break; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ else if(lodepng_chunk_type_equals(chunk, "bKGD")) { state->error = readChunk_bKGD(&state->info_png, data, chunkLength); if(state->error) break; } /*text chunk (tEXt)*/ else if(lodepng_chunk_type_equals(chunk, "tEXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_tEXt(&state->info_png, data, chunkLength); if(state->error) break; } } /*compressed text chunk (zTXt)*/ else if(lodepng_chunk_type_equals(chunk, "zTXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } /*international text chunk (iTXt)*/ else if(lodepng_chunk_type_equals(chunk, "iTXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "tIME")) { state->error = readChunk_tIME(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { state->error = readChunk_pHYs(&state->info_png, data, chunkLength); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ if(!lodepng_chunk_ancillary(chunk)) CERROR_BREAK(state->error, 69); unknown = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(state->decoder.remember_unknown_chunks) { state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ } if(!IEND) chunk = lodepng_chunk_next_const(chunk); } ucvector_init(&scanlines); /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. If the decompressed size does not match the prediction, the image must be corrupt.*/ if(state->info_png.interlace_method == 0) { /*The extra *h is added because this are the filter bytes every scanline starts with*/ predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; } else { /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ const LodePNGColorMode* color = &state->info_png.color; predict = 0; predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); } if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, idat.size, &state->decoder.zlibsettings); if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ } ucvector_cleanup(&idat); if(!state->error) { outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); *out = (unsigned char*)lodepng_malloc(outsize); if(!*out) state->error = 83; /*alloc fail*/ } if(!state->error) { for(i = 0; i < outsize; i++) (*out)[i] = 0; state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); } ucvector_cleanup(&scanlines); } unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { *out = 0; decodeGeneric(out, w, h, state, in, insize); if(state->error) return state->error; if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { /*same color type, no copying or converting of data needed*/ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype the raw image has to the end user*/ if(!state->decoder.color_convert) { state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); if(state->error) return state->error; } } else { /*color conversion needed; sort of copy of the data*/ unsigned char* data = *out; size_t outsize; /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) { return 56; /*unsupported color mode conversion*/ } outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); *out = (unsigned char*)lodepng_malloc(outsize); if(!(*out)) { state->error = 83; /*alloc fail*/ } else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h); lodepng_free(data); } return state->error; } unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; error = lodepng_decode(out, w, h, &state, in, insize); lodepng_state_cleanup(&state); return error; } unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); } unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer = 0; size_t buffersize; unsigned error; error = lodepng_load_file(&buffer, &buffersize, filename); if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); lodepng_free(buffer); return error; } unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); } unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { settings->color_convert = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->read_text_chunks = 1; settings->remember_unknown_chunks = 0; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } #endif /*LODEPNG_COMPILE_DECODER*/ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) void lodepng_state_init(LodePNGState* state) { #ifdef LODEPNG_COMPILE_DECODER lodepng_decoder_settings_init(&state->decoder); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER lodepng_encoder_settings_init(&state->encoder); #endif /*LODEPNG_COMPILE_ENCODER*/ lodepng_color_mode_init(&state->info_raw); lodepng_info_init(&state->info_png); state->error = 1; } void lodepng_state_cleanup(LodePNGState* state) { lodepng_color_mode_cleanup(&state->info_raw); lodepng_info_cleanup(&state->info_png); } void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) { lodepng_state_cleanup(dest); *dest = *source; lodepng_color_mode_init(&dest->info_raw); lodepng_info_init(&dest->info_png); dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; } #endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Encoder / */ /* ////////////////////////////////////////////////////////////////////////// */ /*chunkName must be string of 4 characters*/ static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) { CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); out->allocsize = out->size; /*fix the allocsize again*/ return 0; } static void writeSignature(ucvector* out) { /*8 bytes PNG signature, aka the magic bytes*/ ucvector_push_back(out, 137); ucvector_push_back(out, 80); ucvector_push_back(out, 78); ucvector_push_back(out, 71); ucvector_push_back(out, 13); ucvector_push_back(out, 10); ucvector_push_back(out, 26); ucvector_push_back(out, 10); } static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { unsigned error = 0; ucvector header; ucvector_init(&header); lodepng_add32bitInt(&header, w); /*width*/ lodepng_add32bitInt(&header, h); /*height*/ ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ ucvector_push_back(&header, 0); /*compression method*/ ucvector_push_back(&header, 0); /*filter method*/ ucvector_push_back(&header, interlace_method); /*interlace method*/ error = addChunk(out, "IHDR", header.data, header.size); ucvector_cleanup(&header); return error; } static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { unsigned error = 0; size_t i; ucvector PLTE; ucvector_init(&PLTE); for(i = 0; i != info->palettesize * 4; ++i) { /*add all channels except alpha channel*/ if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); } error = addChunk(out, "PLTE", PLTE.data, PLTE.size); ucvector_cleanup(&PLTE); return error; } static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { unsigned error = 0; size_t i; ucvector tRNS; ucvector_init(&tRNS); if(info->colortype == LCT_PALETTE) { size_t amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ for(i = info->palettesize; i != 0; --i) { if(info->palette[4 * (i - 1) + 3] == 255) --amount; else break; } /*add only alpha channel*/ for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); } else if(info->colortype == LCT_GREY) { if(info->key_defined) { ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); } } error = addChunk(out, "tRNS", tRNS.data, tRNS.size); ucvector_cleanup(&tRNS); return error; } static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, LodePNGCompressSettings* zlibsettings) { ucvector zlibdata; unsigned error = 0; /*compress with the Zlib compressor*/ ucvector_init(&zlibdata); error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); ucvector_cleanup(&zlibdata); return error; } static unsigned addChunk_IEND(ucvector* out) { unsigned error = 0; error = addChunk(out, "IEND", 0, 0); return error; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { unsigned error = 0; size_t i; ucvector text; ucvector_init(&text); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&text, 0); /*0 termination char*/ for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); error = addChunk(out, "tEXt", text.data, text.size); ucvector_cleanup(&text); return error; } static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; ucvector data, compressed; size_t i, textsize = strlen(textstring); ucvector_init(&data); ucvector_init(&compressed); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*0 termination char*/ ucvector_push_back(&data, 0); /*compression method: 0*/ error = zlib_compress(&compressed.data, &compressed.size, (const unsigned char*)textstring, textsize, zlibsettings); if(!error) { for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); error = addChunk(out, "zTXt", data.data, data.size); } ucvector_cleanup(&compressed); ucvector_cleanup(&data); return error; } static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; ucvector data; size_t i, textsize = strlen(textstring); ucvector_init(&data); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*null termination char*/ ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ ucvector_push_back(&data, 0); /*compression method*/ for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); ucvector_push_back(&data, 0); /*null termination char*/ for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); ucvector_push_back(&data, 0); /*null termination char*/ if(compressed) { ucvector compressed_data; ucvector_init(&compressed_data); error = zlib_compress(&compressed_data.data, &compressed_data.size, (const unsigned char*)textstring, textsize, zlibsettings); if(!error) { for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); } ucvector_cleanup(&compressed_data); } else /*not compressed*/ { for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); } if(!error) error = addChunk(out, "iTXt", data.data, data.size); ucvector_cleanup(&data); return error; } static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { unsigned error = 0; ucvector bKGD; ucvector_init(&bKGD); if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); } else if(info->color.colortype == LCT_PALETTE) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ } error = addChunk(out, "bKGD", bKGD.data, bKGD.size); ucvector_cleanup(&bKGD); return error; } static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { unsigned error = 0; unsigned char* data = (unsigned char*)lodepng_malloc(7); if(!data) return 83; /*alloc fail*/ data[0] = (unsigned char)(time->year >> 8); data[1] = (unsigned char)(time->year & 255); data[2] = (unsigned char)time->month; data[3] = (unsigned char)time->day; data[4] = (unsigned char)time->hour; data[5] = (unsigned char)time->minute; data[6] = (unsigned char)time->second; error = addChunk(out, "tIME", data, 7); lodepng_free(data); return error; } static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { unsigned error = 0; ucvector data; ucvector_init(&data); lodepng_add32bitInt(&data, info->phys_x); lodepng_add32bitInt(&data, info->phys_y); ucvector_push_back(&data, info->phys_unit); error = addChunk(out, "pHYs", data.data, data.size); ucvector_cleanup(&data); return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, size_t length, size_t bytewidth, unsigned char filterType) { size_t i; switch(filterType) { case 0: /*None*/ for(i = 0; i != length; ++i) out[i] = scanline[i]; break; case 1: /*Sub*/ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; break; case 2: /*Up*/ if(prevline) { for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; } else { for(i = 0; i != length; ++i) out[i] = scanline[i]; } break; case 3: /*Average*/ if(prevline) { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); } break; case 4: /*Paeth*/ if(prevline) { /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); for(i = bytewidth; i < length; ++i) { out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; default: return; /*unexisting filter type given*/ } } /* log2 approximation. A slight bit faster than std::log. */ static float flog2(float f) { float result = 0; while(f > 32) { result += 4; f /= 16; } while(f > 2) { ++result; f /= 2; } return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); } static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) { /* For PNG filter method 0 out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are the scanlines with 1 extra byte per scanline */ unsigned bpp = lodepng_get_bpp(info); /*the width of a scanline in bytes, not including the filter type*/ size_t linebytes = (w * bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7) / 8; const unsigned char* prevline = 0; unsigned x, y; unsigned error = 0; LodePNGFilterStrategy strategy = settings->filter_strategy; /* There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. use fixed filtering, with the filter None). * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply all five filters and select the filter that produces the smallest sum of absolute values per row. This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum heuristic is used. */ if(settings->filter_palette_zero && (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; if(bpp == 0) return 31; /*error: invalid color type*/ if(strategy == LFS_ZERO) { for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; out[outindex] = 0; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); prevline = &in[inindex]; } } else if(strategy == LFS_MINSUM) { /*adaptive filtering*/ size_t sum[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned char type, bestType = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } if(!error) { for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); /*calculate the sum of the result*/ sum[type] = 0; if(type == 0) { for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); } else { for(x = 0; x != linebytes; ++x) { /*For differences, each byte should be treated as signed, values above 127 are negative (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. This means filtertype 0 is almost never chosen, but that is justified.*/ unsigned char s = attempt[type][x]; sum[type] += s < 128 ? s : (255U - s); } } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum[type] < smallest) { bestType = type; smallest = sum[type]; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_ENTROPY) { float sum[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ float smallest = 0; unsigned type, bestType = 0; unsigned count[256]; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); for(x = 0; x != 256; ++x) count[x] = 0; for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; ++count[type]; /*the filter type itself is part of the scanline*/ sum[type] = 0; for(x = 0; x != 256; ++x) { float p = count[x] / (float)(linebytes + 1); sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum[type] < smallest) { bestType = type; smallest = sum[type]; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_PREDEFINED) { for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; unsigned char type = settings->predefined_filters[y]; out[outindex] = type; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); prevline = &in[inindex]; } } else if(strategy == LFS_BRUTE_FORCE) { /*brute force filter chooser. deflate the scanline after every filter attempt to see which one deflates best. This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; LodePNGCompressSettings zlibsettings = settings->zlibsettings; /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, to simulate the true case where the tree is the same for the whole image. Sometimes it gives better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare cases better compression. It does make this a bit less slow, so it's worth doing this.*/ zlibsettings.btype = 1; /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG images only, so disable it*/ zlibsettings.custom_zlib = 0; zlibsettings.custom_deflate = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } for(y = 0; y != h; ++y) /*try the 5 filter types*/ { for(type = 0; type != 5; ++type) { unsigned testsize = linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); size[type] = 0; dummy = 0; zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); lodepng_free(dummy); /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || size[type] < smallest) { bestType = type; smallest = size[type]; } } prevline = &in[y * linebytes]; out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else return 88; /* unknown filter strategy */ return error; } static void addPaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /*The opposite of the removePaddingBits function olinebits must be >= ilinebits*/ unsigned y; size_t diff = olinebits - ilinebits; size_t obp = 0, ibp = 0; /*bit pointers*/ for(y = 0; y != h; ++y) { size_t x; for(x = 0; x < ilinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/ for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); } } /* in: non-interlaced image with size w*h out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. bpp: bits per pixel there are no padding bits, not between scanlines, not between reduced images in has the following size in bits: w * h * bpp. out is possibly bigger due to padding bits between reduced images NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } } } } } /*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. return value is error**/ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) { /* This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter */ unsigned bpp = lodepng_get_bpp(&info_png->color); unsigned error = 0; if(info_png->interlace_method == 0) { *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ if(!error) { /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); if(!padded) error = 83; /*alloc fail*/ if(!error) { addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); error = filter(*out, padded, w, h, &info_png->color, settings); } lodepng_free(padded); } else { /*we can immediately filter into the out buffer, no other steps needed*/ error = filter(*out, in, w, h, &info_png->color, settings); } } } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned char* adam7; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out)) error = 83; /*alloc fail*/ adam7 = (unsigned char*)lodepng_malloc(passstart[7]); if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ if(!error) { unsigned i; Adam7_interlace(adam7, in, w, h, bpp); for(i = 0; i != 7; ++i) { if(bpp < 8) { unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); if(!padded) ERROR_BREAK(83); /*alloc fail*/ addPaddingBits(padded, &adam7[passstart[i]], ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); error = filter(&(*out)[filter_passstart[i]], padded, passw[i], passh[i], &info_png->color, settings); lodepng_free(padded); } else { error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], passw[i], passh[i], &info_png->color, settings); } if(error) break; } } lodepng_free(adam7); } return error; } /* palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... returns 0 if the palette is opaque, returns 1 if the palette has a single color with alpha 0 ==> color key returns 2 if the palette is semi-translucent. */ static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) { size_t i; unsigned key = 0; unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ for(i = 0; i != palettesize; ++i) { if(!key && palette[4 * i + 3] == 0) { r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; key = 1; i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ } else if(palette[4 * i + 3] != 255) return 2; /*when key, no opaque RGB may have key's RGB*/ else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; } return key; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { unsigned char* inchunk = data; while((size_t)(inchunk - data) < datasize) { CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); out->allocsize = out->size; /*fix the allocsize again*/ inchunk = lodepng_chunk_next(inchunk); } return 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state) { LodePNGInfo info; ucvector outv; unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ size_t datasize = 0; /*provide some proper output values if error will happen*/ *out = 0; *outsize = 0; state->error = 0; /*check input values validity*/ if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) { CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ } if(state->encoder.zlibsettings.btype > 2) { CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ } if(state->info_png.interlace_method > 1) { CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ } state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ /* color convert and compute scanline filter types */ lodepng_info_init(&info); lodepng_info_copy(&info, &state->info_png); if(state->encoder.auto_convert) { state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); } if (!state->error) { if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { unsigned char* converted; size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; converted = (unsigned char*)lodepng_malloc(size); if(!converted && size) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); lodepng_free(converted); } else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); } /* output all PNG chunks */ ucvector_init(&outv); while(!state->error) /*while only executed once, to break on error*/ { #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS size_t i; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*write signature and chunks*/ writeSignature(&outv); /*IHDR*/ addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*unknown chunks between IHDR and PLTE*/ if(info.unknown_chunks_data[0]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) { addChunk_PLTE(&outv, &info.color); } if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { addChunk_PLTE(&outv, &info.color); } /*tRNS*/ if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) { addChunk_tRNS(&outv, &info.color); } if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) { addChunk_tRNS(&outv, &info.color); } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*bKGD (must come between PLTE and the IDAt chunks*/ if(info.background_defined) addChunk_bKGD(&outv, &info); /*pHYs (must come before the IDAT chunks)*/ if(info.phys_defined) addChunk_pHYs(&outv, &info); /*unknown chunks between PLTE and IDAT*/ if(info.unknown_chunks_data[1]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*IDAT (multiple IDAT chunks must be consecutive)*/ state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*tIME*/ if(info.time_defined) addChunk_tIME(&outv, &info.time); /*tEXt and/or zTXt*/ for(i = 0; i != info.text_num; ++i) { if(strlen(info.text_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ break; } if(strlen(info.text_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ break; } if(state->encoder.text_compression) { addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); } else { addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); } } /*LodePNG version id in text chunk*/ if(state->encoder.add_id) { unsigned alread_added_id_text = 0; for(i = 0; i != info.text_num; ++i) { if(!strcmp(info.text_keys[i], "LodePNG")) { alread_added_id_text = 1; break; } } if(alread_added_id_text == 0) { addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ } } /*iTXt*/ for(i = 0; i != info.itext_num; ++i) { if(strlen(info.itext_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ break; } if(strlen(info.itext_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ break; } addChunk_iTXt(&outv, state->encoder.text_compression, info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], &state->encoder.zlibsettings); } /*unknown chunks between IDAT and IEND*/ if(info.unknown_chunks_data[2]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ addChunk_IEND(&outv); break; /*this isn't really a while loop; no error happened so break out now!*/ } lodepng_info_cleanup(&info); lodepng_free(data); /*instead of cleaning the vector up, give it to the output*/ *out = outv.data; *outsize = outv.size; return state->error; } unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; state.info_png.color.colortype = colortype; state.info_png.color.bitdepth = bitdepth; lodepng_encode(out, outsize, image, w, h, &state); error = state.error; lodepng_state_cleanup(&state); return error; } unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); if(!error) error = lodepng_save_file(buffer, buffersize, filename); lodepng_free(buffer); return error; } unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) { lodepng_compress_settings_init(&settings->zlibsettings); settings->filter_palette_zero = 1; settings->filter_strategy = LFS_MINSUM; settings->auto_convert = 1; settings->force_palette = 0; settings->predefined_filters = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->add_id = 0; settings->text_compression = 1; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ERROR_TEXT /* This returns the description of a numerical error code in English. This is also the documentation of all the error codes. */ const char* lodepng_error_text(unsigned code) { switch(code) { case 0: return "no error, everything went ok"; case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ case 13: return "problem while processing dynamic deflate block"; case 14: return "problem while processing dynamic deflate block"; case 15: return "problem while processing dynamic deflate block"; case 16: return "unexisting code while processing dynamic deflate block"; case 17: return "end of out buffer memory reached while inflating"; case 18: return "invalid distance code while inflating"; case 19: return "end of out buffer memory reached while inflating"; case 20: return "invalid deflate block BTYPE encountered while decoding"; case 21: return "NLEN is not ones complement of LEN in a deflate block"; /*end of out buffer memory reached while inflating: This can happen if the inflated deflate data is longer than the amount of bytes required to fill up all the pixels of the image, given the color depth and image dimensions. Something that doesn't happen in a normal, well encoded, PNG image.*/ case 22: return "end of out buffer memory reached while inflating"; case 23: return "end of in buffer memory reached while inflating"; case 24: return "invalid FCHECK in zlib header"; case 25: return "invalid compression method in zlib header"; case 26: return "FDICT encountered in zlib header while it's not used for PNG"; case 27: return "PNG file is smaller than a PNG header"; /*Checks the magic file header, the first 8 bytes of the PNG file*/ case 28: return "incorrect PNG signature, it's no PNG or corrupted"; case 29: return "first chunk is not the header chunk"; case 30: return "chunk length too large, chunk broken off at end of file"; case 31: return "illegal PNG color type or bpp"; case 32: return "illegal PNG compression method"; case 33: return "illegal PNG filter method"; case 34: return "illegal PNG interlace method"; case 35: return "chunk length of a chunk is too large or the chunk too small"; case 36: return "illegal PNG filter type encountered"; case 37: return "illegal bit depth for this color type given"; case 38: return "the palette is too big"; /*more than 256 colors*/ case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; case 40: return "tRNS chunk has wrong size for greyscale image"; case 41: return "tRNS chunk has wrong size for RGB image"; case 42: return "tRNS chunk appeared while it was not allowed for this color type"; case 43: return "bKGD chunk has wrong size for palette image"; case 44: return "bKGD chunk has wrong size for greyscale image"; case 45: return "bKGD chunk has wrong size for RGB image"; case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; case 49: return "jumped past memory while generating dynamic huffman tree"; case 50: return "jumped past memory while generating dynamic huffman tree"; case 51: return "jumped past memory while inflating huffman block"; case 52: return "jumped past memory while inflating"; case 53: return "size of zlib data too small"; case 54: return "repeat symbol in tree while there was no value symbol yet"; /*jumped past tree while generating huffman tree, this could be when the tree will have more leaves than symbols after generating it out of the given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ case 55: return "jumped past tree while generating huffman tree"; case 56: return "given output image colortype or bitdepth not supported for color conversion"; case 57: return "invalid CRC encountered (checking CRC can be disabled)"; case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; case 59: return "requested color conversion not supported"; case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ case 62: return "conversion from color to greyscale not supported"; case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; case 73: return "invalid tIME chunk size"; case 74: return "invalid pHYs chunk size"; /*length could be wrong, or data chopped off*/ case 75: return "no null termination char found while decoding text chunk"; case 76: return "iTXt chunk too short to contain required bytes"; case 77: return "integer overflow in buffer size"; case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ case 79: return "failed to open file for writing"; case 80: return "tried creating a tree of 0 symbols"; case 81: return "lazy matching at pos 0 is impossible"; case 82: return "color conversion to palette requested while a color isn't in palette"; case 83: return "memory allocation failed"; case 84: return "given image too small to contain all pixels to be encoded"; case 86: return "impossible offset in lz77 encoding (internal bug)"; case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; case 89: return "text chunk keyword too short or long: must have size 1-79"; /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; case 91: return "invalid decompressed idat size"; case 92: return "too many pixels, not supported"; case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; } return "unknown error code"; } #endif /*LODEPNG_COMPILE_ERROR_TEXT*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // C++ Wrapper // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_CPP namespace lodepng { #ifdef LODEPNG_COMPILE_DISK unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename) { long size = lodepng_filesize(filename.c_str()); if(size < 0) return 78; buffer.resize((size_t)size); return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename) { return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); } #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, const LodePNGDecompressSettings& settings) { return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, const LodePNGCompressSettings& settings) { return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_ZLIB */ #ifdef LODEPNG_COMPILE_PNG State::State() { lodepng_state_init(this); } State::State(const State& other) { lodepng_state_init(this); lodepng_state_copy(this, &other); } State::~State() { lodepng_state_cleanup(this); } State& State::operator=(const State& other) { lodepng_state_copy(this, &other); return *this; } #ifdef LODEPNG_COMPILE_DECODER unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); if(buffer && !error) { State state; state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::vector<unsigned char>& in, LodePNGColorType colortype, unsigned bitdepth) { return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); } unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize) { unsigned char* buffer = NULL; unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); if(buffer && !error) { size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); } lodepng_free(buffer); return error; } unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, State& state, const std::vector<unsigned char>& in) { return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); } #ifdef LODEPNG_COMPILE_DISK unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype, unsigned bitdepth) { std::vector<unsigned char> buffer; unsigned error = load_file(buffer, filename); if(error) return error; return decode(out, w, h, buffer, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DECODER */ #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ENCODER static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); size_t n = w * h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h, State& state) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, unsigned w, unsigned h, State& state) { if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, state); } #ifdef LODEPNG_COMPILE_DISK unsigned encode(const std::string& filename, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { std::vector<unsigned char> buffer; unsigned error = encode(buffer, in, w, h, colortype, bitdepth); if(!error) error = save_file(buffer, filename); return error; } unsigned encode(const std::string& filename, const std::vector<unsigned char>& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_PNG */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/mathlib.c�������������������������������������������������������������������0000644�0000000�0000000�00000026140�13136631402�015307� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // mathlib.c -- math primitives #include "quakedef.h" vec3_t vec3_origin = {0,0,0}; /*-----------------------------------------------------------------*/ //#define DEG2RAD( a ) ( a * M_PI ) / 180.0F #define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) { float d; vec3_t n; float inv_denom; inv_denom = 1.0F / DotProduct( normal, normal ); d = DotProduct( normal, p ) * inv_denom; n[0] = normal[0] * inv_denom; n[1] = normal[1] * inv_denom; n[2] = normal[2] * inv_denom; dst[0] = p[0] - d * n[0]; dst[1] = p[1] - d * n[1]; dst[2] = p[2] - d * n[2]; } /* ** assumes "src" is normalized */ void PerpendicularVector( vec3_t dst, const vec3_t src ) { int pos; int i; float minelem = 1.0F; vec3_t tempvec; /* ** find the smallest magnitude axially aligned vector */ for ( pos = 0, i = 0; i < 3; i++ ) { if ( fabs( src[i] ) < minelem ) { pos = i; minelem = fabs( src[i] ); } } tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; tempvec[pos] = 1.0F; /* ** project the point onto the plane defined by src */ ProjectPointOnPlane( dst, tempvec, src ); /* ** normalize the result */ VectorNormalize( dst ); } //johnfitz -- removed RotatePointAroundVector() becuase it's no longer used and my compiler fucked it up anyway /*-----------------------------------------------------------------*/ float anglemod(float a) { #if 0 if (a >= 0) a -= 360*(int)(a/360); else a += 360*( 1 + (int)(-a/360) ); #endif a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); return a; } /* ================== BoxOnPlaneSide Returns 1, 2, or 1 + 2 ================== */ int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p) { float dist1, dist2; int sides; #if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this // function // fast axial cases if (p->type < 3) { if (p->dist <= emins[p->type]) return 1; if (p->dist >= emaxs[p->type]) return 2; return 3; } #endif // general case switch (p->signbits) { case 0: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 1: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 2: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 3: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 4: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 5: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 6: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; case 7: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; default: dist1 = dist2 = 0; // shut up compiler Sys_Error ("BoxOnPlaneSide: Bad signbits"); break; } #if 0 int i; vec3_t corners[2]; for (i=0 ; i<3 ; i++) { if (plane->normal[i] < 0) { corners[0][i] = emins[i]; corners[1][i] = emaxs[i]; } else { corners[1][i] = emins[i]; corners[0][i] = emaxs[i]; } } dist = DotProduct (plane->normal, corners[0]) - plane->dist; dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; sides = 0; if (dist1 >= 0) sides = 1; if (dist2 < 0) sides |= 2; #endif sides = 0; if (dist1 >= p->dist) sides = 1; if (dist2 < p->dist) sides |= 2; #ifdef PARANOID if (sides == 0) Sys_Error ("BoxOnPlaneSide: sides==0"); #endif return sides; } //johnfitz -- the opposite of AngleVectors. this takes forward and generates pitch yaw roll //TODO: take right and up vectors to properly set yaw and roll void VectorAngles (const vec3_t forward, vec3_t angles) { vec3_t temp; temp[0] = forward[0]; temp[1] = forward[1]; temp[2] = 0; angles[PITCH] = -atan2(forward[2], VectorLength(temp)) / M_PI_DIV_180; angles[YAW] = atan2(forward[1], forward[0]) / M_PI_DIV_180; angles[ROLL] = 0; } void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; float sr, sp, sy, cr, cp, cy; angle = angles[YAW] * (M_PI*2 / 360); sy = sin(angle); cy = cos(angle); angle = angles[PITCH] * (M_PI*2 / 360); sp = sin(angle); cp = cos(angle); angle = angles[ROLL] * (M_PI*2 / 360); sr = sin(angle); cr = cos(angle); forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; right[0] = (-1*sr*sp*cy+-1*cr*-sy); right[1] = (-1*sr*sp*sy+-1*cr*cy); right[2] = -1*sr*cp; up[0] = (cr*sp*cy+-sr*-sy); up[1] = (cr*sp*sy+-sr*cy); up[2] = cr*cp; } int VectorCompare (vec3_t v1, vec3_t v2) { int i; for (i=0 ; i<3 ; i++) if (v1[i] != v2[i]) return 0; return 1; } void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) { vecc[0] = veca[0] + scale*vecb[0]; vecc[1] = veca[1] + scale*vecb[1]; vecc[2] = veca[2] + scale*vecb[2]; } vec_t _DotProduct (vec3_t v1, vec3_t v2) { return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; } void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0]-vecb[0]; out[1] = veca[1]-vecb[1]; out[2] = veca[2]-vecb[2]; } void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0]+vecb[0]; out[1] = veca[1]+vecb[1]; out[2] = veca[2]+vecb[2]; } void _VectorCopy (vec3_t in, vec3_t out) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) { cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; } vec_t VectorLength(vec3_t v) { return sqrt(DotProduct(v,v)); } float VectorNormalize (vec3_t v) { float length, ilength; length = sqrt(DotProduct(v,v)); if (length) { ilength = 1/length; v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } return length; } void VectorInverse (vec3_t v) { v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } void VectorScale (vec3_t in, vec_t scale, vec3_t out) { out[0] = in[0]*scale; out[1] = in[1]*scale; out[2] = in[2]*scale; } int Q_log2(int val) { int answer=0; while (val>>=1) answer++; return answer; } /* ================ R_ConcatRotations ================ */ void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; } /* ================ R_ConcatTransforms ================ */ void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } /* =================== FloorDivMod Returns mathematically correct (floor-based) quotient and remainder for numer and denom, both of which should contain no fractional part. The quotient must fit in 32 bits. ==================== */ void FloorDivMod (double numer, double denom, int *quotient, int *rem) { int q, r; double x; #ifndef PARANOID if (denom <= 0.0) Sys_Error ("FloorDivMod: bad denominator %f\n", denom); // if ((floor(numer) != numer) || (floor(denom) != denom)) // Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n", // numer, denom); #endif if (numer >= 0.0) { x = floor(numer / denom); q = (int)x; r = (int)floor(numer - (x * denom)); } else { // // perform operations with positive values, and fix mod to make floor-based // x = floor(-numer / denom); q = -(int)x; r = (int)floor(-numer - (x * denom)); if (r != 0) { q--; r = (int)denom - r; } } *quotient = q; *rem = r; } /* =================== GreatestCommonDivisor ==================== */ int GreatestCommonDivisor (int i1, int i2) { if (i1 > i2) { if (i2 == 0) return (i1); return GreatestCommonDivisor (i2, i1 % i2); } else { if (i1 == 0) return (i2); return GreatestCommonDivisor (i1, i2 % i1); } } /* =================== Invert24To16 Inverts an 8.24 value to a 16.16 value ==================== */ fixed16_t Invert24To16(fixed16_t val) { if (val < 256) return (0xFFFFFFFF); return (fixed16_t) (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/server.h��������������������������������������������������������������������0000644�0000000�0000000�00000015010�13136631402�015174� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_SERVER_H #define _QUAKE_SERVER_H // server.h typedef struct { int maxclients; int maxclientslimit; struct client_s *clients; // [maxclients] int serverflags; // episode completion information qboolean changelevel_issued; // cleared when at SV_SpawnServer } server_static_t; //============================================================================= typedef enum {ss_loading, ss_active} server_state_t; typedef struct { qboolean active; // false if only a net client qboolean paused; qboolean loadgame; // handle connections specially double time; int lastcheck; // used by PF_checkclient double lastchecktime; char name[64]; // map name char modelname[64]; // maps/<name>.bsp, for model_precache[0] struct qmodel_s *worldmodel; const char *model_precache[MAX_MODELS]; // NULL terminated struct qmodel_s *models[MAX_MODELS]; const char *sound_precache[MAX_SOUNDS]; // NULL terminated const char *lightstyles[MAX_LIGHTSTYLES]; int num_edicts; int max_edicts; edict_t *edicts; // can NOT be array indexed, because // edict_t is variable sized, but can // be used to reference the world ent server_state_t state; // some actions are only valid during load sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; sizebuf_t reliable_datagram; // copied to all clients at end of frame byte reliable_datagram_buf[MAX_DATAGRAM]; sizebuf_t signon; byte signon_buf[MAX_MSGLEN-2]; //johnfitz -- was 8192, now uses MAX_MSGLEN unsigned protocol; //johnfitz unsigned protocolflags; } server_t; #define NUM_PING_TIMES 16 #define NUM_SPAWN_PARMS 16 typedef struct client_s { qboolean active; // false = client is free qboolean spawned; // false = don't send datagrams qboolean dropasap; // has been told to go to another level qboolean sendsignon; // only valid before spawned double last_message; // reliable messages must be sent // periodically struct qsocket_s *netconnection; // communications handle usercmd_t cmd; // movement vec3_t wishdir; // intended motion calced from cmd sizebuf_t message; // can be added to at any time, // copied and clear once per frame byte msgbuf[MAX_MSGLEN]; edict_t *edict; // EDICT_NUM(clientnum+1) char name[32]; // for printing to other people int colors; float ping_times[NUM_PING_TIMES]; int num_pings; // ping_times[num_pings%NUM_PING_TIMES] // spawn parms are carried from level to level float spawn_parms[NUM_SPAWN_PARMS]; // client known data for deltas int old_frags; } client_t; //============================================================================= // edict->movetype values #define MOVETYPE_NONE 0 // never moves #define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_WALK 3 // gravity #define MOVETYPE_STEP 4 // gravity, special edge handling #define MOVETYPE_FLY 5 #define MOVETYPE_TOSS 6 // gravity #define MOVETYPE_PUSH 7 // no clip to world, push and crush #define MOVETYPE_NOCLIP 8 #define MOVETYPE_FLYMISSILE 9 // extra size to monsters #define MOVETYPE_BOUNCE 10 // edict->solid values #define SOLID_NOT 0 // no interaction with other objects #define SOLID_TRIGGER 1 // touch on edge, but not blocking #define SOLID_BBOX 2 // touch on edge, block #define SOLID_SLIDEBOX 3 // touch on edge, but not an onground #define SOLID_BSP 4 // bsp clip, touch on edge, block // edict->deadflag values #define DEAD_NO 0 #define DEAD_DYING 1 #define DEAD_DEAD 2 #define DAMAGE_NO 0 #define DAMAGE_YES 1 #define DAMAGE_AIM 2 // edict->flags #define FL_FLY 1 #define FL_SWIM 2 //#define FL_GLIMPSE 4 #define FL_CONVEYOR 4 #define FL_CLIENT 8 #define FL_INWATER 16 #define FL_MONSTER 32 #define FL_GODMODE 64 #define FL_NOTARGET 128 #define FL_ITEM 256 #define FL_ONGROUND 512 #define FL_PARTIALGROUND 1024 // not all corners are valid #define FL_WATERJUMP 2048 // player jumping out of water #define FL_JUMPRELEASED 4096 // for jump debouncing // entity effects #define EF_BRIGHTFIELD 1 #define EF_MUZZLEFLASH 2 #define EF_BRIGHTLIGHT 4 #define EF_DIMLIGHT 8 #define SPAWNFLAG_NOT_EASY 256 #define SPAWNFLAG_NOT_MEDIUM 512 #define SPAWNFLAG_NOT_HARD 1024 #define SPAWNFLAG_NOT_DEATHMATCH 2048 //============================================================================ extern cvar_t teamplay; extern cvar_t skill; extern cvar_t deathmatch; extern cvar_t coop; extern cvar_t fraglimit; extern cvar_t timelimit; extern server_static_t svs; // persistant server info extern server_t sv; // local server extern client_t *host_client; extern edict_t *sv_player; //=========================================================== void SV_Init (void); void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count); void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation); void SV_DropClient (qboolean crash); void SV_SendClientMessages (void); void SV_ClearDatagram (void); int SV_ModelIndex (const char *name); void SV_SetIdealPitch (void); void SV_AddUpdates (void); void SV_ClientThink (void); void SV_AddClientToServer (struct qsocket_s *ret); void SV_ClientPrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void SV_BroadcastPrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void SV_Physics (void); qboolean SV_CheckBottom (edict_t *ent); qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg); void SV_MoveToGoal (void); void SV_CheckForNewClients (void); void SV_RunClients (void); void SV_SaveSpawnparms (); void SV_SpawnServer (const char *server); #endif /* _QUAKE_SERVER_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/Makefile.darwin�������������������������������������������������������������0000644�0000000�0000000�00000016010�13142575645�016456� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# GNU Makefile for compiling Mac OS X version of QuakeSpasm. # Usage: "make -f Makefile.darwin" # To cross-compile on Linux hosts, see the build_cross_osx*.sh scripts. # "make DEBUG=1" to build a debug client. # "make SDL_FRAMEWORK_PATH=/path/to/Frameworks" to specify the directory # containing SDL.framework and override the locally included versions. # "make DO_USERDIRS=1" to enable user directories support # Enable/Disable user directories support DO_USERDIRS=0 ### Enable/Disable SDL2 USE_SDL2=0 ### Enable/Disable codecs for streaming music support USE_CODEC_WAVE=1 USE_CODEC_FLAC=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 USE_CODEC_OPUS=1 # either mikmod or xmp USE_CODEC_MIKMOD=1 USE_CODEC_XMP=0 USE_CODEC_UMX=1 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # --------------------------- # Helper functions # --------------------------- check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) # --------------------------- HOST_OS := $(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]') MACH_TYPE= $(shell sh detect.sh arch) DEBUG ?= 0 # --------------------------- # build variables # --------------------------- CC ?= gcc LINKER = $(CC) LIPO ?= lipo STRIP ?= strip CPUFLAGS= LDFLAGS = DFLAGS ?= CFLAGS ?= -Wall # @rpath can be used when targeting 10.5+ USE_RPATH=0 # require 10.5 for 64 bit builds ifeq ($(MACH_TYPE),ppc64) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 USE_RPATH=1 endif ifeq ($(USE_SDL2),1) # sdl2 needs targetting 10.5+ # as of v2.0.5, sdl2 targets 10.6+ ifeq ($(MACH_TYPE),x86) CFLAGS +=-mmacosx-version-min=10.6 LDFLAGS +=-mmacosx-version-min=10.6 USE_RPATH=1 endif endif ifeq ($(MACH_TYPE),x86_64) # require 10.6 for amd64 builds, not 10.5 (SDL's requirement.) # bundle1.o is needed for dyld_stub_binding_helper CFLAGS +=-mmacosx-version-min=10.6 LDFLAGS +=-mmacosx-version-min=10.6 -Wl,-lbundle1.o USE_RPATH=1 endif CFLAGS += $(CPUFLAGS) ifeq ($(USE_RPATH),1) LDFLAGS+=-Wl,-rpath,@executable_path/../Frameworks endif ifneq ($(DEBUG),0) DFLAGS += -DDEBUG CFLAGS += -g do_strip= else DFLAGS += -DNDEBUG CFLAGS += -O2 CFLAGS += $(call check_gcc,-fweb,) CFLAGS += $(call check_gcc,-frename-registers,) cmd_strip=$(STRIP) $(1) define do_strip $(call cmd_strip,$(1)); endef endif ifeq ($(DO_USERDIRS),1) CFLAGS += -DDO_USERDIRS=1 endif ifeq ($(USE_SDL2),1) CFLAGS += -DUSE_SDL2 endif # not relying on sdl-config command and assuming # /Library/Frameworks/SDL.framework is available SDL_CFLAGS =-D_GNU_SOURCE=1 -D_THREAD_SAFE SDL_CFLAGS+=-DSDL_FRAMEWORK -DNO_SDL_CONFIG ifeq ($(USE_SDL2),1) SDL_FRAMEWORK_NAME = SDL2 else SDL_FRAMEWORK_NAME = SDL endif # default to our local SDL[2].framework for build SDL_FRAMEWORK_PATH ?=../MacOSX ifneq ($(SDL_FRAMEWORK_PATH),) SDL_LIBS +=-F$(SDL_FRAMEWORK_PATH) SDL_CFLAGS+=-F$(SDL_FRAMEWORK_PATH) endif SDL_LIBS +=-Wl,-framework,$(SDL_FRAMEWORK_NAME) -Wl,-framework,Cocoa NET_LIBS := ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3.o lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123.o lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif CODECLIBS := ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_FLAC),1) CFLAGS+= -DUSE_CODEC_FLAC CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= -lFLAC endif ifeq ($(USE_CODEC_OPUS),1) CFLAGS+= -DUSE_CODEC_OPUS CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= -lopusfile -lopus -logg endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),1) CFLAGS+= -DUSE_CODEC_MP3 CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),1) CFLAGS+= -DUSE_CODEC_MIKMOD CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),1) CFLAGS+= -DUSE_CODEC_XMP CODEC_INC = -I../MacOSX/codecs/include CODEC_LINK= -L../MacOSX/codecs/lib CODECLIBS+= -lxmp endif ifeq ($(USE_CODEC_UMX),1) CFLAGS+= -DUSE_CODEC_UMX endif CFLAGS+= $(CODEC_INC) COMMON_LIBS:= -Wl,-framework,IOKit -Wl,-framework,OpenGL LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS) # --------------------------- # targets # --------------------------- .PHONY: clean debug release DEFAULT_TARGET := quakespasm # --------------------------- # rules # --------------------------- %.o: %.c $(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $< %.o: %.m $(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $< %.o: ../MacOSX/%.m $(CC) $(DFLAGS) -c -I../MacOSX $(CFLAGS) $(SDL_CFLAGS) -o $@ $< # ---------------------------------------------------------------------------- # objects # ---------------------------------------------------------------------------- MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj) \ snd_mikmod.o \ snd_xmp.o \ snd_umx.o COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o SYSOBJ_CDA := cd_sdl.o SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_LAUNCHER := AppController.o QuakeArgument.o QuakeArguments.o ScreenInfo.o SDLApplication.o SYSOBJ_SYS := pl_osx.o sys_sdl_unix.o SYSOBJ_MAIN:= main_sdl.o SDLMain.o GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_fog.o \ gl_rmisc.o \ r_part.o \ r_world.o \ gl_screen.o \ gl_sky.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ image.o \ gl_texmgr.o \ gl_mesh.o \ r_sprite.o \ r_alias.o \ r_brush.o \ gl_model.o OBJS := strlcat.o \ strlcpy.o \ $(GLOBJS) \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_tent.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ common.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ world.o \ zone.o \ $(SYSOBJ_SYS) $(SYSOBJ_LAUNCHER) $(SYSOBJ_MAIN) # ------------------------ # darwin build rules # ------------------------ quakespasm: $(OBJS) $(LINKER) $(OBJS) $(LDFLAGS) $(LIBS) $(SDL_LIBS) -o $@ $(call do_strip,$@) image.o: lodepng.c lodepng.h stb_image_write.h release: quakespasm debug: $(error Use "make DEBUG=1") clean: rm -f $(shell find . \( -name '*~' -o -name '#*#' -o -name '*.o' -o -name '*.res' -o -name $(DEFAULT_TARGET) \) -print) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/detect.sh�������������������������������������������������������������������0000755�0000000�0000000�00000001453�12013076135�015331� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh # script from loki_setup tools DetectARCH() { status=1 case `uname -m` in amd64 | x86_64) echo "x86_64" status=0;; i?86 | i86*) echo "x86" status=0;; 90*/*) echo "hppa" status=0;; *) case `uname -s` in IRIX*) echo "mips" status=0;; AIX*) echo "ppc" status=0;; *) arch=`uname -p 2> /dev/null || uname -m` if test "$arch" = powerpc; then echo "ppc" else echo $arch fi status=0;; esac esac return $status } DetectOS() { os=`uname -s` if test "$os" = "OpenUNIX"; then echo SCO_SV else echo $os fi return 0 } if test "$1" = "os"; then result=`DetectOS` elif test "$1" = "arch"; then result=`DetectARCH` else result="OS: `DetectOS`, Arch: `DetectARCH`" fi status="$?" echo $result exit $status ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cvar.c����������������������������������������������������������������������0000644�0000000�0000000�00000030404�12407762022�014623� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cvar.c -- dynamic variable tracking #include "quakedef.h" static cvar_t *cvar_vars; static char cvar_null_string[] = ""; //============================================================================== // // USER COMMANDS // //============================================================================== void Cvar_Reset (const char *name); //johnfitz /* ============ Cvar_List_f -- johnfitz ============ */ void Cvar_List_f (void) { cvar_t *cvar; const char *partial; int len, count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = Q_strlen(partial); } else { partial = NULL; len = 0; } count = 0; for (cvar = cvar_vars ; cvar ; cvar = cvar->next) { if (partial && Q_strncmp(partial, cvar->name, len)) { continue; } Con_SafePrintf ("%s%s %s \"%s\"\n", (cvar->flags & CVAR_ARCHIVE) ? "*" : " ", (cvar->flags & CVAR_NOTIFY) ? "s" : " ", cvar->name, cvar->string); count++; } Con_SafePrintf ("%i cvars", count); if (partial) { Con_SafePrintf (" beginning with \"%s\"", partial); } Con_SafePrintf ("\n"); } /* ============ Cvar_Inc_f -- johnfitz ============ */ void Cvar_Inc_f (void) { switch (Cmd_Argc()) { default: case 1: Con_Printf("inc <cvar> [amount] : increment cvar\n"); break; case 2: Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + 1); break; case 3: Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + Q_atof(Cmd_Argv(2))); break; } } /* ============ Cvar_Toggle_f -- johnfitz ============ */ void Cvar_Toggle_f (void) { switch (Cmd_Argc()) { default: case 1: Con_Printf("toggle <cvar> : toggle cvar\n"); break; case 2: if (Cvar_VariableValue(Cmd_Argv(1))) Cvar_Set (Cmd_Argv(1), "0"); else Cvar_Set (Cmd_Argv(1), "1"); break; } } /* ============ Cvar_Cycle_f -- johnfitz ============ */ void Cvar_Cycle_f (void) { int i; if (Cmd_Argc() < 3) { Con_Printf("cycle <cvar> <value list>: cycle cvar through a list of values\n"); return; } //loop through the args until you find one that matches the current cvar value. //yes, this will get stuck on a list that contains the same value twice. //it's not worth dealing with, and i'm not even sure it can be dealt with. for (i = 2; i < Cmd_Argc(); i++) { //zero is assumed to be a string, even though it could actually be zero. The worst case //is that the first time you call this command, it won't match on zero when it should, but after that, //it will be comparing strings that all had the same source (the user) so it will work. if (Q_atof(Cmd_Argv(i)) == 0) { if (!strcmp(Cmd_Argv(i), Cvar_VariableString(Cmd_Argv(1)))) break; } else { if (Q_atof(Cmd_Argv(i)) == Cvar_VariableValue(Cmd_Argv(1))) break; } } if (i == Cmd_Argc()) Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // no match else if (i + 1 == Cmd_Argc()) Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // matched last value in list else Cvar_Set (Cmd_Argv(1), Cmd_Argv(i+1)); // matched earlier in list } /* ============ Cvar_Reset_f -- johnfitz ============ */ void Cvar_Reset_f (void) { switch (Cmd_Argc()) { default: case 1: Con_Printf ("reset <cvar> : reset cvar to default\n"); break; case 2: Cvar_Reset (Cmd_Argv(1)); break; } } /* ============ Cvar_ResetAll_f -- johnfitz ============ */ void Cvar_ResetAll_f (void) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) Cvar_Reset (var->name); } /* ============ Cvar_ResetCfg_f -- QuakeSpasm ============ */ void Cvar_ResetCfg_f (void) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) if (var->flags & CVAR_ARCHIVE) Cvar_Reset (var->name); } //============================================================================== // // INIT // //============================================================================== /* ============ Cvar_Init -- johnfitz ============ */ void Cvar_Init (void) { Cmd_AddCommand ("cvarlist", Cvar_List_f); Cmd_AddCommand ("toggle", Cvar_Toggle_f); Cmd_AddCommand ("cycle", Cvar_Cycle_f); Cmd_AddCommand ("inc", Cvar_Inc_f); Cmd_AddCommand ("reset", Cvar_Reset_f); Cmd_AddCommand ("resetall", Cvar_ResetAll_f); Cmd_AddCommand ("resetcfg", Cvar_ResetCfg_f); } //============================================================================== // // CVAR FUNCTIONS // //============================================================================== /* ============ Cvar_FindVar ============ */ cvar_t *Cvar_FindVar (const char *var_name) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { if (!Q_strcmp(var_name, var->name)) return var; } return NULL; } cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags) { cvar_t *var; if (*prev_name) { var = Cvar_FindVar (prev_name); if (!var) return NULL; var = var->next; } else var = cvar_vars; // search for the next cvar matching the needed flags while (var) { if ((var->flags & with_flags) || !with_flags) break; var = var->next; } return var; } /* ============ Cvar_LockVar ============ */ void Cvar_LockVar (const char *var_name) { cvar_t *var = Cvar_FindVar (var_name); if (var) var->flags |= CVAR_LOCKED; } void Cvar_UnlockVar (const char *var_name) { cvar_t *var = Cvar_FindVar (var_name); if (var) var->flags &= ~CVAR_LOCKED; } void Cvar_UnlockAll (void) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { var->flags &= ~CVAR_LOCKED; } } /* ============ Cvar_VariableValue ============ */ float Cvar_VariableValue (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return 0; return Q_atof (var->string); } /* ============ Cvar_VariableString ============ */ const char *Cvar_VariableString (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return cvar_null_string; return var->string; } /* ============ Cvar_CompleteVariable ============ */ const char *Cvar_CompleteVariable (const char *partial) { cvar_t *cvar; int len; len = Q_strlen(partial); if (!len) return NULL; // check functions for (cvar = cvar_vars ; cvar ; cvar = cvar->next) { if (!Q_strncmp(partial, cvar->name, len)) return cvar->name; } return NULL; } /* ============ Cvar_Reset -- johnfitz ============ */ void Cvar_Reset (const char *name) { cvar_t *var; var = Cvar_FindVar (name); if (!var) Con_Printf ("variable \"%s\" not found\n", name); else Cvar_SetQuick (var, var->default_string); } void Cvar_SetQuick (cvar_t *var, const char *value) { if (var->flags & (CVAR_ROM|CVAR_LOCKED)) return; if (!(var->flags & CVAR_REGISTERED)) return; if (!var->string) var->string = Z_Strdup (value); else { int len; if (!strcmp(var->string, value)) return; // no change var->flags |= CVAR_CHANGED; len = Q_strlen (value); if (len != Q_strlen(var->string)) { Z_Free ((void *)var->string); var->string = (char *) Z_Malloc (len + 1); } memcpy ((char *)var->string, value, len + 1); } var->value = Q_atof (var->string); //johnfitz -- save initial value for "reset" command if (!var->default_string) var->default_string = Z_Strdup (var->string); //johnfitz -- during initialization, update default too else if (!host_initialized) { // Sys_Printf("changing default of %s: %s -> %s\n", // var->name, var->default_string, var->string); Z_Free ((void *)var->default_string); var->default_string = Z_Strdup (var->string); } //johnfitz if (var->callback) var->callback (var); } void Cvar_SetValueQuick (cvar_t *var, const float value) { char val[32], *ptr = val; if (value == (float)((int)value)) q_snprintf (val, sizeof(val), "%i", (int)value); else { q_snprintf (val, sizeof(val), "%f", value); // kill trailing zeroes while (*ptr) ptr++; while (--ptr > val && *ptr == '0' && ptr[-1] != '.') *ptr = '\0'; } Cvar_SetQuick (var, val); } /* ============ Cvar_Set ============ */ void Cvar_Set (const char *var_name, const char *value) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) { // there is an error in C code if this happens Con_Printf ("Cvar_Set: variable %s not found\n", var_name); return; } Cvar_SetQuick (var, value); } /* ============ Cvar_SetValue ============ */ void Cvar_SetValue (const char *var_name, const float value) { char val[32], *ptr = val; if (value == (float)((int)value)) q_snprintf (val, sizeof(val), "%i", (int)value); else { q_snprintf (val, sizeof(val), "%f", value); // kill trailing zeroes while (*ptr) ptr++; while (--ptr > val && *ptr == '0' && ptr[-1] != '.') *ptr = '\0'; } Cvar_Set (var_name, val); } /* ============ Cvar_SetROM ============ */ void Cvar_SetROM (const char *var_name, const char *value) { cvar_t *var = Cvar_FindVar (var_name); if (var) { var->flags &= ~CVAR_ROM; Cvar_SetQuick (var, value); var->flags |= CVAR_ROM; } } /* ============ Cvar_SetValueROM ============ */ void Cvar_SetValueROM (const char *var_name, const float value) { cvar_t *var = Cvar_FindVar (var_name); if (var) { var->flags &= ~CVAR_ROM; Cvar_SetValueQuick (var, value); var->flags |= CVAR_ROM; } } /* ============ Cvar_RegisterVariable Adds a freestanding variable to the variable list. ============ */ void Cvar_RegisterVariable (cvar_t *variable) { char value[512]; qboolean set_rom; cvar_t *cursor,*prev; //johnfitz -- sorted list insert // first check to see if it has already been defined if (Cvar_FindVar (variable->name)) { Con_Printf ("Can't register variable %s, already defined\n", variable->name); return; } // check for overlap with a command if (Cmd_Exists (variable->name)) { Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name); return; } // link the variable in //johnfitz -- insert each entry in alphabetical order if (cvar_vars == NULL || strcmp(variable->name, cvar_vars->name) < 0) // insert at front { variable->next = cvar_vars; cvar_vars = variable; } else //insert later { prev = cvar_vars; cursor = cvar_vars->next; while (cursor && (strcmp(variable->name, cursor->name) > 0)) { prev = cursor; cursor = cursor->next; } variable->next = prev->next; prev->next = variable; } //johnfitz variable->flags |= CVAR_REGISTERED; // copy the value off, because future sets will Z_Free it q_strlcpy (value, variable->string, sizeof(value)); variable->string = NULL; variable->default_string = NULL; if (!(variable->flags & CVAR_CALLBACK)) variable->callback = NULL; // set it through the function to be consistent set_rom = (variable->flags & CVAR_ROM); variable->flags &= ~CVAR_ROM; Cvar_SetQuick (variable, value); if (set_rom) variable->flags |= CVAR_ROM; } /* ============ Cvar_SetCallback Set a callback function to the var ============ */ void Cvar_SetCallback (cvar_t *var, cvarcallback_t func) { var->callback = func; if (func) var->flags |= CVAR_CALLBACK; else var->flags &= ~CVAR_CALLBACK; } /* ============ Cvar_Command Handles variable inspection and changing from the console ============ */ qboolean Cvar_Command (void) { cvar_t *v; // check variables v = Cvar_FindVar (Cmd_Argv(0)); if (!v) return false; // perform a variable print or set if (Cmd_Argc() == 1) { Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); return true; } Cvar_Set (v->name, Cmd_Argv(1)); return true; } /* ============ Cvar_WriteVariables Writes lines containing "set variable value" for all variables with the archive flag set to true. ============ */ void Cvar_WriteVariables (FILE *f) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { if (var->flags & CVAR_ARCHIVE) fprintf (f, "%s \"%s\"\n", var->name, var->string); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/console.c�������������������������������������������������������������������0000644�0000000�0000000�00000067463�13136631402�015346� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // console.c #include <sys/types.h> #include <time.h> #include <sys/stat.h> #include <fcntl.h> #ifdef _WIN32 #include <io.h> #else #include <unistd.h> #endif #include "quakedef.h" int con_linewidth; float con_cursorspeed = 4; #define CON_TEXTSIZE (1024 * 1024) //ericw -- was 65536. johnfitz -- new default size #define CON_MINSIZE 16384 //johnfitz -- old default, now the minimum size int con_buffersize; //johnfitz -- user can now override default qboolean con_forcedup; // because no entities to refresh int con_totallines; // total lines in console scrollback int con_backscroll; // lines up from bottom to display int con_current; // where next message will be printed int con_x; // offset in current line for next print char *con_text = NULL; cvar_t con_notifytime = {"con_notifytime","3",CVAR_NONE}; //seconds cvar_t con_logcenterprint = {"con_logcenterprint", "1", CVAR_NONE}; //johnfitz char con_lastcenterstring[1024]; //johnfitz #define NUM_CON_TIMES 4 float con_times[NUM_CON_TIMES]; // realtime time the line was generated // for transparent notify lines int con_vislines; qboolean con_debuglog = false; qboolean con_initialized; /* ================ Con_Quakebar -- johnfitz -- returns a bar of the desired length, but never wider than the console includes a newline, unless len >= con_linewidth. ================ */ const char *Con_Quakebar (int len) { static char bar[42]; int i; len = q_min(len, (int)sizeof(bar) - 2); len = q_min(len, con_linewidth); bar[0] = '\35'; for (i = 1; i < len - 1; i++) bar[i] = '\36'; bar[len-1] = '\37'; if (len < con_linewidth) { bar[len] = '\n'; bar[len+1] = 0; } else bar[len] = 0; return bar; } /* ================ Con_ToggleConsole_f ================ */ extern int history_line; //johnfitz void Con_ToggleConsole_f (void) { if (key_dest == key_console/* || (key_dest == key_game && con_forcedup)*/) { key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; con_backscroll = 0; //johnfitz -- toggleconsole should return you to the bottom of the scrollback history_line = edit_line; //johnfitz -- it should also return you to the bottom of the command history if (cls.state == ca_connected) { IN_Activate(); key_dest = key_game; } else { M_Menu_Main_f (); } } else { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_console; } SCR_EndLoadingPlaque (); memset (con_times, 0, sizeof(con_times)); } /* ================ Con_Clear_f ================ */ static void Con_Clear_f (void) { if (con_text) Q_memset (con_text, ' ', con_buffersize); //johnfitz -- con_buffersize replaces CON_TEXTSIZE con_backscroll = 0; //johnfitz -- if console is empty, being scrolled up is confusing } /* ================ Con_Dump_f -- johnfitz -- adapted from quake2 source ================ */ static void Con_Dump_f (void) { int l, x; const char *line; FILE *f; char buffer[1024]; char name[MAX_OSPATH]; q_snprintf (name, sizeof(name), "%s/condump.txt", com_gamedir); COM_CreatePath (name); f = fopen (name, "w"); if (!f) { Con_Printf ("ERROR: couldn't open file %s.\n", name); return; } // skip initial empty lines for (l = con_current - con_totallines + 1; l <= con_current; l++) { line = con_text + (l % con_totallines)*con_linewidth; for (x = 0; x < con_linewidth; x++) if (line[x] != ' ') break; if (x != con_linewidth) break; } // write the remaining lines buffer[con_linewidth] = 0; for ( ; l <= con_current; l++) { line = con_text + (l%con_totallines)*con_linewidth; strncpy (buffer, line, con_linewidth); for (x = con_linewidth - 1; x >= 0; x--) { if (buffer[x] == ' ') buffer[x] = 0; else break; } for (x = 0; buffer[x]; x++) buffer[x] &= 0x7f; fprintf (f, "%s\n", buffer); } fclose (f); Con_Printf ("Dumped console text to %s.\n", name); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i = 0; i < NUM_CON_TIMES; i++) con_times[i] = 0; } /* ================ Con_MessageMode_f ================ */ static void Con_MessageMode_f (void) { if (cls.state != ca_connected || cls.demoplayback) return; chat_team = false; key_dest = key_message; } /* ================ Con_MessageMode2_f ================ */ static void Con_MessageMode2_f (void) { if (cls.state != ca_connected || cls.demoplayback) return; chat_team = true; key_dest = key_message; } /* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ void Con_CheckResize (void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; char *tbuf; //johnfitz -- tbuf no longer a static array int mark; //johnfitz width = (vid.conwidth >> 3) - 2; //johnfitz -- use vid.conwidth instead of vid.width if (width == con_linewidth) return; oldwidth = con_linewidth; con_linewidth = width; oldtotallines = con_totallines; con_totallines = con_buffersize / con_linewidth; //johnfitz -- con_buffersize replaces CON_TEXTSIZE numlines = oldtotallines; if (con_totallines < numlines) numlines = con_totallines; numchars = oldwidth; if (con_linewidth < numchars) numchars = con_linewidth; mark = Hunk_LowMark (); //johnfitz tbuf = (char *) Hunk_Alloc (con_buffersize); //johnfitz Q_memcpy (tbuf, con_text, con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE Q_memset (con_text, ' ', con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE for (i = 0; i < numlines; i++) { for (j = 0; j < numchars; j++) { con_text[(con_totallines - 1 - i) * con_linewidth + j] = tbuf[((con_current - i + oldtotallines) % oldtotallines) * oldwidth + j]; } } Hunk_FreeToLowMark (mark); //johnfitz Con_ClearNotify (); con_backscroll = 0; con_current = con_totallines - 1; } /* ================ Con_Init ================ */ void Con_Init (void) { int i; //johnfitz -- user settable console buffer size i = COM_CheckParm("-consize"); if (i && i < com_argc-1) con_buffersize = q_max(CON_MINSIZE,Q_atoi(com_argv[i+1])*1024); else con_buffersize = CON_TEXTSIZE; //johnfitz con_text = (char *) Hunk_AllocName (con_buffersize, "context");//johnfitz -- con_buffersize replaces CON_TEXTSIZE Q_memset (con_text, ' ', con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE con_linewidth = -1; //johnfitz -- no need to run Con_CheckResize here con_linewidth = 38; con_totallines = con_buffersize / con_linewidth;//johnfitz -- con_buffersize replaces CON_TEXTSIZE con_backscroll = 0; con_current = con_totallines - 1; //johnfitz Con_Printf ("Console initialized.\n"); Cvar_RegisterVariable (&con_notifytime); Cvar_RegisterVariable (&con_logcenterprint); //johnfitz Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); Cmd_AddCommand ("messagemode", Con_MessageMode_f); Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); Cmd_AddCommand ("clear", Con_Clear_f); Cmd_AddCommand ("condump", Con_Dump_f); //johnfitz con_initialized = true; } /* =============== Con_Linefeed =============== */ static void Con_Linefeed (void) { //johnfitz -- improved scrolling if (con_backscroll) con_backscroll++; if (con_backscroll > con_totallines - (glheight>>3) - 1) con_backscroll = con_totallines - (glheight>>3) - 1; //johnfitz con_x = 0; con_current++; Q_memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth); } /* ================ Con_Print Handles cursor positioning, line wrapping, etc All console printing must go through this in order to be logged to disk If no console is visible, the notify window will pop up. ================ */ static void Con_Print (const char *txt) { int y; int c, l; static int cr; int mask; qboolean boundary; //con_backscroll = 0; //johnfitz -- better console scrolling if (txt[0] == 1) { mask = 128; // go to colored text S_LocalSound ("misc/talk.wav"); // play talk wav txt++; } else if (txt[0] == 2) { mask = 128; // go to colored text txt++; } else mask = 0; boundary = true; while ( (c = *txt) ) { if (c <= ' ') { boundary = true; } else if (boundary) { // count word length for (l = 0; l < con_linewidth; l++) if (txt[l] <= ' ') break; // word wrap if (l != con_linewidth && (con_x + l > con_linewidth)) con_x = 0; boundary = false; } txt++; if (cr) { con_current--; cr = false; } if (!con_x) { Con_Linefeed (); // mark time for transparent overlay if (con_current >= 0) con_times[con_current % NUM_CON_TIMES] = realtime; } switch (c) { case '\n': con_x = 0; break; case '\r': con_x = 0; cr = 1; break; default: // display character and advance y = con_current % con_totallines; con_text[y*con_linewidth+con_x] = c | mask; con_x++; if (con_x >= con_linewidth) con_x = 0; break; } } } // borrowed from uhexen2 by S.A. for new procs, LOG_Init, LOG_Close static char logfilename[MAX_OSPATH]; // current logfile name static int log_fd = -1; // log file descriptor /* ================ Con_DebugLog ================ */ void Con_DebugLog(const char *msg) { if (log_fd == -1) return; write(log_fd, msg, strlen(msg)); } /* ================ Con_Printf Handles cursor positioning, line wrapping, etc ================ */ #define MAXPRINTMSG 4096 void Con_Printf (const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; static qboolean inupdate; va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); // also echo to debugging console Sys_Printf ("%s", msg); // log all messages to file if (con_debuglog) Con_DebugLog(msg); if (!con_initialized) return; if (cls.state == ca_dedicated) return; // no graphics mode // write it to the scrollable buffer Con_Print (msg); // update the screen if the console is displayed if (cls.signon != SIGNONS && !scr_disabled_for_loading ) { // protect against infinite loop if something in SCR_UpdateScreen calls // Con_Printd if (!inupdate) { inupdate = true; SCR_UpdateScreen (); inupdate = false; } } } /* ================ Con_DWarning -- ericw same as Con_Warning, but only prints if "developer" cvar is set. use for "exceeds standard limit of" messages, which are only relevant for developers targetting vanilla engines ================ */ void Con_DWarning (const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (!developer.value) return; // don't confuse non-developers with techie stuff... va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Con_SafePrintf ("\x02Warning: "); Con_Printf ("%s", msg); } /* ================ Con_Warning -- johnfitz -- prints a warning to the console ================ */ void Con_Warning (const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Con_SafePrintf ("\x02Warning: "); Con_Printf ("%s", msg); } /* ================ Con_DPrintf A Con_Printf that only shows up if the "developer" cvar is set ================ */ void Con_DPrintf (const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (!developer.value) return; // don't confuse non-developers with techie stuff... va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Con_SafePrintf ("%s", msg); //johnfitz -- was Con_Printf } /* ================ Con_DPrintf2 -- johnfitz -- only prints if "developer" >= 2 currently not used ================ */ void Con_DPrintf2 (const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (developer.value >= 2) { va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Con_Printf ("%s", msg); } } /* ================== Con_SafePrintf Okay to call even when the screen can't be updated ================== */ void Con_SafePrintf (const char *fmt, ...) { va_list argptr; char msg[1024]; int temp; va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; Con_Printf ("%s", msg); scr_disabled_for_loading = temp; } /* ================ Con_CenterPrintf -- johnfitz -- pad each line with spaces to make it appear centered ================ */ void Con_CenterPrintf (int linewidth, const char *fmt, ...) FUNC_PRINTF(2,3); void Con_CenterPrintf (int linewidth, const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; //the original message char line[MAXPRINTMSG]; //one line from the message char spaces[21]; //buffer for spaces char *src, *dst; int len, s; va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); linewidth = q_min(linewidth, con_linewidth); for (src = msg; *src; ) { dst = line; while (*src && *src != '\n') *dst++ = *src++; *dst = 0; if (*src == '\n') src++; len = strlen(line); if (len < linewidth) { s = (linewidth-len)/2; memset (spaces, ' ', s); spaces[s] = 0; Con_Printf ("%s%s\n", spaces, line); } else Con_Printf ("%s\n", line); } } /* ================== Con_LogCenterPrint -- johnfitz -- echo centerprint message to the console ================== */ void Con_LogCenterPrint (const char *str) { if (!strcmp(str, con_lastcenterstring)) return; //ignore duplicates if (cl.gametype == GAME_DEATHMATCH && con_logcenterprint.value != 2) return; //don't log in deathmatch strcpy(con_lastcenterstring, str); if (con_logcenterprint.value) { Con_Printf ("%s", Con_Quakebar(40)); Con_CenterPrintf (40, "%s\n", str); Con_Printf ("%s", Con_Quakebar(40)); Con_ClearNotify (); } } /* ============================================================================== TAB COMPLETION ============================================================================== */ //johnfitz -- tab completion stuff //unique defs char key_tabpartial[MAXCMDLINE]; typedef struct tab_s { const char *name; const char *type; struct tab_s *next; struct tab_s *prev; } tab_t; tab_t *tablist; //defs from elsewhere extern qboolean keydown[256]; typedef struct cmd_function_s { struct cmd_function_s *next; const char *name; xcommand_t function; } cmd_function_t; extern cmd_function_t *cmd_functions; #define MAX_ALIAS_NAME 32 typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; } cmdalias_t; extern cmdalias_t *cmd_alias; /* ============ AddToTabList -- johnfitz tablist is a doubly-linked loop, alphabetized by name ============ */ // bash_partial is the string that can be expanded, // aka Linux Bash shell. -- S.A. static char bash_partial[80]; static qboolean bash_singlematch; void AddToTabList (const char *name, const char *type) { tab_t *t,*insert; char *i_bash; const char *i_name; if (!*bash_partial) { strncpy (bash_partial, name, 79); bash_partial[79] = '\0'; } else { bash_singlematch = 0; // find max common between bash_partial and name i_bash = bash_partial; i_name = name; while (*i_bash && (*i_bash == *i_name)) { i_bash++; i_name++; } *i_bash = 0; } t = (tab_t *) Hunk_Alloc(sizeof(tab_t)); t->name = name; t->type = type; if (!tablist) //create list { tablist = t; t->next = t; t->prev = t; } else if (strcmp(name, tablist->name) < 0) //insert at front { t->next = tablist; t->prev = tablist->prev; t->next->prev = t; t->prev->next = t; tablist = t; } else //insert later { insert = tablist; do { if (strcmp(name, insert->name) < 0) break; insert = insert->next; } while (insert != tablist); t->next = insert; t->prev = insert->prev; t->next->prev = t; t->prev->next = t; } } typedef struct arg_completion_type_s { const char *command; filelist_item_t **filelist; } arg_completion_type_t; static const arg_completion_type_t arg_completion_types[] = { { "map ", &extralevels }, { "changelevel ", &extralevels }, { "game ", &modlist }, { "record ", &demolist }, { "playdemo ", &demolist }, { "timedemo ", &demolist } }; static const int num_arg_completion_types = sizeof(arg_completion_types)/sizeof(arg_completion_types[0]); /* ============ FindCompletion -- stevenaaus ============ */ const char *FindCompletion (const char *partial, filelist_item_t *filelist, int *nummatches_out) { static char matched[32]; char *i_matched, *i_name; filelist_item_t *file; int init, match, plen; memset(matched, 0, sizeof(matched)); plen = strlen(partial); match = 0; for (file = filelist, init = 0; file; file = file->next) { if (!strncmp(file->name, partial, plen)) { if (init == 0) { init = 1; strncpy (matched, file->name, sizeof(matched)-1); matched[sizeof(matched)-1] = '\0'; } else { // find max common i_matched = matched; i_name = file->name; while (*i_matched && (*i_matched == *i_name)) { i_matched++; i_name++; } *i_matched = 0; } match++; } } *nummatches_out = match; if (match > 1) { for (file = filelist; file; file = file->next) { if (!strncmp(file->name, partial, plen)) Con_SafePrintf (" %s\n", file->name); } Con_SafePrintf ("\n"); } return matched; } /* ============ BuildTabList -- johnfitz ============ */ void BuildTabList (const char *partial) { cmdalias_t *alias; cvar_t *cvar; cmd_function_t *cmd; int len; tablist = NULL; len = strlen(partial); bash_partial[0] = 0; bash_singlematch = 1; cvar = Cvar_FindVarAfter ("", CVAR_NONE); for ( ; cvar ; cvar=cvar->next) if (!Q_strncmp (partial, cvar->name, len)) AddToTabList (cvar->name, "cvar"); for (cmd=cmd_functions ; cmd ; cmd=cmd->next) if (!Q_strncmp (partial,cmd->name, len)) AddToTabList (cmd->name, "command"); for (alias=cmd_alias ; alias ; alias=alias->next) if (!Q_strncmp (partial, alias->name, len)) AddToTabList (alias->name, "alias"); } /* ============ Con_TabComplete -- johnfitz ============ */ void Con_TabComplete (void) { char partial[MAXCMDLINE]; const char *match; static char *c; tab_t *t; int mark, i; // if editline is empty, return if (key_lines[edit_line][1] == 0) return; // get partial string (space -> cursor) if (!key_tabpartial[0]) //first time through, find new insert point. (Otherwise, use previous.) { //work back from cursor until you find a space, quote, semicolon, or prompt c = key_lines[edit_line] + key_linepos - 1; //start one space left of cursor while (*c!=' ' && *c!='\"' && *c!=';' && c!=key_lines[edit_line]) c--; c++; //start 1 char after the separator we just found } for (i = 0; c + i < key_lines[edit_line] + key_linepos; i++) partial[i] = c[i]; partial[i] = 0; // Map autocomplete function -- S.A // Since we don't have argument completion, this hack will do for now... for (i=0; i<num_arg_completion_types; i++) { // arg_completion contains a command we can complete the arguments // for (like "map ") and a list of all the maps. arg_completion_type_t arg_completion = arg_completion_types[i]; const char *command_name = arg_completion.command; if (!strncmp (key_lines[edit_line] + 1, command_name, strlen(command_name))) { int nummatches = 0; const char *matched_map = FindCompletion(partial, *arg_completion.filelist, &nummatches); if (!*matched_map) return; q_strlcpy (partial, matched_map, MAXCMDLINE); *c = '\0'; q_strlcat (key_lines[edit_line], partial, MAXCMDLINE); key_linepos = c - key_lines[edit_line] + Q_strlen(matched_map); //set new cursor position if (key_linepos >= MAXCMDLINE) key_linepos = MAXCMDLINE - 1; // if only one match, append a space if (key_linepos < MAXCMDLINE - 1 && key_lines[edit_line][key_linepos] == 0 && (nummatches == 1)) { key_lines[edit_line][key_linepos] = ' '; key_linepos++; key_lines[edit_line][key_linepos] = 0; } c = key_lines[edit_line] + key_linepos; return; } } //if partial is empty, return if (partial[0] == 0) return; //trim trailing space becuase it screws up string comparisons if (i > 0 && partial[i-1] == ' ') partial[i-1] = 0; // find a match mark = Hunk_LowMark(); if (!key_tabpartial[0]) //first time through { q_strlcpy (key_tabpartial, partial, MAXCMDLINE); BuildTabList (key_tabpartial); if (!tablist) return; // print list if length > 1 if (tablist->next != tablist) { t = tablist; Con_SafePrintf("\n"); do { Con_SafePrintf(" %s (%s)\n", t->name, t->type); t = t->next; } while (t != tablist); Con_SafePrintf("\n"); } // match = tablist->name; // First time, just show maximum matching chars -- S.A. match = bash_partial; } else { BuildTabList (key_tabpartial); if (!tablist) return; //find current match -- can't save a pointer because the list will be rebuilt each time t = tablist; match = keydown[K_SHIFT] ? t->prev->name : t->name; do { if (!Q_strcmp(t->name, partial)) { match = keydown[K_SHIFT] ? t->prev->name : t->next->name; break; } t = t->next; } while (t != tablist); } Hunk_FreeToLowMark(mark); //it's okay to free it here because match is a pointer to persistent data // insert new match into edit line q_strlcpy (partial, match, MAXCMDLINE); //first copy match string q_strlcat (partial, key_lines[edit_line] + key_linepos, MAXCMDLINE); //then add chars after cursor *c = '\0'; //now copy all of this into edit line q_strlcat (key_lines[edit_line], partial, MAXCMDLINE); key_linepos = c - key_lines[edit_line] + Q_strlen(match); //set new cursor position if (key_linepos >= MAXCMDLINE) key_linepos = MAXCMDLINE - 1; // if cursor is at end of string, let's append a space to make life easier if (key_linepos < MAXCMDLINE - 1 && key_lines[edit_line][key_linepos] == 0 && bash_singlematch) { key_lines[edit_line][key_linepos] = ' '; key_linepos++; key_lines[edit_line][key_linepos] = 0; // S.A.: the map argument completion (may be in combination with the bash-style // display behavior changes, causes weirdness when completing the arguments for // the changelevel command. the line below "fixes" it, although I'm not sure about // the reason, yet, neither do I know any possible side effects of it: c = key_lines[edit_line] + key_linepos; } } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int i, x, v; const char *text; float time; GL_SetCanvas (CANVAS_CONSOLE); //johnfitz v = vid.conheight; //johnfitz for (i = con_current-NUM_CON_TIMES+1; i <= con_current; i++) { if (i < 0) continue; time = con_times[i % NUM_CON_TIMES]; if (time == 0) continue; time = realtime - time; if (time > con_notifytime.value) continue; text = con_text + (i % con_totallines)*con_linewidth; clearnotify = 0; for (x = 0; x < con_linewidth; x++) Draw_Character ((x+1)<<3, v, text[x]); v += 8; scr_tileclear_updates = 0; //johnfitz } if (key_dest == key_message) { clearnotify = 0; if (chat_team) { Draw_String (8, v, "say_team:"); x = 11; } else { Draw_String (8, v, "say:"); x = 6; } text = Key_GetChatBuffer(); i = Key_GetChatMsgLen(); if (i > con_linewidth - x - 1) text += i - con_linewidth + x + 1; while (*text) { Draw_Character (x<<3, v, *text); x++; text++; } Draw_Character (x<<3, v, 10 + ((int)(realtime*con_cursorspeed)&1)); v += 8; scr_tileclear_updates = 0; //johnfitz } } /* ================ Con_DrawInput -- johnfitz -- modified to allow insert editing The input line scrolls horizontally if typing goes beyond the right edge ================ */ extern qpic_t *pic_ovr, *pic_ins; //johnfitz -- new cursor handling void Con_DrawInput (void) { int i, ofs; if (key_dest != key_console && !con_forcedup) return; // don't draw anything // prestep if horizontally scrolling if (key_linepos >= con_linewidth) ofs = 1 + key_linepos - con_linewidth; else ofs = 0; // draw input string for (i = 0; key_lines[edit_line][i+ofs] && i < con_linewidth; i++) Draw_Character ((i+1)<<3, vid.conheight - 16, key_lines[edit_line][i+ofs]); // johnfitz -- new cursor handling if (!((int)((realtime-key_blinktime)*con_cursorspeed) & 1)) { i = key_linepos - ofs; Draw_Pic ((i+1)<<3, vid.conheight - 16, key_insert ? pic_ins : pic_ovr); } } /* ================ Con_DrawConsole -- johnfitz -- heavy revision Draws the console with the solid background The typing input line at the bottom should only be drawn if typing is allowed ================ */ void Con_DrawConsole (int lines, qboolean drawinput) { int i, x, y, j, sb, rows; const char *text; char ver[32]; if (lines <= 0) return; con_vislines = lines * vid.conheight / glheight; GL_SetCanvas (CANVAS_CONSOLE); // draw the background Draw_ConsoleBackground (); // draw the buffer text rows = (con_vislines +7)/8; y = vid.conheight - rows*8; rows -= 2; //for input and version lines sb = (con_backscroll) ? 2 : 0; for (i = con_current - rows + 1; i <= con_current - sb; i++, y += 8) { j = i - con_backscroll; if (j < 0) j = 0; text = con_text + (j % con_totallines)*con_linewidth; for (x = 0; x < con_linewidth; x++) Draw_Character ( (x + 1)<<3, y, text[x]); } // draw scrollback arrows if (con_backscroll) { y += 8; // blank line for (x = 0; x < con_linewidth; x += 4) Draw_Character ((x + 1)<<3, y, '^'); y += 8; } // draw the input prompt, user text, and cursor if (drawinput) Con_DrawInput (); //draw version number in bottom right y += 8; q_snprintf (ver, sizeof(ver), "QuakeSpasm " QUAKESPASM_VER_STRING); for (x = 0; x < (int)strlen(ver); x++) Draw_Character ((con_linewidth - strlen(ver) + x + 2)<<3, y, ver[x] /*+ 128*/); } /* ================== Con_NotifyBox ================== */ void Con_NotifyBox (const char *text) { double t1, t2; int lastkey, lastchar; // during startup for sound / cd warnings Con_Printf ("\n\n%s", Con_Quakebar(40)); //johnfitz Con_Printf ("%s", text); Con_Printf ("Press a key.\n"); Con_Printf ("%s", Con_Quakebar(40)); //johnfitz IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_console; Key_BeginInputGrab (); do { t1 = Sys_DoubleTime (); SCR_UpdateScreen (); Sys_SendKeyEvents (); Key_GetGrabbedInput (&lastkey, &lastchar); Sys_Sleep (16); t2 = Sys_DoubleTime (); realtime += t2-t1; // make the cursor blink } while (lastkey == -1 && lastchar == -1); Key_EndInputGrab (); Con_Printf ("\n"); IN_Activate(); key_dest = key_game; realtime = 0; // put the cursor back to invisible } void LOG_Init (quakeparms_t *parms) { time_t inittime; char session[24]; if (!COM_CheckParm("-condebug")) return; inittime = time (NULL); strftime (session, sizeof(session), "%m/%d/%Y %H:%M:%S", localtime(&inittime)); q_snprintf (logfilename, sizeof(logfilename), "%s/qconsole.log", parms->basedir); // unlink (logfilename); log_fd = open (logfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (log_fd == -1) { fprintf (stderr, "Error: Unable to create log file %s\n", logfilename); return; } con_debuglog = true; Con_DebugLog (va("LOG started on: %s \n", session)); } void LOG_Close (void) { if (log_fd == -1) return; close (log_fd); log_fd = -1; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/modelgen.h������������������������������������������������������������������0000644�0000000�0000000�00000006072�11340073704�015470� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _MODELGEN_H #define _MODELGEN_H // // modelgen.h: header file for model generation program // // ********************************************************* // * This file must be identical in the modelgen directory * // * and in the Quake directory, because it's used to * // * pass data from one to the other via model files. * // ********************************************************* #ifdef INCLUDELIBS #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h> #include "cmdlib.h" #include "scriplib.h" #include "trilib.h" #include "lbmlib.h" #include "mathlib.h" #endif #define ALIAS_VERSION 6 #define ALIAS_ONSEAM 0x0020 // must match definition in spritegn.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum {ST_SYNC=0, ST_RAND } synctype_t; #endif typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t; typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; } mdl_t; // TODO: could be shorts typedef struct { int onseam; int s; int t; } stvert_t; typedef struct dtriangle_s { int facesfront; int vertindex[3]; } dtriangle_t; #define DT_FACES_FRONT 0x0010 // This mirrors trivert_t in trilib.h, is present so Quake knows how to // load this data typedef struct { byte v[3]; byte lightnormalindex; } trivertx_t; typedef struct { trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used char name[16]; // frame name from grabbing } daliasframe_t; typedef struct { int numframes; trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used } daliasgroup_t; typedef struct { int numskins; } daliasskingroup_t; typedef struct { float interval; } daliasinterval_t; typedef struct { float interval; } daliasskininterval_t; typedef struct { aliasframetype_t type; } daliasframetype_t; typedef struct { aliasskintype_t type; } daliasskintype_t; #define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') // little-endian "IDPO" #endif /* _MODELGEN_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_flac.c������������������������������������������������������������������0000644�0000000�0000000�00000026650�13101322626�015443� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * fLaC streaming music support, loosely based QuakeForge implementation * with modifications. requires libFLAC >= 1.0.4 at compile and runtime. * * Copyright (C) 2005 Bill Currie <bill@taniwha.org> * Copyright (C) 2013 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_FLAC) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_flac.h" #undef LEGACY_FLAC #include <FLAC/stream_decoder.h> /* FLAC 1.1.3 has FLAC_API_VERSION_CURRENT == 8 */ #if !defined(FLAC_API_VERSION_CURRENT) || ((FLAC_API_VERSION_CURRENT+0) < 8) #define LEGACY_FLAC #include <FLAC/seekable_stream_decoder.h> #endif #ifdef LEGACY_FLAC #define FLAC__StreamDecoder FLAC__SeekableStreamDecoder #define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus #define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus #define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus #define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus #define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new #define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish #define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete #define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single #define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute #define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata #define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state #define FLAC__STREAM_DECODER_INIT_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_OK #define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK #define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR #define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK /* !!! */ #define FLAC__STREAM_DECODER_WRITE_STATUS_ABORT FLAC__STREAM_DECODER_WRITE_STATUS_ABORT #define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK #define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR #define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK #define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR #define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK typedef unsigned FLAC_SIZE_T; #else typedef size_t FLAC_SIZE_T; #endif typedef struct { FLAC__StreamDecoder *decoder; fshandle_t *file; snd_info_t *info; byte *buffer; int size, pos, error; } flacfile_t; /* CALLBACK FUNCTIONS: */ static void flac_error_func (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; ff->error = -1; Con_Printf ("FLAC: decoder error %i\n", status); } static FLAC__StreamDecoderReadStatus flac_read_func (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], FLAC_SIZE_T *bytes, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (*bytes > 0) { *bytes = FS_fread(buffer, 1, *bytes, ff->file); if (FS_ferror(ff->file)) return FLAC__STREAM_DECODER_READ_STATUS_ABORT; if (*bytes == 0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } static FLAC__StreamDecoderSeekStatus flac_seek_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (FS_fseek(ff->file, (long)absolute_byte_offset, SEEK_SET) < 0) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } static FLAC__StreamDecoderTellStatus flac_tell_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; long pos = FS_ftell (ff->file); if (pos < 0) return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; *absolute_byte_offset = (FLAC__uint64) pos; return FLAC__STREAM_DECODER_TELL_STATUS_OK; } static FLAC__StreamDecoderLengthStatus flac_length_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; *stream_length = (FLAC__uint64) FS_filelength (ff->file); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } static FLAC__bool flac_eof_func (const FLAC__StreamDecoder *decoder, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (FS_feof (ff->file)) return true; return false; } static FLAC__StreamDecoderWriteStatus flac_write_func (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (!ff->buffer) { ff->buffer = (byte *) malloc (ff->info->blocksize * ff->info->channels * ff->info->width); if (!ff->buffer) { ff->error = -1; /* needn't set this here, but... */ Con_Printf("Insufficient memory for fLaC audio\n"); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } } if (ff->info->channels == 1) { unsigned i; const FLAC__int32 *in = buffer[0]; if (ff->info->bits == 8) { byte *out = ff->buffer; for (i = 0; i < frame->header.blocksize; i++) *out++ = *in++ + 128; } else { short *out = (short *) ff->buffer; for (i = 0; i < frame->header.blocksize; i++) *out++ = *in++; } } else { unsigned i; const FLAC__int32 *li = buffer[0]; const FLAC__int32 *ri = buffer[1]; if (ff->info->bits == 8) { char *lo = (char *) ff->buffer + 0; char *ro = (char *) ff->buffer + 1; for (i = 0; i < frame->header.blocksize; i++, lo++, ro++) { *lo++ = *li++ + 128; *ro++ = *ri++ + 128; } } else { short *lo = (short *) ff->buffer + 0; short *ro = (short *) ff->buffer + 1; for (i = 0; i < frame->header.blocksize; i++, lo++, ro++) { *lo++ = *li++; *ro++ = *ri++; } } } ff->size = frame->header.blocksize * ff->info->width * ff->info->channels; ff->pos = 0; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void flac_meta_func (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { ff->info->rate = metadata->data.stream_info.sample_rate; ff->info->bits = metadata->data.stream_info.bits_per_sample; ff->info->width = ff->info->bits / 8; ff->info->channels = metadata->data.stream_info.channels; ff->info->blocksize = metadata->data.stream_info.max_blocksize; ff->info->dataofs = 0; /* got the STREAMINFO metadata */ } } static qboolean S_FLAC_CodecInitialize (void) { return true; } static void S_FLAC_CodecShutdown (void) { } static qboolean S_FLAC_CodecOpenStream (snd_stream_t *stream) { flacfile_t *ff; int rc; ff = (flacfile_t *) Z_Malloc(sizeof(flacfile_t)); ff->decoder = FLAC__stream_decoder_new (); if (ff->decoder == NULL) { Con_Printf("Unable to create fLaC decoder\n"); goto _fail; } stream->priv = ff; ff->info = & stream->info; ff->file = & stream->fh; ff->info->dataofs = -1; /* check for STREAMINFO metadata existence */ #ifdef LEGACY_FLAC FLAC__seekable_stream_decoder_set_error_callback (ff->decoder, flac_error_func); FLAC__seekable_stream_decoder_set_read_callback (ff->decoder, flac_read_func); FLAC__seekable_stream_decoder_set_seek_callback (ff->decoder, flac_seek_func); FLAC__seekable_stream_decoder_set_tell_callback (ff->decoder, flac_tell_func); FLAC__seekable_stream_decoder_set_length_callback (ff->decoder, flac_length_func); FLAC__seekable_stream_decoder_set_eof_callback (ff->decoder, flac_eof_func); FLAC__seekable_stream_decoder_set_write_callback (ff->decoder, flac_write_func); FLAC__seekable_stream_decoder_set_metadata_callback (ff->decoder, flac_meta_func); FLAC__seekable_stream_decoder_set_client_data (ff->decoder, ff); rc = FLAC__seekable_stream_decoder_init (ff->decoder); #else rc = FLAC__stream_decoder_init_stream(ff->decoder, flac_read_func, flac_seek_func, flac_tell_func, flac_length_func, flac_eof_func, flac_write_func, flac_meta_func, flac_error_func, ff); #endif if (rc != FLAC__STREAM_DECODER_INIT_STATUS_OK) /* unlikely */ { Con_Printf ("FLAC: decoder init error %i\n", rc); goto _fail; } rc = FLAC__stream_decoder_process_until_end_of_metadata (ff->decoder); if (rc == false || ff->error) { rc = FLAC__stream_decoder_get_state(ff->decoder); Con_Printf("%s not a valid flac file? (decoder state %i)\n", stream->name, rc); goto _fail; } if (ff->info->dataofs < 0) { Con_Printf("%s has no STREAMINFO\n", stream->name); goto _fail; } if (ff->info->bits != 8 && ff->info->bits != 16) { Con_Printf("%s is not 8 or 16 bit\n", stream->name); goto _fail; } if (ff->info->channels != 1 && ff->info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", ff->info->channels, stream->name); goto _fail; } return true; _fail: if (ff->decoder) { FLAC__stream_decoder_finish (ff->decoder); FLAC__stream_decoder_delete (ff->decoder); } Z_Free(ff); return false; } static int S_FLAC_CodecReadStream (snd_stream_t *stream, int len, void *buffer) { flacfile_t *ff = (flacfile_t *) stream->priv; byte *buf = (byte *) buffer; int count = 0; while (len) { int res = 0; if (ff->size == ff->pos) FLAC__stream_decoder_process_single (ff->decoder); if (ff->error) return -1; res = ff->size - ff->pos; if (res > len) res = len; if (res > 0) { memcpy (buf, ff->buffer + ff->pos, res); count += res; len -= res; buf += res; ff->pos += res; } else if (res < 0) { /* error */ return -1; } else { Con_DPrintf ("FLAC: EOF\n"); break; } } return count; } static void S_FLAC_CodecCloseStream (snd_stream_t *stream) { flacfile_t *ff = (flacfile_t *) stream->priv; FLAC__stream_decoder_finish (ff->decoder); FLAC__stream_decoder_delete (ff->decoder); if (ff->buffer) free(ff->buffer); Z_Free(ff); S_CodecUtilClose(&stream); } static int S_FLAC_CodecRewindStream (snd_stream_t *stream) { flacfile_t *ff = (flacfile_t *) stream->priv; ff->pos = ff->size = 0; if (FLAC__stream_decoder_seek_absolute(ff->decoder, 0)) return 0; return -1; } snd_codec_t flac_codec = { CODECTYPE_FLAC, true, /* always available. */ "flac", S_FLAC_CodecInitialize, S_FLAC_CodecShutdown, S_FLAC_CodecOpenStream, S_FLAC_CodecReadStream, S_FLAC_CodecRewindStream, S_FLAC_CodecCloseStream, NULL }; #endif /* USE_CODEC_FLAC */ ����������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cl_demo.c�������������������������������������������������������������������0000644�0000000�0000000�00000025525�12733161720�015302� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" static void CL_FinishTimeDemo (void); /* ============================================================================== DEMO CODE When a demo is playing back, all NET_SendMessages are skipped, and NET_GetMessages are read from the demo file. Whenever cl.time gets past the last received message, another message is read from the demo file. ============================================================================== */ // from ProQuake: space to fill out the demo header for record at any time static byte demo_head[3][MAX_MSGLEN]; static int demo_head_size[2]; /* ============== CL_StopPlayback Called when a demo file runs out, or the user starts a game ============== */ void CL_StopPlayback (void) { if (!cls.demoplayback) return; fclose (cls.demofile); cls.demoplayback = false; cls.demopaused = false; cls.demofile = NULL; cls.state = ca_disconnected; if (cls.timedemo) CL_FinishTimeDemo (); } /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length and view angles ==================== */ static void CL_WriteDemoMessage (void) { int len; int i; float f; len = LittleLong (net_message.cursize); fwrite (&len, 4, 1, cls.demofile); for (i = 0; i < 3; i++) { f = LittleFloat (cl.viewangles[i]); fwrite (&f, 4, 1, cls.demofile); } fwrite (net_message.data, net_message.cursize, 1, cls.demofile); fflush (cls.demofile); } static int CL_GetDemoMessage (void) { int r, i; float f; if (cls.demopaused) return 0; // decide if it is time to grab the next message if (cls.signon == SIGNONS) // always grab until fully connected { if (cls.timedemo) { if (host_framecount == cls.td_lastframe) return 0; // already read this frame's message cls.td_lastframe = host_framecount; // if this is the second frame, grab the real td_starttime // so the bogus time on the first frame doesn't count if (host_framecount == cls.td_startframe + 1) cls.td_starttime = realtime; } else if (/* cl.time > 0 && */ cl.time <= cl.mtime[0]) { return 0; // don't need another message yet } } // get the next message fread (&net_message.cursize, 4, 1, cls.demofile); VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); for (i = 0 ; i < 3 ; i++) { r = fread (&f, 4, 1, cls.demofile); cl.mviewangles[0][i] = LittleFloat (f); } net_message.cursize = LittleLong (net_message.cursize); if (net_message.cursize > MAX_MSGLEN) Sys_Error ("Demo message > MAX_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } return 1; } /* ==================== CL_GetMessage Handles recording and playback of demos, on top of NET_ code ==================== */ int CL_GetMessage (void) { int r; if (cls.demoplayback) return CL_GetDemoMessage (); while (1) { r = NET_GetMessage (cls.netcon); if (r != 1 && r != 2) return r; // discard nop keepalive message if (net_message.cursize == 1 && net_message.data[0] == svc_nop) Con_Printf ("<-- server to client keepalive\n"); else break; } if (cls.demorecording) CL_WriteDemoMessage (); if (cls.signon < 2) { // record messages before full connection, so that a // demo record can happen after connection is done memcpy(demo_head[cls.signon], net_message.data, net_message.cursize); demo_head_size[cls.signon] = net_message.cursize; } return r; } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { if (cmd_source != src_command) return; if (!cls.demorecording) { Con_Printf ("Not recording a demo.\n"); return; } // write a disconnect message to the demo file SZ_Clear (&net_message); MSG_WriteByte (&net_message, svc_disconnect); CL_WriteDemoMessage (); // finish up fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Con_Printf ("Completed demo\n"); // ericw -- update demo tab-completion list DemoList_Rebuild (); } /* ==================== CL_Record_f record <demoname> <map> [cd track] ==================== */ void CL_Record_f (void) { int c; char name[MAX_OSPATH]; int track; if (cmd_source != src_command) return; if (cls.demoplayback) { Con_Printf ("Can't record during demo playback\n"); return; } if (cls.demorecording) CL_Stop_f(); c = Cmd_Argc(); if (c != 2 && c != 3 && c != 4) { Con_Printf ("record <demoname> [<map> [cd track]]\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } if (c == 2 && cls.state == ca_connected) { #if 0 Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n"); return; #endif if (cls.signon < 2) { Con_Printf("Can't record - try again when connected\n"); return; } } // write the forced cd track number, or -1 if (c == 4) { track = atoi(Cmd_Argv(3)); Con_Printf ("Forcing CD track to %i\n", cls.forcetrack); } else { track = -1; } q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); // start the map up if (c > 2) { Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command); if (cls.state != ca_connected) return; } // open the demo file COM_AddExtension (name, ".dem", sizeof(name)); Con_Printf ("recording to %s.\n", name); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't create %s\n", name); return; } cls.forcetrack = track; fprintf (cls.demofile, "%i\n", cls.forcetrack); cls.demorecording = true; // from ProQuake: initialize the demo file if we're already connected if (c == 2 && cls.state == ca_connected) { byte *data = net_message.data; int cursize = net_message.cursize; int i; for (i = 0; i < 2; i++) { net_message.data = demo_head[i]; net_message.cursize = demo_head_size[i]; CL_WriteDemoMessage(); } net_message.data = demo_head[2]; SZ_Clear (&net_message); // current names, colors, and frag counts for (i = 0; i < cl.maxclients; i++) { MSG_WriteByte (&net_message, svc_updatename); MSG_WriteByte (&net_message, i); MSG_WriteString (&net_message, cl.scores[i].name); MSG_WriteByte (&net_message, svc_updatefrags); MSG_WriteByte (&net_message, i); MSG_WriteShort (&net_message, cl.scores[i].frags); MSG_WriteByte (&net_message, svc_updatecolors); MSG_WriteByte (&net_message, i); MSG_WriteByte (&net_message, cl.scores[i].colors); } // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&net_message, svc_lightstyle); MSG_WriteByte (&net_message, i); MSG_WriteString (&net_message, cl_lightstyle[i].map); } // what about the CD track or SVC fog... future consideration. MSG_WriteByte (&net_message, svc_updatestat); MSG_WriteByte (&net_message, STAT_TOTALSECRETS); MSG_WriteLong (&net_message, cl.stats[STAT_TOTALSECRETS]); MSG_WriteByte (&net_message, svc_updatestat); MSG_WriteByte (&net_message, STAT_TOTALMONSTERS); MSG_WriteLong (&net_message, cl.stats[STAT_TOTALMONSTERS]); MSG_WriteByte (&net_message, svc_updatestat); MSG_WriteByte (&net_message, STAT_SECRETS); MSG_WriteLong (&net_message, cl.stats[STAT_SECRETS]); MSG_WriteByte (&net_message, svc_updatestat); MSG_WriteByte (&net_message, STAT_MONSTERS); MSG_WriteLong (&net_message, cl.stats[STAT_MONSTERS]); // view entity MSG_WriteByte (&net_message, svc_setview); MSG_WriteShort (&net_message, cl.viewentity); // signon MSG_WriteByte (&net_message, svc_signonnum); MSG_WriteByte (&net_message, 3); CL_WriteDemoMessage(); // restore net_message net_message.data = data; net_message.cursize = cursize; } } /* ==================== CL_PlayDemo_f play [demoname] ==================== */ void CL_PlayDemo_f (void) { char name[MAX_OSPATH]; int i, c; qboolean neg; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("playdemo <demoname> : plays a demo\n"); return; } // disconnect from server CL_Disconnect (); // open the demo file q_strlcpy (name, Cmd_Argv(1), sizeof(name)); COM_AddExtension (name, ".dem", sizeof(name)); Con_Printf ("Playing demo from %s.\n", name); COM_FOpenFile (name, &cls.demofile, NULL); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open %s\n", name); cls.demonum = -1; // stop demo loop return; } // ZOID, fscanf is evil // O.S.: if a space character e.g. 0x20 (' ') follows '\n', // fscanf skips that byte too and screws up further reads. // fscanf (cls.demofile, "%i\n", &cls.forcetrack); cls.forcetrack = 0; c = 0; /* silence pesky compiler warnings */ neg = false; // read a decimal integer possibly with a leading '-', // followed by a '\n': for (i = 0; i < 13; i++) { c = getc(cls.demofile); if (c == '\n') break; if (c == '-') { neg = true; continue; } // check for multiple '-' or legal digits? meh... cls.forcetrack = cls.forcetrack * 10 + (c - '0'); } if (c != '\n') { fclose (cls.demofile); cls.demofile = NULL; cls.demonum = -1; // stop demo loop Con_Printf ("ERROR: demo \"%s\" is invalid\n", name); return; } if (neg) cls.forcetrack = -cls.forcetrack; cls.demoplayback = true; cls.demopaused = false; cls.state = ca_connected; // get rid of the menu and/or console key_dest = key_game; } /* ==================== CL_FinishTimeDemo ==================== */ static void CL_FinishTimeDemo (void) { int frames; float time; cls.timedemo = false; // the first frame didn't count frames = (host_framecount - cls.td_startframe) - 1; time = realtime - cls.td_starttime; if (!time) time = 1; Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); } /* ==================== CL_TimeDemo_f timedemo [demoname] ==================== */ void CL_TimeDemo_f (void) { if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("timedemo <demoname> : gets demo speeds\n"); return; } CL_PlayDemo_f (); if (!cls.demofile) return; // cls.td_starttime will be grabbed at the second frame of the demo, so // all the loading time doesn't get counted cls.timedemo = true; cls.td_startframe = host_framecount; cls.td_lastframe = -1; // get a new message this frame } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pl_linux.c������������������������������������������������������������������0000644�0000000�0000000�00000004522�12407762022�015524� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif static const Uint8 bmp_bytes[] = { #include "qs_bmp.h" }; void PL_SetWindowIcon (void) { SDL_RWops *rwop; SDL_Surface *icon; Uint32 colorkey; /* SDL_RWFromConstMem() requires SDL >= 1.2.7 */ rwop = SDL_RWFromConstMem(bmp_bytes, sizeof(bmp_bytes)); if (rwop == NULL) return; icon = SDL_LoadBMP_RW(rwop, 1); if (icon == NULL) return; /* make pure magenta (#ff00ff) tranparent */ colorkey = SDL_MapRGB(icon->format, 255, 0, 255); #if defined(USE_SDL2) SDL_SetColorKey(icon, SDL_TRUE, colorkey); SDL_SetWindowIcon((SDL_Window*) VID_GetWindow(), icon); #else SDL_SetColorKey(icon, SDL_SRCCOLORKEY, colorkey); SDL_WM_SetIcon(icon, NULL); #endif SDL_FreeSurface(icon); } void PL_VID_Shutdown (void) { } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *PL_GetClipboardData (void) { char *data = NULL; #if defined(USE_SDL2) char *cliptext = SDL_GetClipboardText(); if (cliptext != NULL) { size_t size = strlen(cliptext) + 1; /* this is intended for simple small text copies * such as an ip address, etc: do chop the size * here, otherwise we may experience Z_Malloc() * failures and all other not-oh-so-fun stuff. */ size = q_min(MAX_CLIPBOARDTXT, size); data = (char *) Z_Malloc(size); q_strlcpy (data, cliptext, size); } #endif return data; } void PL_ErrorDialog (const char *errorMsg) { } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sys.h�����������������������������������������������������������������������0000644�0000000�0000000�00000003711�13136715127�014520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_SYS_H #define _QUAKE_SYS_H // sys.h -- non-portable functions void Sys_Init (void); // // file IO // // returns the file size or -1 if file is not present. // the file should be in BINARY mode for stupid OSs that care int Sys_FileOpenRead (const char *path, int *hndl); int Sys_FileOpenWrite (const char *path); void Sys_FileClose (int handle); void Sys_FileSeek (int handle, int position); int Sys_FileRead (int handle, void *dest, int count); int Sys_FileWrite (int handle,const void *data, int count); int Sys_FileTime (const char *path); void Sys_mkdir (const char *path); // // system IO // FUNC_NORETURN void Sys_Quit (void); FUNC_NORETURN void Sys_Error (const char *error, ...) FUNC_PRINTF(1,2); // an error will cause the entire program to exit #ifdef __WATCOMC__ #pragma aux Sys_Error aborts; #pragma aux Sys_Quit aborts; #endif void Sys_Printf (const char *fmt, ...) FUNC_PRINTF(1,2); // send text to the console double Sys_DoubleTime (void); const char *Sys_ConsoleInput (void); void Sys_Sleep (unsigned long msecs); // yield for about 'msecs' milliseconds. void Sys_SendKeyEvents (void); // Perform Key_Event () callbacks until the input que is empty #endif /* _QUAKE_SYS_H */ �������������������������������������������������������quakespasm-0.93.0/Quake/gl_fog.c��������������������������������������������������������������������0000644�0000000�0000000�00000020163�12625545642�015137� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //gl_fog.c -- global and volumetric fog #include "quakedef.h" //============================================================================== // // GLOBAL FOG // //============================================================================== #define DEFAULT_DENSITY 0.0 #define DEFAULT_GRAY 0.3 float fog_density; float fog_red; float fog_green; float fog_blue; float old_density; float old_red; float old_green; float old_blue; float fade_time; //duration of fade float fade_done; //time when fade will be done /* ============= Fog_Update update internal variables ============= */ void Fog_Update (float density, float red, float green, float blue, float time) { //save previous settings for fade if (time > 0) { //check for a fade in progress if (fade_done > cl.time) { float f; f = (fade_done - cl.time) / fade_time; old_density = f * old_density + (1.0 - f) * fog_density; old_red = f * old_red + (1.0 - f) * fog_red; old_green = f * old_green + (1.0 - f) * fog_green; old_blue = f * old_blue + (1.0 - f) * fog_blue; } else { old_density = fog_density; old_red = fog_red; old_green = fog_green; old_blue = fog_blue; } } fog_density = density; fog_red = red; fog_green = green; fog_blue = blue; fade_time = time; fade_done = cl.time + time; } /* ============= Fog_ParseServerMessage handle an SVC_FOG message from server ============= */ void Fog_ParseServerMessage (void) { float density, red, green, blue, time; density = MSG_ReadByte() / 255.0; red = MSG_ReadByte() / 255.0; green = MSG_ReadByte() / 255.0; blue = MSG_ReadByte() / 255.0; time = q_max(0.0, MSG_ReadShort() / 100.0); Fog_Update (density, red, green, blue, time); } /* ============= Fog_FogCommand_f handle the 'fog' console command ============= */ void Fog_FogCommand_f (void) { switch (Cmd_Argc()) { default: case 1: Con_Printf("usage:\n"); Con_Printf(" fog <density>\n"); Con_Printf(" fog <red> <green> <blue>\n"); Con_Printf(" fog <density> <red> <green> <blue>\n"); Con_Printf("current values:\n"); Con_Printf(" \"density\" is \"%f\"\n", fog_density); Con_Printf(" \"red\" is \"%f\"\n", fog_red); Con_Printf(" \"green\" is \"%f\"\n", fog_green); Con_Printf(" \"blue\" is \"%f\"\n", fog_blue); break; case 2: Fog_Update(q_max(0.0, atof(Cmd_Argv(1))), fog_red, fog_green, fog_blue, 0.0); break; case 3: //TEST Fog_Update(q_max(0.0, atof(Cmd_Argv(1))), fog_red, fog_green, fog_blue, atof(Cmd_Argv(2))); break; case 4: Fog_Update(fog_density, CLAMP(0.0, atof(Cmd_Argv(1)), 1.0), CLAMP(0.0, atof(Cmd_Argv(2)), 1.0), CLAMP(0.0, atof(Cmd_Argv(3)), 1.0), 0.0); break; case 5: Fog_Update(q_max(0.0, atof(Cmd_Argv(1))), CLAMP(0.0, atof(Cmd_Argv(2)), 1.0), CLAMP(0.0, atof(Cmd_Argv(3)), 1.0), CLAMP(0.0, atof(Cmd_Argv(4)), 1.0), 0.0); break; case 6: //TEST Fog_Update(q_max(0.0, atof(Cmd_Argv(1))), CLAMP(0.0, atof(Cmd_Argv(2)), 1.0), CLAMP(0.0, atof(Cmd_Argv(3)), 1.0), CLAMP(0.0, atof(Cmd_Argv(4)), 1.0), atof(Cmd_Argv(5))); break; } } /* ============= Fog_ParseWorldspawn called at map load ============= */ void Fog_ParseWorldspawn (void) { char key[128], value[4096]; const char *data; //initially no fog fog_density = DEFAULT_DENSITY; fog_red = DEFAULT_GRAY; fog_green = DEFAULT_GRAY; fog_blue = DEFAULT_GRAY; old_density = DEFAULT_DENSITY; old_red = DEFAULT_GRAY; old_green = DEFAULT_GRAY; old_blue = DEFAULT_GRAY; fade_time = 0.0; fade_done = 0.0; data = COM_Parse(cl.worldmodel->entities); if (!data) return; // error if (com_token[0] != '{') return; // error while (1) { data = COM_Parse(data); if (!data) return; // error if (com_token[0] == '}') break; // end of worldspawn if (com_token[0] == '_') strcpy(key, com_token + 1); else strcpy(key, com_token); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; data = COM_Parse(data); if (!data) return; // error strcpy(value, com_token); if (!strcmp("fog", key)) { sscanf(value, "%f %f %f %f", &fog_density, &fog_red, &fog_green, &fog_blue); } } } /* ============= Fog_GetColor calculates fog color for this frame, taking into account fade times ============= */ float *Fog_GetColor (void) { static float c[4]; float f; int i; if (fade_done > cl.time) { f = (fade_done - cl.time) / fade_time; c[0] = f * old_red + (1.0 - f) * fog_red; c[1] = f * old_green + (1.0 - f) * fog_green; c[2] = f * old_blue + (1.0 - f) * fog_blue; c[3] = 1.0; } else { c[0] = fog_red; c[1] = fog_green; c[2] = fog_blue; c[3] = 1.0; } //find closest 24-bit RGB value, so solid-colored sky can match the fog perfectly for (i=0;i<3;i++) c[i] = (float)(Q_rint(c[i] * 255)) / 255.0f; return c; } /* ============= Fog_GetDensity returns current density of fog ============= */ float Fog_GetDensity (void) { float f; if (fade_done > cl.time) { f = (fade_done - cl.time) / fade_time; return f * old_density + (1.0 - f) * fog_density; } else return fog_density; } /* ============= Fog_SetupFrame called at the beginning of each frame ============= */ void Fog_SetupFrame (void) { glFogfv(GL_FOG_COLOR, Fog_GetColor()); glFogf(GL_FOG_DENSITY, Fog_GetDensity() / 64.0); } /* ============= Fog_EnableGFog called before drawing stuff that should be fogged ============= */ void Fog_EnableGFog (void) { if (Fog_GetDensity() > 0) glEnable(GL_FOG); } /* ============= Fog_DisableGFog called after drawing stuff that should be fogged ============= */ void Fog_DisableGFog (void) { if (Fog_GetDensity() > 0) glDisable(GL_FOG); } /* ============= Fog_StartAdditive called before drawing stuff that is additive blended -- sets fog color to black ============= */ void Fog_StartAdditive (void) { vec3_t color = {0,0,0}; if (Fog_GetDensity() > 0) glFogfv(GL_FOG_COLOR, color); } /* ============= Fog_StopAdditive called after drawing stuff that is additive blended -- restores fog color ============= */ void Fog_StopAdditive (void) { if (Fog_GetDensity() > 0) glFogfv(GL_FOG_COLOR, Fog_GetColor()); } //============================================================================== // // VOLUMETRIC FOG // //============================================================================== cvar_t r_vfog = {"r_vfog", "1", CVAR_NONE}; void Fog_DrawVFog (void){} void Fog_MarkModels (void){} //============================================================================== // // INIT // //============================================================================== /* ============= Fog_NewMap called whenever a map is loaded ============= */ void Fog_NewMap (void) { Fog_ParseWorldspawn (); //for global fog Fog_MarkModels (); //for volumetric fog } /* ============= Fog_Init called when quake initializes ============= */ void Fog_Init (void) { Cmd_AddCommand ("fog",Fog_FogCommand_f); //Cvar_RegisterVariable (&r_vfog); //set up global fog fog_density = DEFAULT_DENSITY; fog_red = DEFAULT_GRAY; fog_green = DEFAULT_GRAY; fog_blue = DEFAULT_GRAY; Fog_SetupState (); } /* ============= Fog_SetupState ericw -- moved from Fog_Init, state that needs to be setup when a new context is created ============= */ void Fog_SetupState (void) { glFogi(GL_FOG_MODE, GL_EXP2); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_wipx.h������������������������������������������������������������������0000644�0000000�0000000�00000003553�12407762022�015537� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __NET_WINIPX_H #define __NET_WINIPX_H sys_socket_t WIPX_Init (void); void WIPX_Shutdown (void); void WIPX_Listen (qboolean state); sys_socket_t WIPX_OpenSocket (int port); int WIPX_CloseSocket (sys_socket_t socketid); int WIPX_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t WIPX_CheckNewConnections (void); int WIPX_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WIPX_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WIPX_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *WIPX_AddrToString (struct qsockaddr *addr); int WIPX_StringToAddr (const char *string, struct qsockaddr *addr); int WIPX_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name); int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr); int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int WIPX_GetSocketPort (struct qsockaddr *addr); int WIPX_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __NET_WINIPX_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net.h�����������������������������������������������������������������������0000644�0000000�0000000�00000006640�12506735242�014474� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2009-2010 Ozkan Sezer Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* net.h quake's interface to the networking layer network functions and data, common to the whole engine */ #ifndef _QUAKE_NET_H #define _QUAKE_NET_H #define NET_NAMELEN 64 #define NET_MAXMESSAGE 64000 /* ericw -- was 32000 */ extern int DEFAULTnet_hostport; extern int net_hostport; extern cvar_t hostname; extern double net_time; extern sizebuf_t net_message; extern int net_activeconnections; void NET_Init (void); void NET_Shutdown (void); struct qsocket_s *NET_CheckNewConnections (void); // returns a new connection number if there is one pending, else -1 struct qsocket_s *NET_Connect (const char *host); // called by client to connect to a host. Returns -1 if not able to double NET_QSocketGetTime (const struct qsocket_s *sock); const char *NET_QSocketGetAddressString (const struct qsocket_s *sock); qboolean NET_CanSendMessage (struct qsocket_s *sock); // Returns true or false if the given qsocket can currently accept a // message to be transmitted. int NET_GetMessage (struct qsocket_s *sock); // returns data in net_message sizebuf // returns 0 if no data is waiting // returns 1 if a message was received // returns 2 if an unreliable message was received // returns -1 if the connection died int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data); int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data); // returns 0 if the message connot be delivered reliably, but the connection // is still considered valid // returns 1 if the message was sent properly // returns -1 if the connection died int NET_SendToAll(sizebuf_t *data, double blocktime); // This is a reliable *blocking* send to all attached clients. void NET_Close (struct qsocket_s *sock); // if a dead connection is returned by a get or send function, this function // should be called when it is convenient // Server calls when a client is kicked off for a game related misbehavior // like an illegal protocal conversation. Client calls when disconnecting // from a server. // A netcon_t number will not be reused until this function is called for it void NET_Poll (void); // Server list related globals: extern qboolean slistInProgress; extern qboolean slistSilent; extern qboolean slistLocal; extern int hostCacheCount; void NET_Slist_f (void); void NET_SlistSort (void); const char *NET_SlistPrintServer (int n); const char *NET_SlistPrintServerName (int n); /* FIXME: driver related, but public: */ extern qboolean ipxAvailable; extern qboolean tcpipAvailable; extern char my_ipx_address[NET_NAMELEN]; extern char my_tcpip_address[NET_NAMELEN]; #endif /* _QUAKE_NET_H */ ������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/screen.h��������������������������������������������������������������������0000644�0000000�0000000�00000004147�12407762022�015161� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_SCREEN_H #define _QUAKE_SCREEN_H // screen.h void SCR_Init (void); void SCR_LoadPics (void); void SCR_UpdateScreen (void); void SCR_SizeUp (void); void SCR_SizeDown (void); void SCR_BringDownConsole (void); void SCR_CenterPrint (const char *str); void SCR_BeginLoadingPlaque (void); void SCR_EndLoadingPlaque (void); int SCR_ModalMessage (const char *text, float timeout); //johnfitz -- added timeout extern float scr_con_current; extern float scr_conlines; // lines of console to display extern int sb_lines; extern int clearnotify; // set to 0 whenever notify text is drawn extern qboolean scr_disabled_for_loading; extern qboolean scr_skipupdate; extern cvar_t scr_viewsize; extern cvar_t scr_sbaralpha; //johnfitz void SCR_UpdateWholeScreen (void); //johnfitz -- stuff for 2d drawing control typedef enum { CANVAS_NONE, CANVAS_DEFAULT, CANVAS_CONSOLE, CANVAS_MENU, CANVAS_SBAR, CANVAS_WARPIMAGE, CANVAS_CROSSHAIR, CANVAS_BOTTOMLEFT, CANVAS_BOTTOMRIGHT, CANVAS_TOPRIGHT, CANVAS_INVALID = -1 } canvastype; extern cvar_t scr_menuscale; extern cvar_t scr_sbarscale; extern cvar_t scr_conwidth; extern cvar_t scr_conscale; extern cvar_t scr_scale; extern cvar_t scr_crosshairscale; //johnfitz extern int scr_tileclear_updates; //johnfitz #endif /* _QUAKE_SCREEN_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/strl_fn.h�������������������������������������������������������������������0000644�0000000�0000000�00000000503�11676323013�015341� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* header file for BSD strlcat and strlcpy */ #ifndef __STRLFUNCS_H #define __STRLFUNCS_H /* use our own copies of strlcpy and strlcat taken from OpenBSD */ extern size_t q_strlcpy (char *dst, const char *src, size_t size); extern size_t q_strlcat (char *dst, const char *src, size_t size); #endif /* __STRLFUNCS_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/build_cross_win32-sdl2.sh���������������������������������������������������0000755�0000000�0000000�00000000714�12475156650�020271� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Change this script to meet your needs and/or environment. TARGET=i686-w64-mingw32 PREFIX=/opt/cross_win32 PATH="$PREFIX/bin:$PATH" export PATH MAKE_CMD=make CC="$TARGET-gcc" AS="$TARGET-as" RANLIB="$TARGET-ranlib" AR="$TARGET-ar" WINDRES="$TARGET-windres" STRIP="$TARGET-strip" export PATH CC AS AR RANLIB WINDRES STRIP exec $MAKE_CMD USE_SDL2=1 WINSOCK2=1 CC=$CC AS=$AS RANLIB=$RANLIB AR=$AR WINDRES=$WINDRES STRIP=$STRIP -f Makefile.w32 $* ����������������������������������������������������quakespasm-0.93.0/Quake/snd_opus.h������������������������������������������������������������������0000644�0000000�0000000�00000000323�12171711457�015530� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Ogg/Opus streaming music support. */ #if !defined(_SND_OPUS_H_) #define _SND_OPUS_H_ 1 #if defined(USE_CODEC_OPUS) extern snd_codec_t opus_codec; #endif /* USE_CODEC_OPUS */ #endif /* ! _SND_OPUS_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sv_main.c�������������������������������������������������������������������0000644�0000000�0000000�00000111721�13136713770�015334� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_main.c -- server main program #include "quakedef.h" server_t sv; server_static_t svs; static char localmodels[MAX_MODELS][8]; // inline model names for precache int sv_protocol = PROTOCOL_FITZQUAKE; //johnfitz extern qboolean pr_alpha_supported; //johnfitz //============================================================================ /* =============== SV_Protocol_f =============== */ void SV_Protocol_f (void) { int i; switch (Cmd_Argc()) { case 1: Con_Printf ("\"sv_protocol\" is \"%i\"\n", sv_protocol); break; case 2: i = atoi(Cmd_Argv(1)); if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) Con_Printf ("sv_protocol must be %i or %i or %i\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); else { sv_protocol = i; if (sv.active) Con_Printf ("changes will not take effect until the next level load.\n"); } break; default: Con_SafePrintf ("usage: sv_protocol <protocol>\n"); break; } } /* =============== SV_Init =============== */ void SV_Init (void) { int i; const char *p; extern cvar_t sv_maxvelocity; extern cvar_t sv_gravity; extern cvar_t sv_nostep; extern cvar_t sv_freezenonclients; extern cvar_t sv_friction; extern cvar_t sv_edgefriction; extern cvar_t sv_stopspeed; extern cvar_t sv_maxspeed; extern cvar_t sv_accelerate; extern cvar_t sv_idealpitchscale; extern cvar_t sv_aim; extern cvar_t sv_altnoclip; //johnfitz sv.edicts = NULL; // ericw -- sv.edicts switched to use malloc() Cvar_RegisterVariable (&sv_maxvelocity); Cvar_RegisterVariable (&sv_gravity); Cvar_RegisterVariable (&sv_friction); Cvar_SetCallback (&sv_gravity, Host_Callback_Notify); Cvar_SetCallback (&sv_friction, Host_Callback_Notify); Cvar_RegisterVariable (&sv_edgefriction); Cvar_RegisterVariable (&sv_stopspeed); Cvar_RegisterVariable (&sv_maxspeed); Cvar_SetCallback (&sv_maxspeed, Host_Callback_Notify); Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_idealpitchscale); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&sv_nostep); Cvar_RegisterVariable (&sv_freezenonclients); Cvar_RegisterVariable (&sv_altnoclip); //johnfitz Cmd_AddCommand ("sv_protocol", &SV_Protocol_f); //johnfitz for (i=0 ; i<MAX_MODELS ; i++) sprintf (localmodels[i], "*%i", i); i = COM_CheckParm ("-protocol"); if (i && i < com_argc - 1) sv_protocol = atoi (com_argv[i + 1]); switch (sv_protocol) { case PROTOCOL_NETQUAKE: p = "NetQuake"; break; case PROTOCOL_FITZQUAKE: p = "FitzQuake"; break; case PROTOCOL_RMQ: p = "RMQ"; break; default: Sys_Error ("Bad protocol version request %i. Accepted values: %i, %i, %i.", sv_protocol, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); return; /* silence compiler */ } Sys_Printf ("Server using protocol %i (%s)\n", sv_protocol, p); } /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ================== SV_StartParticle Make sure the event gets sent to all clients ================== */ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count) { int i, v; if (sv.datagram.cursize > MAX_DATAGRAM-16) return; MSG_WriteByte (&sv.datagram, svc_particle); MSG_WriteCoord (&sv.datagram, org[0], sv.protocolflags); MSG_WriteCoord (&sv.datagram, org[1], sv.protocolflags); MSG_WriteCoord (&sv.datagram, org[2], sv.protocolflags); for (i=0 ; i<3 ; i++) { v = dir[i]*16; if (v > 127) v = 127; else if (v < -128) v = -128; MSG_WriteChar (&sv.datagram, v); } MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, color); } /* ================== SV_StartSound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything allready running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. (max 4 attenuation) ================== */ void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation) { int sound_num, ent; int i, field_mask; if (volume < 0 || volume > 255) Host_Error ("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) Host_Error ("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 7) Host_Error ("SV_StartSound: channel = %i", channel); if (sv.datagram.cursize > MAX_DATAGRAM-16) return; // find precache number for sound for (sound_num = 1; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++) { if (!strcmp(sample, sv.sound_precache[sound_num])) break; } if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) { Con_Printf ("SV_StartSound: %s not precacheed\n", sample); return; } ent = NUM_FOR_EDICT(entity); field_mask = 0; if (volume != DEFAULT_SOUND_PACKET_VOLUME) field_mask |= SND_VOLUME; if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) field_mask |= SND_ATTENUATION; //johnfitz -- PROTOCOL_FITZQUAKE if (ent >= 8192) { if (sv.protocol == PROTOCOL_NETQUAKE) return; //don't send any info protocol can't support else field_mask |= SND_LARGEENTITY; } if (sound_num >= 256 || channel >= 8) { if (sv.protocol == PROTOCOL_NETQUAKE) return; //don't send any info protocol can't support else field_mask |= SND_LARGESOUND; } //johnfitz // directed messages go only to the entity the are targeted on MSG_WriteByte (&sv.datagram, svc_sound); MSG_WriteByte (&sv.datagram, field_mask); if (field_mask & SND_VOLUME) MSG_WriteByte (&sv.datagram, volume); if (field_mask & SND_ATTENUATION) MSG_WriteByte (&sv.datagram, attenuation*64); //johnfitz -- PROTOCOL_FITZQUAKE if (field_mask & SND_LARGEENTITY) { MSG_WriteShort (&sv.datagram, ent); MSG_WriteByte (&sv.datagram, channel); } else MSG_WriteShort (&sv.datagram, (ent<<3) | channel); if (field_mask & SND_LARGESOUND) MSG_WriteShort (&sv.datagram, sound_num); else MSG_WriteByte (&sv.datagram, sound_num); //johnfitz for (i = 0; i < 3; i++) MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]), sv.protocolflags); } /* ============================================================================== CLIENT SPAWNING ============================================================================== */ /* ================ SV_SendServerinfo Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ void SV_SendServerinfo (client_t *client) { const char **s; char message[2048]; int i; //johnfitz MSG_WriteByte (&client->message, svc_print); sprintf (message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version MSG_WriteString (&client->message,message); MSG_WriteByte (&client->message, svc_serverinfo); MSG_WriteLong (&client->message, sv.protocol); //johnfitz -- sv.protocol instead of PROTOCOL_VERSION if (sv.protocol == PROTOCOL_RMQ) { // mh - now send protocol flags so that the client knows the protocol features to expect MSG_WriteLong (&client->message, sv.protocolflags); } MSG_WriteByte (&client->message, svs.maxclients); if (!coop.value && deathmatch.value) MSG_WriteByte (&client->message, GAME_DEATHMATCH); else MSG_WriteByte (&client->message, GAME_COOP); MSG_WriteString (&client->message, PR_GetString(sv.edicts->v.message)); //johnfitz -- only send the first 256 model and sound precaches if protocol is 15 for (i=0,s = sv.model_precache+1 ; *s; s++,i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); for (i=0,s = sv.sound_precache+1 ; *s ; s++,i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); //johnfitz // send music MSG_WriteByte (&client->message, svc_cdtrack); MSG_WriteByte (&client->message, sv.edicts->v.sounds); MSG_WriteByte (&client->message, sv.edicts->v.sounds); // set view MSG_WriteByte (&client->message, svc_setview); MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); MSG_WriteByte (&client->message, svc_signonnum); MSG_WriteByte (&client->message, 1); client->sendsignon = true; client->spawned = false; // need prespawn, spawn, etc } /* ================ SV_ConnectClient Initializes a client_t for a new net connection. This will only be called once for a player each game, not once for each level change. ================ */ void SV_ConnectClient (int clientnum) { edict_t *ent; client_t *client; int edictnum; struct qsocket_s *netconnection; int i; float spawn_parms[NUM_SPAWN_PARMS]; client = svs.clients + clientnum; Con_DPrintf ("Client %s connected\n", NET_QSocketGetAddressString(client->netconnection)); edictnum = clientnum+1; ent = EDICT_NUM(edictnum); // set up the client_t netconnection = client->netconnection; if (sv.loadgame) memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); memset (client, 0, sizeof(*client)); client->netconnection = netconnection; strcpy (client->name, "unconnected"); client->active = true; client->spawned = false; client->edict = ent; client->message.data = client->msgbuf; client->message.maxsize = sizeof(client->msgbuf); client->message.allowoverflow = true; // we can catch it if (sv.loadgame) memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); else { // call the progs to get default spawn parms for the new client PR_ExecuteProgram (pr_global_struct->SetNewParms); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) client->spawn_parms[i] = (&pr_global_struct->parm1)[i]; } SV_SendServerinfo (client); } /* =================== SV_CheckForNewClients =================== */ void SV_CheckForNewClients (void) { struct qsocket_s *ret; int i; // // check for new connections // while (1) { ret = NET_CheckNewConnections (); if (!ret) break; // // init a new client structure // for (i=0 ; i<svs.maxclients ; i++) if (!svs.clients[i].active) break; if (i == svs.maxclients) Sys_Error ("Host_CheckForNewClients: no free clients"); svs.clients[i].netconnection = ret; SV_ConnectClient (i); net_activeconnections++; } } /* =============================================================================== FRAME UPDATES =============================================================================== */ /* ================== SV_ClearDatagram ================== */ void SV_ClearDatagram (void) { SZ_Clear (&sv.datagram); } /* ============================================================================= The PVS must include a small area around the client to allow head bobbing or other small motion on the client side. Otherwise, a bob might cause an entity that should be visible to not show up, especially when the bob crosses a waterline. ============================================================================= */ static int fatbytes; static byte *fatpvs; static int fatpvs_capacity; void SV_AddToFatPVS (vec3_t org, mnode_t *node, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter { int i; byte *pvs; mplane_t *plane; float d; while (1) { // if this is a leaf, accumulate the pvs bits if (node->contents < 0) { if (node->contents != CONTENTS_SOLID) { pvs = Mod_LeafPVS ( (mleaf_t *)node, worldmodel); //johnfitz -- worldmodel as a parameter for (i=0 ; i<fatbytes ; i++) fatpvs[i] |= pvs[i]; } return; } plane = node->plane; d = DotProduct (org, plane->normal) - plane->dist; if (d > 8) node = node->children[0]; else if (d < -8) node = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0], worldmodel); //johnfitz -- worldmodel as a parameter node = node->children[1]; } } } /* ============= SV_FatPVS Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. ============= */ byte *SV_FatPVS (vec3_t org, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter { fatbytes = (worldmodel->numleafs+7)>>3; // ericw -- was +31, assumed to be a bug/typo if (fatpvs == NULL || fatbytes > fatpvs_capacity) { fatpvs_capacity = fatbytes; fatpvs = (byte *) realloc (fatpvs, fatpvs_capacity); if (!fatpvs) Sys_Error ("SV_FatPVS: realloc() failed on %d bytes", fatpvs_capacity); } Q_memset (fatpvs, 0, fatbytes); SV_AddToFatPVS (org, worldmodel->nodes, worldmodel); //johnfitz -- worldmodel as a parameter return fatpvs; } /* ============= SV_VisibleToClient -- johnfitz PVS test encapsulated in a nice function ============= */ qboolean SV_VisibleToClient (edict_t *client, edict_t *test, qmodel_t *worldmodel) { byte *pvs; vec3_t org; int i; VectorAdd (client->v.origin, client->v.view_ofs, org); pvs = SV_FatPVS (org, worldmodel); for (i=0 ; i < test->num_leafs ; i++) if (pvs[test->leafnums[i] >> 3] & (1 << (test->leafnums[i]&7) )) return true; return false; } //============================================================================= /* ============= SV_WriteEntitiesToClient ============= */ void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) { int e, i; int bits; byte *pvs; vec3_t org; float miss; edict_t *ent; // find the client's PVS VectorAdd (clent->v.origin, clent->v.view_ofs, org); pvs = SV_FatPVS (org, sv.worldmodel); // send over all entities (excpet the client) that touch the pvs ent = NEXT_EDICT(sv.edicts); for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) { if (ent != clent) // clent is ALLWAYS sent { // ignore ents without visible models if (!ent->v.modelindex || !PR_GetString(ent->v.model)[0]) continue; //johnfitz -- don't send model>255 entities if protocol is 15 if (sv.protocol == PROTOCOL_NETQUAKE && (int)ent->v.modelindex & 0xFF00) continue; // ignore if not touching a PV leaf for (i=0 ; i < ent->num_leafs ; i++) if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) break; // ericw -- added ent->num_leafs < MAX_ENT_LEAFS condition. // // if ent->num_leafs == MAX_ENT_LEAFS, the ent is visible from too many leafs // for us to say whether it's in the PVS, so don't try to vis cull it. // this commonly happens with rotators, because they often have huge bboxes // spanning the entire map, or really tall lifts, etc. if (i == ent->num_leafs && ent->num_leafs < MAX_ENT_LEAFS) continue; // not visible } //johnfitz -- max size for protocol 15 is 18 bytes, not 16 as originally //assumed here. And, for protocol 85 the max size is actually 24 bytes. if (msg->cursize + 24 > msg->maxsize) { //johnfitz -- less spammy overflow message if (!dev_overflows.packetsize || dev_overflows.packetsize + CONSOLE_RESPAM_TIME < realtime ) { Con_Printf ("Packet overflow!\n"); dev_overflows.packetsize = realtime; } goto stats; //johnfitz } // send an update bits = 0; for (i=0 ; i<3 ; i++) { miss = ent->v.origin[i] - ent->baseline.origin[i]; if ( miss < -0.1 || miss > 0.1 ) bits |= U_ORIGIN1<<i; } if ( ent->v.angles[0] != ent->baseline.angles[0] ) bits |= U_ANGLE1; if ( ent->v.angles[1] != ent->baseline.angles[1] ) bits |= U_ANGLE2; if ( ent->v.angles[2] != ent->baseline.angles[2] ) bits |= U_ANGLE3; if (ent->v.movetype == MOVETYPE_STEP) bits |= U_STEP; // don't mess up the step animation if (ent->baseline.colormap != ent->v.colormap) bits |= U_COLORMAP; if (ent->baseline.skin != ent->v.skin) bits |= U_SKIN; if (ent->baseline.frame != ent->v.frame) bits |= U_FRAME; if (ent->baseline.effects != ent->v.effects) bits |= U_EFFECTS; if (ent->baseline.modelindex != ent->v.modelindex) bits |= U_MODEL; //johnfitz -- alpha if (pr_alpha_supported) { // TODO: find a cleaner place to put this code eval_t *val; val = GetEdictFieldValue(ent, "alpha"); if (val) ent->alpha = ENTALPHA_ENCODE(val->_float); } //don't send invisible entities unless they have effects if (ent->alpha == ENTALPHA_ZERO && !ent->v.effects) continue; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE if (sv.protocol != PROTOCOL_NETQUAKE) { if (ent->baseline.alpha != ent->alpha) bits |= U_ALPHA; if (bits & U_FRAME && (int)ent->v.frame & 0xFF00) bits |= U_FRAME2; if (bits & U_MODEL && (int)ent->v.modelindex & 0xFF00) bits |= U_MODEL2; if (ent->sendinterval) bits |= U_LERPFINISH; if (bits >= 65536) bits |= U_EXTEND1; if (bits >= 16777216) bits |= U_EXTEND2; } //johnfitz if (e >= 256) bits |= U_LONGENTITY; if (bits >= 256) bits |= U_MOREBITS; // // write the message // MSG_WriteByte (msg, bits | U_SIGNAL); if (bits & U_MOREBITS) MSG_WriteByte (msg, bits>>8); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & U_EXTEND1) MSG_WriteByte(msg, bits>>16); if (bits & U_EXTEND2) MSG_WriteByte(msg, bits>>24); //johnfitz if (bits & U_LONGENTITY) MSG_WriteShort (msg,e); else MSG_WriteByte (msg,e); if (bits & U_MODEL) MSG_WriteByte (msg, ent->v.modelindex); if (bits & U_FRAME) MSG_WriteByte (msg, ent->v.frame); if (bits & U_COLORMAP) MSG_WriteByte (msg, ent->v.colormap); if (bits & U_SKIN) MSG_WriteByte (msg, ent->v.skin); if (bits & U_EFFECTS) MSG_WriteByte (msg, ent->v.effects); if (bits & U_ORIGIN1) MSG_WriteCoord (msg, ent->v.origin[0], sv.protocolflags); if (bits & U_ANGLE1) MSG_WriteAngle(msg, ent->v.angles[0], sv.protocolflags); if (bits & U_ORIGIN2) MSG_WriteCoord (msg, ent->v.origin[1], sv.protocolflags); if (bits & U_ANGLE2) MSG_WriteAngle(msg, ent->v.angles[1], sv.protocolflags); if (bits & U_ORIGIN3) MSG_WriteCoord (msg, ent->v.origin[2], sv.protocolflags); if (bits & U_ANGLE3) MSG_WriteAngle(msg, ent->v.angles[2], sv.protocolflags); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & U_ALPHA) MSG_WriteByte(msg, ent->alpha); if (bits & U_FRAME2) MSG_WriteByte(msg, (int)ent->v.frame >> 8); if (bits & U_MODEL2) MSG_WriteByte(msg, (int)ent->v.modelindex >> 8); if (bits & U_LERPFINISH) MSG_WriteByte(msg, (byte)(Q_rint((ent->v.nextthink-sv.time)*255))); //johnfitz } //johnfitz -- devstats stats: if (msg->cursize > 1024 && dev_peakstats.packetsize <= 1024) Con_DWarning ("%i byte packet exceeds standard limit of 1024 (max = %d).\n", msg->cursize, msg->maxsize); dev_stats.packetsize = msg->cursize; dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize); //johnfitz } /* ============= SV_CleanupEnts ============= */ void SV_CleanupEnts (void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) { ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) { int bits; int i; edict_t *other; int items; eval_t *val; // // send a damage message // if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); MSG_WriteByte (msg, svc_damage); MSG_WriteByte (msg, ent->v.dmg_save); MSG_WriteByte (msg, ent->v.dmg_take); for (i=0 ; i<3 ; i++) MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]), sv.protocolflags ); ent->v.dmg_take = 0; ent->v.dmg_save = 0; } // // send the current viewpos offset from the view entity // SV_SetIdealPitch (); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. if ( ent->v.fixangle ) { MSG_WriteByte (msg, svc_setangle); for (i=0 ; i < 3 ; i++) MSG_WriteAngle (msg, ent->v.angles[i], sv.protocolflags ); ent->v.fixangle = 0; } bits = 0; if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT; if (ent->v.idealpitch) bits |= SU_IDEALPITCH; // stuff the sigil bits into the high bits of items for sbar, or else // mix in items2 val = GetEdictFieldValue(ent, "items2"); if (val) items = (int)ent->v.items | ((int)val->_float << 23); else items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); bits |= SU_ITEMS; if ( (int)ent->v.flags & FL_ONGROUND) bits |= SU_ONGROUND; if ( ent->v.waterlevel >= 2) bits |= SU_INWATER; for (i=0 ; i<3 ; i++) { if (ent->v.punchangle[i]) bits |= (SU_PUNCH1<<i); if (ent->v.velocity[i]) bits |= (SU_VELOCITY1<<i); } if (ent->v.weaponframe) bits |= SU_WEAPONFRAME; if (ent->v.armorvalue) bits |= SU_ARMOR; // if (ent->v.weapon) bits |= SU_WEAPON; //johnfitz -- PROTOCOL_FITZQUAKE if (sv.protocol != PROTOCOL_NETQUAKE) { if (bits & SU_WEAPON && SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) & 0xFF00) bits |= SU_WEAPON2; if ((int)ent->v.armorvalue & 0xFF00) bits |= SU_ARMOR2; if ((int)ent->v.currentammo & 0xFF00) bits |= SU_AMMO2; if ((int)ent->v.ammo_shells & 0xFF00) bits |= SU_SHELLS2; if ((int)ent->v.ammo_nails & 0xFF00) bits |= SU_NAILS2; if ((int)ent->v.ammo_rockets & 0xFF00) bits |= SU_ROCKETS2; if ((int)ent->v.ammo_cells & 0xFF00) bits |= SU_CELLS2; if (bits & SU_WEAPONFRAME && (int)ent->v.weaponframe & 0xFF00) bits |= SU_WEAPONFRAME2; if (bits & SU_WEAPON && ent->alpha != ENTALPHA_DEFAULT) bits |= SU_WEAPONALPHA; //for now, weaponalpha = client entity alpha if (bits >= 65536) bits |= SU_EXTEND1; if (bits >= 16777216) bits |= SU_EXTEND2; } //johnfitz // send the data MSG_WriteByte (msg, svc_clientdata); MSG_WriteShort (msg, bits); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_EXTEND1) MSG_WriteByte(msg, bits>>16); if (bits & SU_EXTEND2) MSG_WriteByte(msg, bits>>24); //johnfitz if (bits & SU_VIEWHEIGHT) MSG_WriteChar (msg, ent->v.view_ofs[2]); if (bits & SU_IDEALPITCH) MSG_WriteChar (msg, ent->v.idealpitch); for (i=0 ; i<3 ; i++) { if (bits & (SU_PUNCH1<<i)) MSG_WriteChar (msg, ent->v.punchangle[i]); if (bits & (SU_VELOCITY1<<i)) MSG_WriteChar (msg, ent->v.velocity[i]/16); } // [always sent] if (bits & SU_ITEMS) MSG_WriteLong (msg, items); if (bits & SU_WEAPONFRAME) MSG_WriteByte (msg, ent->v.weaponframe); if (bits & SU_ARMOR) MSG_WriteByte (msg, ent->v.armorvalue); if (bits & SU_WEAPON) MSG_WriteByte (msg, SV_ModelIndex(PR_GetString(ent->v.weaponmodel))); MSG_WriteShort (msg, ent->v.health); MSG_WriteByte (msg, ent->v.currentammo); MSG_WriteByte (msg, ent->v.ammo_shells); MSG_WriteByte (msg, ent->v.ammo_nails); MSG_WriteByte (msg, ent->v.ammo_rockets); MSG_WriteByte (msg, ent->v.ammo_cells); if (standard_quake) { MSG_WriteByte (msg, ent->v.weapon); } else { for(i=0;i<32;i++) { if ( ((int)ent->v.weapon) & (1<<i) ) { MSG_WriteByte (msg, i); break; } } } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_WEAPON2) MSG_WriteByte (msg, SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) >> 8); if (bits & SU_ARMOR2) MSG_WriteByte (msg, (int)ent->v.armorvalue >> 8); if (bits & SU_AMMO2) MSG_WriteByte (msg, (int)ent->v.currentammo >> 8); if (bits & SU_SHELLS2) MSG_WriteByte (msg, (int)ent->v.ammo_shells >> 8); if (bits & SU_NAILS2) MSG_WriteByte (msg, (int)ent->v.ammo_nails >> 8); if (bits & SU_ROCKETS2) MSG_WriteByte (msg, (int)ent->v.ammo_rockets >> 8); if (bits & SU_CELLS2) MSG_WriteByte (msg, (int)ent->v.ammo_cells >> 8); if (bits & SU_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v.weaponframe >> 8); if (bits & SU_WEAPONALPHA) MSG_WriteByte (msg, ent->alpha); //for now, weaponalpha = client entity alpha //johnfitz } /* ======================= SV_SendClientDatagram ======================= */ qboolean SV_SendClientDatagram (client_t *client) { byte buf[MAX_DATAGRAM]; sizebuf_t msg; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; //johnfitz -- if client is nonlocal, use smaller max size so packets aren't fragmented if (Q_strcmp(NET_QSocketGetAddressString(client->netconnection), "LOCAL") != 0) msg.maxsize = DATAGRAM_MTU; //johnfitz MSG_WriteByte (&msg, svc_time); MSG_WriteFloat (&msg, sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage (client->edict, &msg); SV_WriteEntitiesToClient (client->edict, &msg); // copy the server datagram if there is space if (msg.cursize + sv.datagram.cursize < msg.maxsize) SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); // send the datagram if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) { SV_DropClient (true);// if the message couldn't send, kick off return false; } return true; } /* ======================= SV_UpdateToReliableMessages ======================= */ void SV_UpdateToReliableMessages (void) { int i, j; client_t *client; // check for changes to be sent over the reliable streams for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (host_client->old_frags != host_client->edict->v.frags) { for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, i); MSG_WriteShort (&client->message, host_client->edict->v.frags); } host_client->old_frags = host_client->edict->v.frags; } } for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++) { if (!client->active) continue; SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); } SZ_Clear (&sv.reliable_datagram); } /* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ void SV_SendNop (client_t *client) { sizebuf_t msg; byte buf[4]; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; MSG_WriteChar (&msg, svc_nop); if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) SV_DropClient (true); // if the message couldn't send, kick off client->last_message = realtime; } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages (void) { int i; // update frags, names, etc SV_UpdateToReliableMessages (); // build individual updates for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (!host_client->active) continue; if (host_client->spawned) { if (!SV_SendClientDatagram (host_client)) continue; } else { // the player isn't totally in the game yet // send small keepalive messages if too much time has passed // send a full message when the next signon stage has been requested // some other message data (name changes, etc) may accumulate // between signon stages if (!host_client->sendsignon) { if (realtime - host_client->last_message > 5) SV_SendNop (host_client); continue; // don't send out non-signon messages } } // check for an overflowed message. Should only happen // on a very fucked up connection that backs up a lot, then // changes level if (host_client->message.overflowed) { SV_DropClient (true); host_client->message.overflowed = false; continue; } if (host_client->message.cursize || host_client->dropasap) { if (!NET_CanSendMessage (host_client->netconnection)) { // I_Printf ("can't write\n"); continue; } if (host_client->dropasap) SV_DropClient (false); // went to another level else { if (NET_SendMessage (host_client->netconnection , &host_client->message) == -1) SV_DropClient (true); // if the message couldn't send, kick off SZ_Clear (&host_client->message); host_client->last_message = realtime; host_client->sendsignon = false; } } } // clear muzzle flashes SV_CleanupEnts (); } /* ============================================================================== SERVER SPAWNING ============================================================================== */ /* ================ SV_ModelIndex ================ */ int SV_ModelIndex (const char *name) { int i; if (!name || !name[0]) return 0; for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++) if (!strcmp(sv.model_precache[i], name)) return i; if (i==MAX_MODELS || !sv.model_precache[i]) Sys_Error ("SV_ModelIndex: model %s not precached", name); return i; } /* ================ SV_CreateBaseline ================ */ void SV_CreateBaseline (void) { int i; edict_t *svent; int entnum; int bits; //johnfitz -- PROTOCOL_FITZQUAKE for (entnum = 0; entnum < sv.num_edicts ; entnum++) { // get the current server version svent = EDICT_NUM(entnum); if (svent->free) continue; if (entnum > svs.maxclients && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skin = svent->v.skin; if (entnum > 0 && entnum <= svs.maxclients) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); svent->baseline.alpha = ENTALPHA_DEFAULT; //johnfitz -- alpha support } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); svent->baseline.alpha = svent->alpha; //johnfitz -- alpha support } //johnfitz -- PROTOCOL_FITZQUAKE bits = 0; if (sv.protocol == PROTOCOL_NETQUAKE) //still want to send baseline in PROTOCOL_NETQUAKE, so reset these values { if (svent->baseline.modelindex & 0xFF00) svent->baseline.modelindex = 0; if (svent->baseline.frame & 0xFF00) svent->baseline.frame = 0; svent->baseline.alpha = ENTALPHA_DEFAULT; } else //decide which extra data needs to be sent { if (svent->baseline.modelindex & 0xFF00) bits |= B_LARGEMODEL; if (svent->baseline.frame & 0xFF00) bits |= B_LARGEFRAME; if (svent->baseline.alpha != ENTALPHA_DEFAULT) bits |= B_ALPHA; } //johnfitz // // add to the message // //johnfitz -- PROTOCOL_FITZQUAKE if (bits) MSG_WriteByte (&sv.signon, svc_spawnbaseline2); else MSG_WriteByte (&sv.signon, svc_spawnbaseline); //johnfitz MSG_WriteShort (&sv.signon,entnum); //johnfitz -- PROTOCOL_FITZQUAKE if (bits) MSG_WriteByte (&sv.signon, bits); if (bits & B_LARGEMODEL) MSG_WriteShort (&sv.signon, svent->baseline.modelindex); else MSG_WriteByte (&sv.signon, svent->baseline.modelindex); if (bits & B_LARGEFRAME) MSG_WriteShort (&sv.signon, svent->baseline.frame); else MSG_WriteByte (&sv.signon, svent->baseline.frame); //johnfitz MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skin); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i], sv.protocolflags); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i], sv.protocolflags); } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & B_ALPHA) MSG_WriteByte (&sv.signon, svent->baseline.alpha); //johnfitz } } /* ================ SV_SendReconnect Tell all the clients that the server is changing levels ================ */ void SV_SendReconnect (void) { byte data[128]; sizebuf_t msg; msg.data = data; msg.cursize = 0; msg.maxsize = sizeof(data); MSG_WriteChar (&msg, svc_stufftext); MSG_WriteString (&msg, "reconnect\n"); NET_SendToAll (&msg, 5.0); if (!isDedicated) Cmd_ExecuteString ("reconnect\n", src_command); } /* ================ SV_SaveSpawnparms Grabs the current state of each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms (void) { int i, j; svs.serverflags = pr_global_struct->serverflags; for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (!host_client->active) continue; // call the progs to get default spawn parms for the new client pr_global_struct->self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (pr_global_struct->SetChangeParms); for (j=0 ; j<NUM_SPAWN_PARMS ; j++) host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j]; } } /* ================ SV_SpawnServer This is called at the start of each level ================ */ extern float scr_centertime_off; void SV_SpawnServer (const char *server) { static char dummy[8] = { 0,0,0,0,0,0,0,0 }; edict_t *ent; int i; // let's not have any servers with no name if (hostname.string[0] == 0) Cvar_Set ("hostname", "UNNAMED"); scr_centertime_off = 0; Con_DPrintf ("SpawnServer: %s\n",server); svs.changelevel_issued = false; // now safe to issue another // // tell all connected clients that we are going to a new level // if (sv.active) { SV_SendReconnect (); } // // make cvars consistant // if (coop.value) Cvar_Set ("deathmatch", "0"); current_skill = (int)(skill.value + 0.5); if (current_skill < 0) current_skill = 0; if (current_skill > 3) current_skill = 3; Cvar_SetValue ("skill", (float)current_skill); // // set up the new server // //memset (&sv, 0, sizeof(sv)); Host_ClearMemory (); q_strlcpy (sv.name, server, sizeof(sv.name)); sv.protocol = sv_protocol; // johnfitz if (sv.protocol == PROTOCOL_RMQ) { // set up the protocol flags used by this server // (note - these could be cvar-ised so that server admins could choose the protocol features used by their servers) sv.protocolflags = PRFL_INT32COORD | PRFL_SHORTANGLE; } else sv.protocolflags = 0; // load progs to get entity field count PR_LoadProgs (); // allocate server memory /* Host_ClearMemory() called above already cleared the whole sv structure */ sv.max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS); //johnfitz -- max_edicts cvar sv.edicts = (edict_t *) malloc (sv.max_edicts*pr_edict_size); // ericw -- sv.edicts switched to use malloc() sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.cursize = 0; sv.datagram.data = sv.datagram_buf; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.cursize = 0; sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.signon.maxsize = sizeof(sv.signon_buf); sv.signon.cursize = 0; sv.signon.data = sv.signon_buf; // leave slots at start for clients only sv.num_edicts = svs.maxclients+1; memset(sv.edicts, 0, sv.num_edicts*pr_edict_size); // ericw -- sv.edicts switched to use malloc() for (i=0 ; i<svs.maxclients ; i++) { ent = EDICT_NUM(i+1); svs.clients[i].edict = ent; } sv.state = ss_loading; sv.paused = false; sv.time = 1.0; q_strlcpy (sv.name, server, sizeof(sv.name)); q_snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", server); sv.worldmodel = Mod_ForName (sv.modelname, false); if (!sv.worldmodel) { Con_Printf ("Couldn't spawn server %s\n", sv.modelname); sv.active = false; return; } sv.models[1] = sv.worldmodel; // // clear world interaction links // SV_ClearWorld (); sv.sound_precache[0] = dummy; sv.model_precache[0] = dummy; sv.model_precache[1] = sv.modelname; for (i=1 ; i<sv.worldmodel->numsubmodels ; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = Mod_ForName (localmodels[i], false); } // // load the rest of the entities // ent = EDICT_NUM(0); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ent->v.model = PR_SetEngineString(sv.worldmodel->name); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; if (coop.value) pr_global_struct->coop = coop.value; else pr_global_struct->deathmatch = deathmatch.value; pr_global_struct->mapname = PR_SetEngineString(sv.name); // serverflags are for cross level information (sigils) pr_global_struct->serverflags = svs.serverflags; ED_LoadFromFile (sv.worldmodel->entities); sv.active = true; // all setup is completed, any further precache statements are errors sv.state = ss_active; // run two frames to allow everything to settle host_frametime = 0.1; SV_Physics (); SV_Physics (); // create a baseline for more efficient communications SV_CreateBaseline (); //johnfitz -- warn if signon buffer larger than standard server can handle if (sv.signon.cursize > 8000-2) //max size that will fit into 8000-sized client->message buffer with 2 extra bytes on the end Con_DWarning ("%i byte signon buffer exceeds standard limit of 7998 (max = %d).\n", sv.signon.cursize, sv.signon.maxsize); //johnfitz // send serverinfo to all connected clients for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) if (host_client->active) SV_SendServerinfo (host_client); Con_DPrintf ("Server spawned.\n"); } �����������������������������������������������quakespasm-0.93.0/Quake/build_cross_osx.sh����������������������������������������������������������0000755�0000000�0000000�00000002641�12021217500�017252� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh rm -f quakespasm.ppc \ quakespasm.x86 \ quakespasm.x86_64 \ QuakeSpasm make clean OLDPATH=$PATH MAKE_CMD=make OSXBUILD=1 export OSXBUILD STRIP=/bin/true export STRIP # ppc PATH=/opt/cross_osx-ppc/bin:$OLDPATH CC=powerpc-apple-darwin9-gcc AS=powerpc-apple-darwin9-as AR=powerpc-apple-darwin9-ar RANLIB=powerpc-apple-darwin9-ranlib LIPO=powerpc-apple-darwin9-lipo export PATH CC AS AR RANLIB LIPO $MAKE_CMD MACH_TYPE=ppc -f Makefile.darwin $* || exit 1 powerpc-apple-darwin9-strip -S quakespasm || exit 1 mv quakespasm quakespasm.ppc || exit 1 $MAKE_CMD clean # x86 PATH=/opt/cross_osx-x86/bin:$OLDPATH CC=i686-apple-darwin9-gcc AS=i686-apple-darwin9-as AR=i686-apple-darwin9-ar RANLIB=i686-apple-darwin9-ranlib LIPO=i686-apple-darwin9-lipo export PATH CC AS AR RANLIB LIPO $MAKE_CMD MACH_TYPE=x86 -f Makefile.darwin $* || exit 1 i686-apple-darwin9-strip -S quakespasm || exit 1 mv quakespasm quakespasm.x86 || exit 1 $MAKE_CMD clean # x86_64 PATH=/opt/cross_osx-x86_64/usr/bin:$OLDPATH CC=x86_64-apple-darwin9-gcc AS=x86_64-apple-darwin9-as AR=x86_64-apple-darwin9-ar RANLIB=x86_64-apple-darwin9-ranlib LIPO=x86_64-apple-darwin9-lipo export PATH CC AS AR RANLIB LIPO $MAKE_CMD MACH_TYPE=x86_64 -f Makefile.darwin $* || exit 1 x86_64-apple-darwin9-strip -S quakespasm || exit 1 mv quakespasm quakespasm.x86_64 || exit 1 $MAKE_CMD clean $LIPO -create -o QuakeSpasm quakespasm.ppc quakespasm.x86 quakespasm.x86_64 || exit 1 �����������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/quakespasm.pak��������������������������������������������������������������0000644�0000000�0000000�00002102770�12665410352�016405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PACKøƒ����€�����®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­¬¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬«««¬«¬¬¬­¬¬¬¬¬­¬­¬¬­­¬¬­¬¬¬¬¬­¬¬­¬­¬­®®®®®®®®®¿111¿11¿¿1¿¿1¿¿¿1¿¿¿®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬­¬¬­¬¬­­­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬«­«««««¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬®®®®®®®®®®¿11¿1111111111¿¿¿1®®®®®®®®®­¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬«¬««««¬¬¬¬¬¬­­¬¬¬¬­­¬¬¬¬¬¬­¬¬¬¬­­®®®®®®®®®¿¿¿¿¿¿1¿1111¿¿¿¿1¿¿¿¿®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬­­­¬­­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬­­­¬¬¬¬¬¬­®®®®®®®®¿¿¿¿¿¿¿11111¿¿1¿1¿1®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­¬¬­¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬«¬¬¬¬««­­¬­­­¬¬­­­¬¬¬­¬¬­¬­®®®®®¿¿¿11¿¿11111111¿1111111111®®®®®®®®®®®¬­®¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­­¬­¬¬¬­­­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬­«¬¬¬¬¬¬­¬­¬­­¬¬¬­­¬¬¬¬¬­®®®®®®®®¿1111¿¿¿111111¿11111111¿®®®®®®®®®®®®®¬­­®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬­¬¬­­¬¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬«­««¬¬¬«¬¬¬¬¬¬­¬­­¬­­¬¬­¬¬¬¬¬¬¬®®®®®¿1111¿¿¿¿1¿1111¿1¿1¿1¿¿¿¿®®®®®®®®®®¬¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬««««¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬¬®®®®®¿11¿1¿¿¿1¿¿11¿1¿¿¿¿®®®®®®®®®®¬¬®®¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­­¬¬¬¬¬¬­­¬­­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬«««¬¬¬««««««¬«¬¬¬¬¬­¬¬¬¬¬¬­­­¬¬¬­¬¬¬­®®®®®¿¿111¿¿¿1¿¿¿11¿¿¿¿®®®®®®®­®®®®¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬­¬¬««««¬¬¬¬¬¬­­¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬®®¿11¿¿¿1¿¿1¿¿¿1®®®®®­­¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬­¬­¬­¬­¬¬­­¬­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬¬¬«¬­¬­¬¬­¬­¬¬­¬­¬­¬­¬¬¬­­­­®®®®®1¿¿¿¿¿11¿¿¿¿¿¿¿1®®®®®®®®®­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬¬«¬¬¬¬¬¬­¬­¬­¬¬¬­­­­­¬¬¬¬¬­¬¬­¬­®­®®®¿¿¿¿1111111¿®®®®®®®®®®®®­­®­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­¬¬¬­­¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬¬­­¬­­®®¿¿111111¿¿¿¿¿®®®®®®®®®®®¬­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬«¬¬«¬«««¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬®®®®®¿1¿1¿¿¿¿¿®®®®®®®®®®®®®­®­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬«¬¬¬«««««¬¬¬­¬¬­¬¬¬¬¬¬¬¬­­­­­­¬¬­®®®®®11¿¿¿1¿®®®®®®®®®®®®®­®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬¬­¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«««¬««««¬¬­­¬¬¬­­¬¬­¬¬¬¬¬­¬­­¬¬®®®®®®®1¿®¿¿¿¿®®®®®®®®®®®®®®®®®®­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬­«««««¬««««««««««««««««­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­®®®®¿1¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­¬¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­®­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««¬«««««¬¬­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬­­­¬­¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««­¬­¬­­¬¬­¬¬­¬¬­­¬­­¬¬­¬­¬­¬®®¿®¿¿¿®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­­¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬­¬¬¬­¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬«««¬«««««««««««««««««««¬«­¬­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬­¬¬­¬¬®®®1¿®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬¬¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®¬­­­¬¬¬­¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬­«¬¬«««««««««¬««««««¬«¬¬­¬¬­­®¬­­¬¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬­¬­­­­®®¿¿¿®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬¬¬¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬¬«««««««««««¬«««««««««««««­¬­¬¬¬¬¬­¬¬­­­¬¬­­¬­¬¬¬¬¬­¬­­¬¬¬­­®®®¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®¬­­­­­­­¬¬¬¬¬¬¬­¬¬¬­¬¬«««¬¬¬¬¬¬¬¬««««««««««««««««««««««««¬¬¬­¬­­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­­­®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬«««««««««««««««««««««¬¬¬¬¬¬­¬­¬­­¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬­¬­®®¿1¿1¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­­­¬¬¬¬­¬¬¬¬­¬¬«¬¬­¬¬¬­¬­¬¬¬¬¬¬¬«««««««««««««««««««¬¬¬­¬­­¬¬¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬­­¬¬­­®®®®®¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­­­¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬¬¬¬¬««««««««««««««««««««««««¬¬¬¬­­­¬­¬­¬¬­¬­¬­¬¬­­­¬¬¬¬¬­¬¬¬­®®®®¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««­««««««««««««««««¬«««««««¬¬­¬¬­¬­¬¬®¬¬­¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬®®®¿¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­­­¬­¬¬¬¬¬­¬¬¬¬­­¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««¬¬¬­¬¬¬¬­¬­­¬¬¬¬­¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬­®®¿11®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬¬¬­¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬««««¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬­¬¬¬­¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬­®¿®¿¿®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬«««««««««««««««««««««««¬¬¬«¬¬¬­¬­­­­¬¬¬­¬¬¬¬¬­¬¬¬¬­­®¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­¬¬¬­­­¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­««¬¬¬¬«¬«««««««««««««««««««««««¬¬­­¬­®¬¬¬¬­­¬¬¬¬¬­­¬¬­­¬¬®®®¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­¬¬­¬¬­­­¬¬¬­­¬¬¬¬­¬­¬¬««¬¬¬¬¬««¬«««««««««««««««¬««««¬¬¬­­¬®®®¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­¬¬¬¬¬¬­®®®¿¿¿1¿®1®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬­¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬¬­¬­­¬¬­­­¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬«¬¬«¬¬¬¬«««¬¬«¬«¬¬¬¬­¬¬­­®®®­­¬­¬¬­¬­­¬­¬¬¬­­­¬­­­¬­­®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬­¬¬­¬¬¬¬­¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­¬¬¬­¬¬¬­¬¬¬¬¬­­¬­¬¬¬¬¬««««¬«¬«¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­®­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬­¬­­­¬­®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­¬­­¬­­­­¬­­¬¬¬¬­¬¬­¬¬¬­¬¬«¬«¬«¬¬«¬«¬¬¬¬¬¬«¬«¬¬¬¬¬¬­¬¬­­­­¬­¬¬¬¬­¬¬¬¬¬­¬¬­­­­®®®®®®®¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­¬¬­¬­­­­­­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬««¬¬¬¬«««¬««««¬«¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬­¬­­®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­¬¬¬­¬­­¬¬¬¬¬­¬¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­¬­­¬­­­¬¬­¬¬­¬¬¬­­¬­¬¬«««««««««««««¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­­¬­­¬¬¬¬¬¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬­®¬¬¬­­¬¬­¬¬¬¬­¬¬¬««««««««««¬¬¬¬¬¬¬¬­¬¬««­¬¬­¬­­¬¬­¬¬­­­¬¬¬­­¬¬­¬¬­­¬¬­¬¬®®®®®®®®®®®®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­­­­­­­­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬«¬«««««¬¬¬¬««¬«¬¬­­¬¬¬«¬­¬­¬¬¬¬­­¬¬¬¬­­¬¬¬¬­¬¬­¬¬­¬¬­®­®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­¬¬­¬­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬¬«««¬¬¬¬««¬¬¬«¬¬¬¬­¬­¬­­­­¬¬¬¬¬¬­­¬­¬­¬­¬¬­®¬­¬®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬­¬¬¬¬­­­¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­®­­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬««««««¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬­¬¬¬¬­­¬¬­­¬¬¬¬¬­­­®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬­­¬­¬­­¬¬¬¬­¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬­­®­­¬¬¬­¬¬¬¬¬­¬¬¬¬­¬«««¬­¬¬¬«¬¬«¬¬¬¬«¬¬¬«¬¬¬¬­¬­­¬¬¬¬¬­­¬­¬­¬¬®­­¬¬­®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬­¬¬­¬¬¬­­¬¬¬­­­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬®®¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬««¬«¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬¬¬¬¬­­¬¬­¬­¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬­¬¬¬¬¬­¬­¬¬¬­¬­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­®­¬¬¬¬¬¬­­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬«¬­¬¬­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­­­­­¬­®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬­­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬­­­¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬«¬««¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­­­­¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­­­­­­­­­­¬¬¬¬­¬¬­­¬­¬¬­¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬«¬¬¬­­¬¬­­¬­¬¬­¬­­¬­­¬­¬¬¬¬¬¬¬¬­®­®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬­­­¬¬¬­¬­¬­­¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­­­­¬­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬««««¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬¬­¬¬¬­¬­¬¬­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬­­­­­®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­­­­­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬«¬««¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬­­­­¬¬­¬¬¬¬­¬­¬¬­®¬¬­­­­®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬­¬®®­®­¬­¬¬­­­¬¬­­¬­¬­­¬­¬­­­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬­­­¬¬¬¬¬­¬¬­­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­­­­­¬¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬««¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬­­­¬¬­¬­¬¬¬¬¬¬¬­­¬­¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬­¬­¬¬¬­¬¬¬­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«««¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬«¬¬¬¬¬¬¬­¬®­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­¬¬®­¬­­®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬­¬¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­¬­¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬«¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬®®¬¬­¬¬®­¬¬­¬¬¬¬¬¬¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­¬®­­­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬®­­®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®¬¬¬¬­¬¬­¬­¬­¬­­­¬¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬­¬¬­¬¬¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­®­­®­¬®­­¬¬­¬¬¬¬­­¬­¬­®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®¬¬¬­¬¬­­¬¬¬¬¬¬¬­¬¬­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­­®®®­¬¬¬­¬¬¬¬­­¬¬­®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­¬­¬­¬¬­­¬¬¬¬­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­­¬¬­­­­­­­­­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­­¬­®¬¬­¬¬¬­¬¬­­­­­­¬¬®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬­¬¬­¬­¬¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­®¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬­­­­®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­­­¬­¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­¬«­¬¬¬¬­­­¬­­¬­­¬¬¬­­­¬­¬¬­¬®®­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬­­¬­¬­¬­­­­¬¬­¬­®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬«¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬«¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬¬­­­­¬¬­¬¬®­¬¬¬¬­­¬®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬­¬¬­¬­­­¬¬­­¬®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬«¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬¬¬­­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­­¬¬¬­­¬­­¬­­¬¬¬®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®­­­®¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬«¬«¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬­¬¬¬­¬­­­­­­¬­¬­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬­¬¬¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­®¬¬­­­­­¬¬¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬®­¬¬­¬­¬¬¬¬¬­¬­­­¬¬­­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬­¬¬¬­­¬¬¬­¬­­¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­­­­­¬­­¬­­¬¬­¬­¬¬¬¬­­­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­­­­¬­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬«««««¬¬¬¬«¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬­­¬¬¬¬¬®­­¬­¬­¬­¬­¬¬­¬­®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬­¬¬¬¬¬¬­­¬¬­­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬­¬¬¬­¬¬­¬¬­¬¬­­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬­­¬¬­­¬­®®®®®®­­®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬­¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬­¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­­¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­¬­¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬­­­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­­­­¬¬­¬®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬­¬­¬­¬¬­¬­­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­­®¬­­­­­­®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­­¬­¬¬­­­­¬¬­­¬¬¬¬­¬¬­¬¬¬­¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬­¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­­¬¬­¬­­¬¬­­­¬¬­¬­¬¬¬¬¬­­­¬¬¬¬­¬­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­®®­®®®­®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬­­¬¬­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­­­¬¬­¬¬¬¬¬¬¬­¬¬­­­­­¬­­­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬­¬®®¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®­®®®¬­¬­¬­¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­¬­¬¬­¬¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­­¬¬¬¬­­¬¬­¬¬­¬¬¬¬¬¬­­¬¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬¬¬­­¬¬­¬¬­­¬¬¬¬¬­­¬¬­¬­¬®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬®®®®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬¬­­¬­¬¬¬¬¬­­­­®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­­¬­¬¬¬¬¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬­¬­­­­¬¬¬¬­¬­¬®­­­­®®­®®®¬­®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬¬­­¬¬­¬¬¬­¬¬¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­®­­­­®®®®¬®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­¬¬­¬¬­¬¬­¬­¬­¬­¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬­¬¬­¬­­¬¬¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬®­­­®®®®®®®®®®®®®®®®®®®11¿®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬­¬¬¬­¬­¬¬­¬­­¬®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬­­¬­­­­¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬­­¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬­­¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­­¬­­¬­­­­­¬¬¬®®®®®®®®®®®®®®®®®®®¿¿1®®®®®®®®®®®®®®®®®®­¬­­­¬¬¬¬¬¬¬­¬­¬¬¬¬®¬¬¬¬®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬¬­­¬¬­¬­¬¬­¬¬¬¬­¬¬¬­¬¬¬­­¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬­¬¬­­¬¬¬¬¬¬¬­­¬¬­¬¬¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬­¬­­­¬®¬¬­­¬¬­®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­¬¬¬¬¬¬¬­¬¬­­¬¬¬¬¬¬­¬­¬­­¬¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®¬­¬­¬¬¬¬®­®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬®¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®¬¬¬¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬­¬­­¬­¬­¬¬¬¬¬¬­­¬­¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬®­®®®®®®®®®®®®®®®®®®®¿1®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­¬¬­¬¬¬­¬¬¬­¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬­­¬­¬¬­­¬¬­­¬¬­¬¬¬­¬­¬¬¬­­¬­®­®®®¬®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®¬®­¬¬¬¬¬­­¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬¬­¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬¬­¬­­¬­¬¬­¬¬¬¬­¬¬­­¬­¬¬¬­­¬¬­­¬¬¬­­­¬­¬¬¬¬¬¬­¬¬¬¬¬®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­­­¬­¬¬­¬­­®®­­®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬­¬¬­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬­¬¬­¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬­­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬­­¬­¬¬­­­­®®­­¬¬®®­®®®®®®®®®®®®®®¿¿®®®®®®®®®­¬­¬¬¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®­­¬­¬¬¬¬­­¬¬­­¬¬¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬­­¬¬­­¬­­¬¬¬¬­­¬¬¬¬­¬¬¬¬­¬­¬®®­­®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®¬­¬¬¬¬­­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬¬­­¬¬­¬¬¬¬¬¬­­¬¬­¬­¬¬¬¬­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬­¬¬¬¬¬­¬¬¬¬¬¬®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬­¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬¬­­¬­¬¬¬¬­­¬¬­¬¬¬¬­­¬¬­¬­¬¬­­¬­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­­­¬®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬®¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­­­­­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬­¬¬¬­­¬­­¬¬­­­­­­®­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®®­®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­­¬­¬¬­­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬­­­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­­¬­¬­­¬­­¬¬­¬¬¬¬¬¬®­¬®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬®­®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬®®®¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬­­¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬­¬¬­¬­­­¬­­­¬®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­¬­¬¬¬­¬¬¬¬­­­¬¬¬¬¬­¬¬¬­¬­¬¬­¬­¬­¬¬¬¬­¬­¬¬¬¬­­¬¬­¬¬­¬¬¬­¬¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­¬­¬­¬¬¬­­­¬®­¬­¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­­¬¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­­­­¬­­¬¬­­¬¬­¬­¬¬­­¬¬¬¬­¬­­¬¬­­­®­¬­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬¬­­­¬­¬¬¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬¬¬­­¬¬­­­¬®­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬¬¬¬­¬­¬­¬­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®®®®®®­¬¬¬¬¬¬­¬¬­­¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬¬­¬­¬¬­­¬¬¬¬­­¬¬­¬¬¬¬­¬­¬­¬¬­¬­­­­¬®¬­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­­¬­¬¬­¬¬­¬­¬¬¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­­­­­¬­¬­­­¬¬¬®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®­­¬­­¬¬­¬¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬­¬¬­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬®¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­­­­¬­­¬­¬¬¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬­­­­¬¬¬­­¬­­­­­­®®®®®®®®®®®®®®®®¿®®®®®®®®®­­­¬¬­¬¬¬¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬®®®®®®11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­¬¬­­¬­¬¬­­­¬¬¬¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­¬¬¬¬¬­®¬¬®­­­­­¬­­­­­­¬¬­­­­­­®®®®®®®®®1®¿®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­­¬¬­¬¬­¬­®®®®®®®®®®1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬­¬­­­¬­­­­­®­­­­¬¬­¬¬­­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬¬­­­¬¬¬¬­¬­¬­­­­­­­­­¬­­­¬­¬­­­­®®®®®®®®®1®1®®®®®®®®®®®®®®¬¬¬¬­­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬®®®®®®1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬­­­­­­­­­­­¬¬¬¬­¬­­­¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­­¬¬¬­­­­¬­­­­¬­­­¬­­­®®®®®®®1¿¿®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬­¬¬¬¬­¬­­®®®®®®®11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬¬­¬¬­­­­­­­­­­­¬­¬­­¬­¬­­­¬¬¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­­¬­¬­®®­­­­­­®­­¬­­­­­¬­¬¬¬¬¬®®®®®®®®¿1¿¿®®®®®®®®®®®®®®®®®¬­­¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬­¬­¬¬­®®®®®1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬¬­­­­­­¬­­­­­­­¬­­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬¬­¬¬­­­¬­¬¬¬®­­¬­­®­­¬¬­¬¬­­®®®®®¿®®®®®®®®®®®®®®®®®¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬­­¬¬¬­¬­¬®®®®®®®®11¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬­¬¬­­­­¬¬¬¬­­­¬¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬­­­­­­¬®­­¬­­­­¬¬­­­®®®®®¿1¿®®®®®®®®®®®®¬¬¬­¬­¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬­®®®®1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­­­¬­¬¬¬­¬¬¬¬¬­¬¬¬­­¬¬­­¬­¬­¬¬­¬­­­¬¬­¬¬­­®¬¬­­­¬¬¬¬­®®®®®®®®¿¿®®®®®®®®®®®®­¬¬­¬¬¬¬­­­¬­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬®®®®1¿¿1¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬­­¬¬®­¬­­¬¬­¬­®­¬¬­­¬¬¬¬¬¬­¬¬¬­¬­¬­­­­¬­¬­­­­­­®®®­¬­­¬­­­­®®®®®®®®®®¿®®®®®®®®®®®®®®®®®¬¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬®®®®®®111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬¬¬­­¬¬­®­¬¬­¬­­¬¬¬¬¬¬­­¬¬¬­¬¬¬¬¬¬­­¬¬­¬¬­®­­­®­¬­¬­­¬­­¬­­­­®­®®®®¿¿1¿®®®®®®®®®®®®®®®®®¬¬¬­¬­¬¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬®®®®®®®®11111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬­­¬­­¬¬¬­®­¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬­­­®¬­­®­­¬¬­¬­­­¬¬¬­­®®®®®1¿¿1¿¿®®®®®®®®®®®®®®¬­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­®®®®®1111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­®®®¬¬­¬­¬­¬¬¬­­¬¬­¬­¬¬¬­¬¬­¬­­¬¬­­­®­®®®­­®­­­­­¬¬¬¬¬­¬¬¬¬¬­®®®®¿11111®®®®®®®®®®®®®®®­¬­­¬­¬¬¬¬­¬¬¬­­­¬¬¬¬¬¬­­¬¬¬¬¬­¬¬®®®®®®®¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬­­¬­¬­¬¬­¬­¬¬­¬¬¬­­¬¬­¬¬¬¬¬­¬¬¬¬­­­­¬­­¬­­­­¬¬¬¬­­­¬¬­­¬¬¬­­¬¬­­®®®®®®®®11¿¿®®®®®®®®®®­¬¬­¬­¬¬¬¬¬­¬®¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬®®®®11¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­­­­¬­®®®­¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­­­¬­¬¬­®¬­¬­¬¬­­¬¬¬¬¬¬¬­­¬®®®¿1¿¿®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬®®1111¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®®­­­­®¬¬®¬¬¬­¬¬­¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­­­®­¬­­­­¬¬­­¬¬¬¬¬­­­­®®®®®®¿¿¿¿®®®®®®®®®®®®¬¬¬­­¬­¬¬¬­¬¬­­¬­¬¬¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬®®®1¿11¿1¿¿1¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­­®­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬­¬­®®­­®®­­¬­¬¬­¬¬¬®¬¬¬¬¬¬®®®®®¿¿11¿¿¿®®®®®®®®®®®®®®­­­¬¬­­­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬®®11111¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬®­­¬­­¬­¬­¬­­­¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬¬¬¬­­¬­®®¬­®¬­­­¬¬­¬­¬¬­®¬­¬­­­®®®®®®®®®®¿¿¿¿¿11®®®®®®®®®®®®®®®®®®®­¬¬­¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬­­¬®®111¿1¿¿¿1¿¿¿¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­¬­­­¬­­­¬­­¬¬­­­¬­¬­¬¬­­®®®®®®®®®®®®®®­­¬¬¬¬­­¬­­®®®®®®®®®®®®®®®®¿¿¿11¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬¬­­¬­¬¬¬¬­¬­¬¬­¬­¬­¬¬­­¬¬¬¬­¬­¬¬®1¿1111¿1¿1111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®¬­¬­­¬®­¬­­­­­­­­­¬¬­¬­¬¬­¬¬¬­­®­®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­­­¬­­®®®®®®®®®®®®®®®®®®®1¿1¿¿1¿1¿1¿®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬111¿111¿1¿¿¿1¿1111¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬¬­­­­¬­¬¬¬¬¬¬¬­®®®®®®®®®®®®®®¬­­­­¬¬¬¬­¬­­¬­®®®®®®®®®®®®®®®®®®¿1¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬®­11¿1111¿11¿¿1¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­­¬¬­¬­¬­­­¬­®®®®®®®®®®®®®®®®®®¬®­¬¬¬­¬¬¬¬­¬­­­­®®®®®®®®®®®®®®1¿¿¿1¿¿¿11¿11111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­­¬®111111¿¿111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬­¬¬­¬¬­­¬­®®®®®®®®®®®®®®®®®®®®­¬¬­¬­¬­­®­­­­®®®®®®®®®®®®¿¿¿¿111¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬¬­¬¬¬¬¬­¬­¬­¬¬­¬¬¬¬­­¬­¬­¬¬­¬­¬­¬¬¬­­­®¿1111¿1¿¿¿¿1¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­¬¬¬¬¬¬¬­­®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­®®®®®®®®®®®®®®¿11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬­¬¬¬¬¬­­¬­¬­­¬¬­¬¬¬¬­¬¬­­­­¬­®¿111¿¿1¿11¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®¬¬¬­­¬¬­­®®®®®®®®®®®®®®®®®®®®®®®­­­­®®®®®®®®®®®®®111¿111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬®¿111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬¬­¬­­®­­®®®®®®®®®®®®®®®®®®®®®®®®®­­¬®®­®®®­®®®®®®®®¿1111¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­®®1¿1¿1¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­­¬­¬­¬¬¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­­¬­­­®®®®®¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­¬­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬­¬­­¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬­­­¬­­®®®¿1¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®¬­­­­­­¬­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬­®¿1¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬¬¬¬­­¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬­¬¬­­­®®®1¿¿1¿11¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­¬­¬­®®®­¿1¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬­¬­¬­­¬¬­¬­­­­¬¬®®¿1¿¿1¿¿¿¿11¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬­®®®¿¿¿1¿¿1111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬­¬­­¬¬¬¬­¬¬¬¬¬­¬¬¬­¬¬®®®¿¿®¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬­¬­¬­¬¬¬­­¬­¿¿¿1¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­®®­¬¬­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬­­¿¿¿¿®¿¿1¿¿11¿¿¿¿¿¿1¿¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬­¿¿1¿¿®¿1¿¿¿¿¿¿¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿1¿11¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬¬1¿¿¿¿1111¿¿¿¿¿¿¿¿¿¿1¿¿¿¿11111111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿1¿¿1¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬«¬¬­¬¬¬¬­­¬¬¬­¬¬¬­¬1¿¿¿¿1¿1¿¿111111¿¿1¿11111¿¿¿¿¿¿¿¿¿¿1111111111¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿1¿¿¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿¿¿1¿11¿111®®®®®®®®®®®®­®®®®®®®®®®®®®¬¬®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬­­¬¬¬¬¬­¬¬¬««¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¿¿1¿¿¿¿¿11¿1111111111111111111¿11¿1¿¿1¿¿1¿¿11111111111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿¿1¿¿1¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®¿1¿¿¿¿1¿1¿11®®®®®®®®®­­®®®®®®®®®®®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬¬¬¬¬¬­¬­««««¬¬¬¬¬¬¬¬¬¬¬¬¿¿1¿¿¿¿¿1¿¿11¿¿11111111¿¿¿1111111111¿11¿¿¿¿¿¿11111111111¿¿¿1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿¿1¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®¿11¿¿¿1®®®®®®®®­­­­­­¬­­®®®®®­®®®­¬¬­®®®®®®®®®®®®®®®®®®­¬¬¬««««¬«««««¬¬¬¬¬­¬­¬¿¿¿¿¿1¿1¿¿11¿¿111¿¿¿11111¿1¿¿¿¿¿111111111111¿¿¿11¿1111111111¿1¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®111¿1¿1®®®®­­¬¬¬­­¬¬­®®®®®®®­®®®®®­®¬®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®¬¬­¬««««««¬«««««««««««««««««¬¬¬­¬¬¬¬¬¿¿¿1¿¿11¿¿¿111¿¿1¿¿1¿¿¿11¿1¿11¿¿¿¿¿1¿111111¿1¿1¿¿11111111111111¿¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿111¿®­­­­­­­­¬®®®®­®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬««««««««¬««««««««««««««««««««¬¬¬­¬¬­­¿¿¿¿¿¿¿¿¿111¿¿¿11¿¿¿11¿¿111¿¿¿¿¿¿¿¿¿¿1¿¿1111¿¿111111111111¿¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­­¬¬­¬®®®®­®®®¬®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®­­¬¬­¬««««««««««««««««««««««««««¬¬¬­¬11¿1¿¿1111¿¿¿1¿¿¿11111111111111¿1111111111111¿¿1¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬­­­­­¬­­­¬­­­®­­­­¬­®®®®®®®­¬¬­­¬¬¬¬¬««««««««««««««««««««««¬¬¬¬¿111¿111111¿¿¿1¿¿¿1¿1111111111111111111111111¿11¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­¬¬¬­¬­­¬¬¬¬­¬¬­¬­­­­­­¬­®­­­­­¬­¬­¬­®­®®®®®®®®®®¬­­¬¬­¬¬­¬¬¬¬«««««««««¬«««««««««««¬««¬¬¬¬11¿¿1¿¿111¿111¿¿¿1¿11111111111111111111111111¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®¿®®®®®®®®®®®®®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬¬­¬­­­­­­­­¬¬¬¬¬­¬¬­­¬¬¬¬¬­¬®®®®®®®®®®®®®®®­­¬¬­¬¬­¬¬««««««««««««««««««««««««««««¬¬¬¬¿11111¿¿¿¿111¿1111111111111¿¿¿¿1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®­®®®®®®®¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬¬¬­¬¬¬­¬¬¬¬­¬­­¬¬­¬­¬¬­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬®®­®®®®®®®®®®®¬­­­­¬¬¬¬¬««««««««««««««««««««««¬¬¬­¬¬¬111¿¿¿¿¿¿¿¿¿11111111¿¿¿¿¿¿111®®®11¿®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­­¬¬¬¬¬­¬­­¬­¬¬¬¬¬¬­¬­¬­­®®®®®®®®®®®®®®®¬­­­­¬­¬¬­¬¬««««««««««««««««««««««««¬¬­­¬¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿1®®®®­­®®®®®®®­­®®®®®®®®®­­­­­¬¬¬¬¬¬­­¬¬­­¬­­¬­¬­¬¬­­­¬¬­­¬­­­¬¬¬­­¬­­¬­¬­¬¬­­®®®®®­­®®®®®®®®®®®¬­¬­­­¬¬¬¬­­¬­­¬¬­®®¬¬­¬¬­««¬«««¬««««««««««««¬¬¬­¬¬¬¬­¬­¬­­­­­¬¬¬¬­¬­¬¬¬­¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­®®®­®®­­­¬¬­­¬¬­¬¬¬­­­­­­¬­¬¬¬¬¬­¬¬­­¬­­­¬­¬­¬¬­¬¬­­¬­¬¬­­¬­­­­­­®®­®®®®®®®®®®­­­¬­¬­­¬­¬¬­¬­­­­­­­¬­®®®¬¬¬««««««««««««««««««««¬­­¬­­¬­­­®­¬¬­¬¬¬­­¬«««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬­¬¬¬­­­­­­¬­¬¬­¬­¬¬­¬­­¬¬­¬¬¬­­¬­¬¬­­¬¬­­­­®®­®®®®®®®®®­­¬­¬¬­¬¬­¬­­¬­¬¬¬¬­­­­­¬­¬¬­®­­®®®¬­®¬¬¬¬¬«««««««««««««««««««««««««­¬­­­­¬¬¬¬­¬¬¬¬­­­¬­¬¬¬­«¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬­®­­®­­­­¬­¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬­­¬¬¬­­¬­­­­¬¬¬¬¬­­¬­­­­­¬¬®®®®®®®®®®®®®­¬­­­­­¬­¬¬­¬­­¬­¬­­¬¬­­­­­­­¬¬­¬¬­¬«««««««««««««««««««««««¬­¬¬¬¬­­¬­¬¬¬­®­­¬­¬­­¬««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®­­¬¬­¬¬­­¬¬¬¬­­¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬­­¬­¬­­¬¬­¬¬¬­­­­¬¬¬®®®®®®®®®®®®®®®­­­­­­­¬¬­¬¬­¬­¬¬­¬¬­­¬¬­­¬¬¬­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬­¬¬¬­¬¬­¬­­¬®­¬­¬¬­­««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­­®¬­¬­¬¬­¬¬­¬¬¬­­­¬¬­¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬­¬­­¬¬¬­®®­®®®®®­¬­­­­­­¬¬­¬¬­­­¬­¬­¬­­­­¬¬­¬­¬¬¬¬­¬¬¬««««««««««««««««««««««¬¬¬¬¬­­¬­¬­¬­­¬¬®¬­­¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­®­­­­­­­­­­­­­­­®­¬¬¬¬­­­­¬¬­¬¬­¬¬­¬­­¬¬­­¬¬­¬­¬¬¬­¬¬­¬¬¬­­­­­­¬­¬¬­¬¬¬¬­¬¬­­¬­¬­¬¬®­­­­®®­®¬¬­­­­¬­­¬¬¬¬­­­¬­­­¬­¬­­¬¬­­­­­¬­¬¬­¬««««««««««««««¬¬¬¬¬¬­¬­­¬®¬¬¬¬¬­­¬¬­«¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­­®­­­­­­­­­­¬¬­¬¬­¬¬­­¬­¬­­¬­¬¬¬¬­­¬¬¬¬¬¬­­¬¬¬­¬¬¬­­¬­­¬­¬¬­¬­¬¬­­­­¬¬­­­­­­®­¬­­¬¬¬­­­­¬­¬¬­¬­¬¬­­­¬­¬­¬¬­¬­­­¬¬­­®®­­¬«««««««««««««««««««¬¬¬¬¬¬¬¬­­¬­­­®¬­­¬­­¬¬¬¬­¬¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬­­­­­­­­­­­­­­­­­­­¬­¬¬­¬­­­¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­­¬¬¬­­­­¬­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­­­­®®­¬­­¬¬¬­­¬­¬­­¬¬¬¬¬¬­­­¬­¬¬­­­¬­­¬­¬®­¬­«««««««««««««««««««««­¬¬¬¬¬­­¬¬¬­¬®®¬¬¬¬¬¬­­¬­¬«¬¬««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­¬­­­­­­­­­­­­¬­­¬­¬­­­­­®­­¬¬­¬­¬¬­¬¬¬­¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬­¬¬¬­¬­­­¬¬¬­­­­¬¬¬¬­¬­­­­®­­­¬¬¬¬¬¬­¬­­­®¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­­¬­¬¬¬­­­¬­­­««««««««««««««««««««««««¬¬¬¬¬­­¬¬­­¬¬®­¬­¬¬­­¬­«¬«¬«««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬­¬­­­­¬­­­­­­­­¬­­­­®®®­­­¬­¬­¬­­­¬¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬­­¬¬­¬¬­¬­¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬­­­­­­­­®®­­­­¬¬­¬­­­¬­¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬­¬¬­­¬­¬¬­­¬¬¬¬««««««««««««««««««¬­¬¬¬¬¬¬­¬¬­¬¬­¬­®¬­¬­¬¬¬¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­­­¬­­­­¬¬­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬­­­­­¬¬¬¬¬­­­­­¬­¬­¬­¬¬¬­¬¬­¬¬­­¬¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­­­­­­­®®­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­­­¬­¬¬¬¬­¬¬­­¬­­­­­««««««««««««««««­¬¬¬­­¬¬­­¬­¬¬­®¬¬¬¬­­¬¬¬¬¬«««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­­­­­­­­­­­­¬¬­­­­­¬­­¬­¬¬­¬¬¬¬¬­¬¬­­­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬¬­­¬­¬­­¬¬­¬¬¬­¬¬­¬­­­­­­­®®­­­­¬­¬­¬¬¬¬­¬­¬¬­¬­­¬­­­­¬¬­­¬­¬¬¬­¬­¬­­­­«««««««««««««««««««««¬­¬­¬¬¬­¬¬­¬¬­¬¬¬­­¬¬«««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­­­¬­­¬¬¬¬­­­­­¬­­­­­­¬¬­¬¬­¬¬­¬¬­­­­­¬­¬¬­­¬­¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬­¬¬¬¬¬­¬­¬­­­­­­­®­­­¬­­­¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­­¬­¬¬­¬¬­¬¬­­­¬­­­­­¬­«««««««««««««««««««««¬­­¬¬¬­­¬­­­­¬¬¬­¬¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­­¬¬¬¬­­­¬¬¬¬¬­­¬¬­­­®­¬­­¬¬­¬¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬­¬¬¬­¬­­¬¬¬¬¬­¬­¬­¬¬¬­¬¬­¬¬­¬¬¬¬­¬­­­­­­­®®®¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬­¬­¬¬­­¬­««««««««««««««««««««¬¬¬¬­¬¬­¬¬­­®¬­­¬¬­­¬¬­¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­­­­­¬­¬­­¬¬¬­­­­­¬­®¬®¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬¬¬­­­¬­¬¬­¬¬­¬­¬­­­­¬­¬¬¬¬­­­­­­­­­­­­­¬¬­­¬¬¬¬­­¬¬¬¬­­¬¬­­¬­¬¬­¬­¬¬­¬¬­­­­­­­­«««««««««««««««««««««««¬­¬­¬­¬­­¬­­®¬­®®¬¬­®­­¬¬««««««««««««««««®®®®®­­®®­­­­­­­­­­­­­­¬¬­­­¬­¬­­­¬­¬¬¬­¬¬¬¬¬¬­­¬­­­­­¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬­­®®¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬««««­««««««««««««««««««««¬««««««««««««««««¬­¬®®®­¬¬­¬¬­­¬¬«««««««««««««««««®®®®®®®®®­®®®¬­­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬­¬¬­¬­¬¬¬­¬­¬­¬¬¬¬­¬­¬­­­¬®®¬¬­­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬«««««««¬««¬«««««««««««««««««««««««««««««««¬¬®¬­¬­«««««««««««««««««««««®®®®®®­­­­­­®®®­­­­­­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­­­­­­¬¬­­­­¬­¬¬­¬­¬¬­¬­­¬¬­¬¬¬¬­­­­¬­­­¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬««¬««¬¬««««««««««««««««««««««««««««¬¬­¬¬¬¬¬¬¬¬­¬­¬¬¬­¬«««««««««««««««®®®®®®®®®®­­­­¬¬­¬­¬¬­¬¬¬¬­­¬¬¬­¬­¬¬¬­­¬¬¬­¬­¬¬­¬¬­­¬­¬¬­­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­­¬¬­¬­¬¬¬­®­¬¬­­­­¬¬¬¬¬¬¬­««¬¬«««««««««««««««««««««««««««««««««««¬¬¬¬­­­­­¬¬¬­­¬«««««««««««««««««««««®®®®®®®®®­­­­®­®®­¬­¬¬¬­­¬­­¬¬­¬­¬­¬­¬¬­¬­¬¬­¬¬­¬¬­­¬¬­¬¬­­­¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬­­¬­¬¬­­¬¬­­¬­­­­¬¬¬­¬¬¬¬¬¬¬¬¬««¬¬¬«««««««««««««««««««««««««««««««««­­¬®­¬­¬¬¬­«¬«««««««««««««««®®®®®®­­®®®­®®®®­­­¬¬¬­­¬¬¬¬­¬­¬¬­¬¬¬­­­­¬­¬¬¬­¬­­¬¬­¬­­­¬­¬­¬¬¬­­¬¬¬­­¬¬­­­­­­¬¬¬­­­¬¬¬¬¬««¬¬¬««««««««««««««««««««««««««««««««««­­®¬¬­¬­­¬««««««««««««®®®®®®®®­­­­­­®®®­­®­­¬¬¬¬­¬¬­­¬¬¬¬¬­¬­¬­¬­­¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬­­­¬¬¬¬¬¬¬¬­¬­­­­¬­­¬­¬¬¬¬­¬¬¬¬¬¬­¬¬¬«««««««««««««««««««««««««««««««««­«­­¬¬®¬¬­¬¬­­«««««««««««««««««««««®®®­­­­­­­®­­­­­­¬­¬­¬¬¬¬¬¬­¬¬­¬¬¬­­­­¬¬­¬­¬¬¬¬¬­­¬­¬¬­¬¬¬¬¬­¬­¬¬­¬­¬­¬­­­­­¬­¬¬¬­¬¬­­¬¬¬¬¬¬«««¬««¬¬­«««««««««««««««««««««¬­¬«««¬¬­¬¬¬¬¬®­¬­­¬««««««««««««««««««®®®®®®®®®®®­­­¬­®­­­­­¬¬­­­¬­­¬¬­¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬­¬­­¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬¬­¬­­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬««­¬«««««««««««««««««««««««««¬¬¬­®¬¬¬¬¬­­¬¬­«¬««««««««««««««®®®®®®®®®­­­­­­­­¬­­­¬­­­¬¬­¬¬­¬¬­­¬¬¬­¬­­¬¬¬­­¬¬¬¬¬­­¬­­­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬­¬¬¬­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬¬«­¬«««««««««««««««««««««««««««««®­­­¬­­¬¬¬¬­¬¬««««««««««««««««««®®®­­­­­­­­­®®®®­­­­­­­­¬­­­­­­­¬¬­¬¬­¬¬­¬­¬¬­­­¬¬­¬¬­¬­­¬­¬¬­¬¬¬­¬¬­­¬­¬¬­¬¬¬­¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬««¬¬¬¬¬«««««««««««««««¬«««««««««««««««¬­¬­¬¬¬¬¬­­¬­¬«¬¬«««««««««««««««««®®®­­­­­­­­­­¬¬­¬¬¬¬¬­­¬­¬­¬¬¬¬­¬¬¬­¬­¬¬­¬¬­­¬­­­¬­¬­¬­¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬­¬¬­­­¬¬¬¬«¬«¬¬¬««««««««««««««««««««««««««««««««¬­¬­¬¬­­¬­«¬«¬««««««««««««««««««®®®®®®®­­­­­­­­­­­®®­­­­­­­¬­­¬¬¬¬¬­­¬­­¬­¬­¬¬¬¬¬¬­¬­­­¬­­¬­¬¬¬¬¬­¬¬¬¬¬­¬¬­¬­­­¬¬¬¬¬¬­¬­¬­­¬¬­¬­¬¬­«¬¬¬¬¬¬«¬««««««««««««««««««««««««««­«¬¬¬­®¬­¬­¬¬¬¬««««««««««««««««««««««®®®®®®®®®­­­­­­­­­­­­­­­­­­­¬­­­­­­­¬­­¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­¬¬¬­¬¬­¬¬­¬¬­­¬¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬««¬¬«««¬««¬¬««¬«««««««««««««««««««««««¬«¬¬¬¬®¬¬¬¬­­¬¬¬¬¬«««««««««««««««®®®®®®®­­­­®®­­­¬¬­­­¬­¬¬­¬¬­­­¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬­­¬­¬¬­¬­­­­­¬¬­¬¬­¬¬­­­¬­­¬¬­¬­¬¬¬­¬¬¬­­¬­¬¬¬¬­¬¬¬¬¬­¬¬««««¬««­««¬««««««««««««««««««««««««¬¬­¬­¬¬¬­­¬¬««««««««««««««««««®®®®®­­­­­­®­­®®®­­­­­­­­¬¬¬­¬­­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­­­¬­­­¬¬¬­­­®®®­¬¬¬¬¬­¬¬­­¬­­¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬««««««««««««««««««««««««««««««««««­­­¬¬¬­¬¬««««««««««««««««­®­­­­­­­­­®®®®®­­¬¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­­¬¬¬­¬¬¬­­¬¬¬¬¬¬¬¬¬­­­­­­¬­­¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬¬­¬¬««««««««««««««¬¬«««««««««««««««¬«««««««««««««¬­­­®­¬­­¬¬­­¬¬­¬«««««««««««««««««««®®®®®­­­­­­­­¬­­®¬­¬¬¬­¬­¬¬¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­¬­¬­¬¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬®®®¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«««««««««««««¬««««««««««««««««««««««««««««¬«¬¬¬­¬­®¬­¬­¬­®®¬¬­®­­¬¬««««««««««««««««®­­­­­­­®­®®®­­¬¬­¬¬­­¬­¬­¬¬­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­¬¬­­­­¬¬­­¬­¬¬¬¬¬¬¬­¬®¬¬¬¬¬¬¬¬¬¬¬«­¬¬«¬«««««««««««««««««¬«««««««««««««««««««¬¬­­­®­¬¬®¬¬­¬¬­¬««««««««««««««««««®®­­®­­­­­­®®®­®­¬­¬¬­¬¬­¬­¬­¬­¬­¬­¬¬­¬¬­¬­­¬­¬¬­¬¬¬¬¬¬­¬­¬­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬«¬«««««««««««««««««««««¬«««««««««««««««««¬««««¬¬­¬­¬®®¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬«¬««««««««««««««««­­­­®­­­®­®®¬­¬­¬¬­¬­¬¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬«¬¬­¬¬¬¬¬¬¬¬¬¬«««««¬««««««««««««««««««««««««««««¬««­¬­­¬¬®¬¬¬­­­¬¬¬­­¬«¬«««««««««««««®­®®­­­­­­­­®®®®­­¬­¬­­¬­¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬­­­­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬«««««««««««««¬­«««««¬¬®¬­¬¬®¬¬¬¬­­®¬­®®­¬¬­¬¬¬¬¬¬«««««««««««««««««®­­­­­­­®®®®¬¬­­¬­­¬¬­­¬¬¬­¬¬­¬­¬¬¬¬­¬­¬­¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬­¬««««««««««««««««««««««««««««««««««­««««¬¬­¬­¬­®­¬¬¬¬¬®¬¬«««««««««««««««««««««­®­­­­­­®®®®®®®®®¬­¬¬­¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­­­¬¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬«­¬«««««««««««««««««««««««««««««««««««««¬««««¬¬¬¬­®®®¬¬­­­¬­¬­­¬¬¬¬«««««««««««««««««®®®®®®­­­­­­­­­®­¬¬­¬­­¬¬­¬­¬¬­­­¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬­¬­­¬¬¬¬¬­­¬¬¬¬­¬¬¬««««««««««««««««««««««««««««««««««««««««¬¬««¬¬­¬­®®¬­¬­­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬««««««««««««««®­®®®­­­­­­®®®®®­¬¬¬¬­­¬­­­­­¬­­¬¬¬¬­¬¬­¬­­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬¬­¬««¬««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬­¬­­¬­¬¬¬¬¬¬¬««««««««««««««««««««®­­®­­®­­­­®®®¬­¬¬­­­¬¬­¬¬¬¬­¬­­­¬¬¬­­­­¬¬¬¬­¬¬¬¬¬­¬¬­­­¬­¬¬¬¬­¬«¬¬¬¬­«««««««««««««««««««««««««««««««««««­«¬¬­¬­¬¬­­¬­¬¬­­­­­¬­¬¬¬¬«««««««««««««««®­­­­®­­­­­­­­­®­­¬­¬¬­¬¬­¬¬­­¬­­­¬­­¬­­­¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬¬­¬¬¬¬¬¬­¬¬¬¬«««««««««««««««¬««««««««««««««««««¬«­­®®­¬¬­­­­¬¬­­¬­¬¬¬««««««««««««««««®­­­­­­­­­­­­®®®®®¬­­¬­¬­¬¬­­¬¬­¬­¬­¬­­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬­­­¬¬­­¬¬¬¬¬­­­¬¬¬««««««««««««««««««««««««««««««««««««¬¬¬¬¬¬­¬¬¬¬­­¬­¬¬¬­¬¬¬­¬­¬¬¬¬«««««««««««««««««­­­­­­­­­­­­­­­­®®®­­¬¬¬¬¬¬­­¬­­­­¬­­¬¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­¬¬¬¬¬¬¬¬¬­¬«¬¬««««««««««««««««««««««««««««««««««¬¬­­­¬¬¬¬­¬¬­¬¬¬­­¬¬­¬­¬¬­­¬«¬¬¬««¬«««««««««««««­­­­­­­­­­­®®­¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««­­¬¬¬­¬¬­¬­­­­¬­¬¬¬¬¬¬¬¬««««««««««««««««®®®®®­­®­­­­­­­­­®­­¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬­­¬­¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­­¬¬«¬¬­­­¬¬¬­­¬­¬¬¬¬­®­¬¬¬­¬¬««««««««««««««««««««®®®®®­­­­­¬¬¬­­¬¬¬­¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬«¬¬¬««««¬«««««¬«««««««««««««««««««««««««««««««««««¬«««««««««««««««««¬¬¬¬¬¬¬­¬­­¬­¬®­­­¬¬¬¬­­­¬¬¬¬¬«««««««««««««««««««¬®®®®­­®®®®­­®¬­¬¬­­¬­¬¬­¬¬¬¬­­¬¬¬¬««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«­­¬­­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­««««««««««««««««®®®®®®®®®®¬­¬­¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬­¬­¬¬¬­¬¬®®­­¬¬¬¬¬¬«­¬¬««¬«««««««««««««®®®®­®®®®­­­¬¬¬­­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬¬«««««­«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬­¬¬®¬¬¬­¬¬¬¬­¬«¬­¬¬­«­¬¬««««««««««««®®®®­®­®®­­®®­­­­¬¬­¬¬­¬¬¬¬­­­¬¬¬¬­¬­­­¬¬¬¬¬«­««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬­¬¬¬¬®¬¬¬¬­¬­¬¬«¬¬««««««««««««­®­®­­®®®®®­¬­¬­¬¬¬­¬­¬­¬¬­­¬­¬«««¬¬«««««««««««««««««««««««««««««¬««««««««««««««««««««««¬«««««««««««¬¬¬¬­¬­­¬¬®­¬¬¬­¬¬¬¬¬¬¬¬­«««¬«««««««««­««­®­®®®®®®­®­¬¬¬­¬¬­¬¬­­¬¬­¬­­¬¬¬¬««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬¬­¬®¬­¬¬¬­¬¬¬¬«««¬¬¬­«««««««««««««««««®®®®­®®®®­­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬«««¬­«««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««¬«¬¬¬¬¬¬¬¬®­­¬­¬¬¬¬¬«««¬¬««««««««««««««««®®®®­®­­®®®­­­­­¬­­¬­¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««¬¬­¬¬¬¬¬¬­¬­¬«««­¬««««««««««««««««¬«®®®®®®®®®®®®®­­­­­­­­­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬«¬«««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬«¬¬¬«¬¬­¬­¬¬¬­¬¬«­¬«««««««««««««®®®­®®®®­­­­­­­¬¬­¬¬­¬¬­¬¬¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬«¬¬­¬­¬¬¬¬­­®­­¬¬¬¬¬¬¬­¬««««««««««««««««®®®®­­®®­­®­­­­®¬¬­¬­­¬¬¬¬­¬­¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««­­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬«¬««¬««««««««««««««««««®®®®®®®®­­®®®­®®®­­­­­­¬­¬¬­¬­­¬­¬­¬¬¬­¬¬­¬¬«««««««««««««««¬«««««««««««««««««««««««««««««««««««««««¬¬­¬­­­­¬­¬¬¬¬¬¬«««««««««««­«««««««­­®®®­®­­®®­®­®­­­­­­­­¬¬¬¬­¬¬¬­¬­­­¬­¬¬¬¬¬¬«««¬««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«¬¬¬­­®­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««­®®­®®®®­¬­­­­­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬­­¬¬¬««««««¬¬««««««««««««««««««««««««««¬«««««««««««««««««««««««­¬¬¬¬¬¬®¬¬¬¬¬¬¬««««¬««««««««««««««««««®®­®®®®®®®®®­­­­­­­­¬­¬¬­¬­­¬­¬¬­¬¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬¬¬­®®®¬­®¬¬¬«««««««¬««¬«««««««««««««««®®®­®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­­­«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«­­®­¬¬¬¬¬¬¬«««¬«««¬«««««««««««¬««««««®­®®­®®®®®­®®®®®®®®®®®®®®®®®­­®®®­­­­­¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬«««­¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬«¬­­®®¬­«««««¬««««««««¬«««¬¬««««««®®®­®®®®®®®®®®®®®®®®®®®®®¬­­¬¬­¬®­¬¬­¬¬¬¬«««¬¬«««¬«««««««««¬¬«««««««««««««««««««««««««««««««««««««««¬¬«¬¬¬¬®­¬«««««««««¬««««««««««««¬««««­®®­®®®®®®®®®®®®®®®®®®®®®­®®®­¬­¬­¬¬­¬¬¬¬­­¬¬¬¬«««¬¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««­¬­­­¬®®¬¬¬«««««««««¬¬«««««««««««¬«««¬®®®®­®®®®®®®®®®®®®®®®®®®®®®­®®®­­¬¬¬­­¬¬¬¬®­¬¬¬¬¬¬¬¬¬««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬­¬¬­¬®¬¬¬««««««¬«««««««««««««««««««¬®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®­­­­®®®®®­­­®­­­¬­­­­¬¬¬¬­¬¬¬¬¬««¬¬­¬¬«««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬­¬¬¬¬®­¬­««««««««¬«««««««««¬««««®­®®®®®®®®®®®®®®®®®­®­®®®®®­­¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬«««¬¬¬««««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««¬««¬¬¬¬­«­­­¬¬«¬¬««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­¬®¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬««¬¬¬««¬«««««««««««««­­¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««­¬¬¬¬¬­¬¬­¬®¬¬¬««««««««««««««««­­¬«­®®®®®®®®®®®®­®®®®®®®®®®®®­®®­­­­¬­®®¬­¬¬¬¬­¬¬¬¬¬¬«­¬¬¬«««««««««««««««¬««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬­¬¬­­­¬«¬««««««««««««««««¬¬­««««®®®®®®­®®®®®®®®®®®®®®®®®®®®®­®®­­¬®­®®¬¬­¬¬¬¬­¬­¬­¬¬¬¬¬¬«¬¬¬¬««««««««««««««««««««««««««««««««««««­««««««««««««««¬¬¬¬¬¬¬­­¬­¬¬¬­««««««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬­¬¬¬¬«¬¬«««««««««¬«««««««««««««««««««««««««««««««««««««««««««­­­¬¬¬¬­­­¬¬¬¬¬¬««««««««««««««««««¬®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬¬­¬¬­¬¬¬«««­¬¬««¬¬«¬««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬­¬¬¬®¬¬«««««««¬¬¬««««««««««­««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®­­¬¬­­¬­¬¬¬¬¬¬«­¬¬­¬««¬««««««¬«¬«««««««««««««««««««««««««««««««««««««««««««¬­¬­¬¬¬¬¬¬¬¬¬¬®¬­«««««««««««¬«««««««««««««­«««®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬­¬¬­­¬­¬¬¬¬¬­¬¬¬«­¬««¬¬««««««««««««¬««««««««««««««««««««««««««««««««««««­­¬¬¬¬¬¬¬¬¬««««««««««««««««««««««®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­¬¬­¬­¬¬¬¬­­®¬­¬¬¬¬«««««¬­¬¬¬««««««««««««¬¬«««««««««««««««««««««««««««««««««««««««««¬­¬­¬¬¬¬¬««««¬««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®¬­­¬¬­­­­¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««¬«««««««««««««««««««¬««««««««««««««««««««««««««««««««««««­¬¬­­¬¬­¬­®¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­¬­­­­­¬¬¬­­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­««­«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«¬­¬«®««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­¬­¬­¬­­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬««¬««««««¬««««««««««««««««««««««««««««««««««««««««­«¬­­¬««««««««««««««««««««««¬««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬¬­¬¬­¬¬­¬­­­¬¬­­¬­¬¬­¬¬¬¬«¬¬¬¬¬¬¬««¬«««¬«««««««««¬««««««««««««««««««««««««««««««««««««««««¬«¬¬¬««««««««««««««««««««««¬««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬«¬¬¬­¬«¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬««­«««««««««««««««««««««¬¬¬«««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬­­¬­­¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬«­«««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬¬­­¬­¬¬¬­¬¬­¬¬¬®¬¬¬¬¬¬¬«««««««¬«««««««««««««««««««««««««««««««««««««««««««««««¬¬««®¬­««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­¬¬¬¬¬¬­¬¬­­¬­¬­¬¬®®®¬¬¬¬¬«¬¬­¬¬¬¬­¬««¬«««««««««««««««««««««««««««««««««««««««««««««««¬«¬«¬­¬¬««­¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­¬­¬¬­¬¬­­¬¬¬­­®®®­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­««®¬«««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®­­­­¬¬¬­¬­­­¬­¬­­­¬®®®­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­­¬¬¬«®««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬­¬¬­¬¬¬­¬¬­­¬¬®¬­¬¬­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬«««­¬¬¬¬««¬­«««««««¬««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­­¬­­¬¬­­¬­¬­¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­­­¬¬¬¬¬«¬««««««­««««««««««««««¬««««««««««««««««««««««««««««««««««««««««­­««®¬«««««««««¬«««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬«¬¬¬¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­­¬¬­¬­¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬«¬«¬­«««««¬««««««««««««««««««««««««««««««««««­««««««««««¬¬­¬¬¬¬­««««­­¬¬«««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­­¬­¬­­¬­¬¬­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬«««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬¬¬¬«®¬­««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬¬¬¬¬¬­­¬¬¬­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬«¬««««¬««¬«¬­«««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬­¬­««­®­¬¬««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬®®¬­¬¬­«¬¬¬¬¬¬­­­¬¬¬¬¬­¬««««««¬«««¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­¬¬««®¬¬¬¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬¬¬¬­­­­¬­¬®­¬­¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬­®¬¬¬­¬¬«««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­«««¬®¬¬««««¬««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­­¬­­­­¬­¬¬¬¬®®¬¬­­¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬­¬­««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««¬««­­««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬­¬¬­¬¬­¬­­®¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬­­¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬®­¬­¬¬«««««««««¬«««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­¬¬¬­­¬­­¬¬¬­¬®®®¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬­¬¬¬­¬­¬«¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬®¬¬««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­¬­¬­¬­­­¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬¬­¬­¬¬­¬«««««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬®¬¬¬¬«««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬­¬¬­¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬­­­­­­¬¬¬­­­¬­¬­¬¬¬¬«««««««««¬«««««««««««««««««««««««««««««««««­«««¬«««««««««««««««¬¬­®¬­¬«¬«««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­¬­­­­­¬¬­¬¬­¬­¬¬­¬¬¬­¬¬­­­­¬­­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­­¬«««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬¬¬­¬­­¬­­­­¬¬¬­¬¬¬­­¬¬­­¬¬¬­¬¬­­¬¬¬¬¬­¬¬««««¬««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««­­¬¬¬¬««««««««««««««««««¬«®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­¬­¬¬­­¬¬­¬¬­¬¬­­¬¬¬­¬¬­¬¬¬¬¬­¬­­¬¬¬¬­¬¬­­¬¬­¬­¬««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬¬¬®¬«««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­¬­­¬­¬¬­¬¬­¬¬­­­¬¬­¬¬¬¬­¬¬¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­­¬­­¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬««««­­­¬««««««««««««««««««««««««««®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­®­­­¬¬­¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­­¬¬­­¬­¬¬¬¬­­­¬­¬¬­­­­­¬¬¬««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬¬«¬««­¬«««««««««««««««««««««««®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬­¬­¬¬­¬¬­¬­¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬««««««««««««««««¬««««««««««««««««««««««««««««««««««««««¬«­¬­«««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬¬­­¬¬­¬­¬­¬¬­­­¬¬­­¬­¬¬¬­­­¬­¬¬¬¬­­­¬­­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««­«««««««««««««««««««¬««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­­­¬¬­¬¬­­¬¬­¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬­¬¬­¬­¬¬­¬­­¬­­­¬¬««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­¬««««««««««««««««¬««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­¬­¬¬­­­¬­¬¬­¬¬­­®­¬¬¬­­¬¬¬­¬¬­¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬«¬««««««««««««««««««««¬«««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­­¬¬­¬­­¬­¬¬­­¬¬¬­¬­¬­¬¬­­¬­¬¬­¬¬¬¬­­¬­¬­¬¬¬¬­¬¬­­¬¬¬¬««¬«««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««¬«««­­««¬««««««««««««««««««¬«««¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬­­­­¬¬¬¬­¬¬­¬¬®¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬«««««««««««««««­«««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­«¬««««««««««««««««««««««¬¬­¬¬¬¿¿¿1¿1¿¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­¬¬¬¬¬¬­¬­¬¬¬¬­¬¬­­¬­¬­¬¬­¬¬¬¬¬­¬¬­­¬­¬¬¬¬­¬¬­¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««®¬««¬«««««¬««««««««««««««««­¬¿1¿11¿1¿11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­®­­¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬®®®¬­¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬¬­­«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬¬¬««««««««««¬«««««««««««¬¿¿1¿¿¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬¬­­¬­­­­­¬¬­¬­¬­­¬­¬­­¬­¬¬­¬¬¬¬­­¬¬­¬¬¬«¬«««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««««­«¬«««««««««««««««««««««««««««¬¬«¬1¿¿1¿¿1¿¿11¿¿¿1¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­­­¬¬­¬¬¬­­¬¬­­¬¬¬­®­­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­««««««««««««««­¬¬«««««««««­¬¬¬¬¿¿11¿¿¿1111¿¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­¬¬­­­¬¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬««««««««««««««««««««¬«««««««««««««««¬««««««««««««««««««««««««««««««««««««««««­¬«««««««««««««¬¬­««««««¬1111111¿¿1¿11¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­¬­¬­¬¬¬­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬­¬¬¬««««««««««««««««««««««««««««««««¬««««««««««««««¬«««¬«««««««««««««««««­¬­««««««««««««­«««¬¬¿¿111111¿¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬­­­­­­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­¬¬¬­­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬«¬««««««««««««««««««¬¬¬¬¬¬¿¿11¿1¿¿¿1¿1¿11¿¿1¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬­­¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««­«««««««««««««««««««««««««««««««««««¬¬«««««««¬«««««««««¬«««««««««¬¬¬­¬¿¿¿1111¿1¿¿¿¿11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬­¬¬­¬¬­¬­¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««­¬¬««««««««««««««««««««¬¬«¬¬­¬¬«111¿¿11111¿1¿¿11111111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®­¬¬­­®¬­¬¬¬­¬¬®­¬¬­­¬¬­¬¬­¬­¬¬­­¬¬­¬¬¬­¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬«««««««««««««««««««««««­¬«1¿¿11¿11111111¿¿1111¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬­¬¬­¬¬¬¬­¬¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬««¬««««««««««««««««««««««««««««««««««««««««««¬««««««««­««««««««««««««««««¬¬¬«««««««««««¬«¬«««««««¬¿¿1¿1111¿¿11¿¿1¿1111111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­¬¬­¬¬¬­¬­¬¬­¬¬­¬¬¬­¬­¬­¬­¬¬­¬¬¬­­¬¬­¬­¬­­¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«««««««¬««««««««««­¬¬1¿¿1¿¿1¿¿1¿111¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬¬¬¬¬®­­­¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬­­¬¬¬­­¬­¬¬¬¬¬¬¬¬­¬¬¬­«¬¬¬«¬«««««««««««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««¬­«¬¬«««««««««««¬«««­«««««««¬¬¬­¬«¿¿¿11¿1¿¿111¿1111¿¿1¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬­¬¬­¬¬­­¬¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬«¬¬«««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««¬¬««««««««««¬«««««««­¬¬¬«1¿1111¿¿1¿1111¿¿¿¿¿¿1¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­®®­­¬¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­­­®¬¬¬¬¬­­­­¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬­­¬«««««««««««««««««««««««¬¬¬¬¬¬¿¿¿¿¿¿¿¿¿¿111¿¿¿1¿1¿¿1¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬­­¬¬¬®®¬¬¬¬­¬¬­¬¬­­¬¬¬¬­¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬­¬««««««««««««««¬««««¬¬¬¬1¿¿¿¿¿¿1¿11¿11¿¿11¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬­­¬¬­­­¬­¬¬­¬¬¬¬¬­¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««¬¬¬¬¬¬¬«««««««««««««««¬¬¬¬«­¬¬¬¿¿¿¿¿¿11¿11¿¿¿1¿1¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®­­®®­¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬­­¬­¬¬¬­­¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«¬«««««««««¬¬¬¬¬¬­¬¬¿¿111¿¿¿¿¿¿¿1¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­®­¬¬¬­­­¬¬­­¬¬­¬¬­¬¬­¬­¬¬­­¬¬­­¬¬¬¬¬¬­­¬¬­«¬«««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««¬¬««««««««««««««««««««««¬­¬««««««¬«««««­«¬¬¿1¿¿1¿¿¿11¿¿11¿¿1¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬¬¬­¬­¬¬­¬­­­¬­­¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««¬­¬¬¬¬¬¬««««««««««¬¬¬¬¬¿¿¿¿¿1¿¿¿¿¿1¿¿1¿¿1¿¿1¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬­­­¬¬­¬¬¬­¬­­­®­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬¬¬««««««««¬¬­¬«­«¬­¬¬¬­¬¿¿¿¿¿¿¿11¿¿¿1¿¿1¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­¬®¬¬¬­­¬¬­­­¬¬­¬­­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬«««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬««««¬¬««««««««¬­««««¬¬¬¬¬¬¬¿¿¿¿11¿¿¿¿11¿¿¿¿1111¿111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬®­­¬­¬­­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬«««¬¬¬«««««¬¬««¬¬¬¬¬¬¬¬«¿¿111¿¿1111¿¿¿¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®¬­­¬¬­¬¬¬¬­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««­¬¬««¬¬­¬¬¬«««««¬««¬¬¬¬¬¬¿111¿¿¿¿¿¿¿¿¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­­­­¬­¬¬­¬­¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«­¬¬¬¬«««««««««¬¬««««¬««¬¬¬¬¬¬¬1111¿¿¿¿¿¿111¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬­¬¬¬­¬­¬¬­¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬­¬¬¬«««¬««««««««««««««««««««««««¬««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««¬¬­­¬­««««««««««««««­¬¬¬¬¬¬¬¿1111¿¿¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­­¬­­¬­¬¬­­¬¬­­¬¬­¬¬­­¬¬¬¬¬¬¬¬­¬¬¬««««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««¬­¬¬­«««««««««««¬¬¬¬¬¬¬¬­¬111¿¿¿11¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®­­­­­­¬­­¬¬¬­¬­­¬¬¬¬¬­­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««¬««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««««««««««««««««««««««««««««««¬¬¬¬¬­¬¬««««¬«««¬«««««¬¬¬­¬¬¬­¬¿¿11¿¿¿111¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««««««««««¬«««««««««««««««¬«««««««««««««««««««««««««««««««¬««¬¬¬¬¬¬¬««««««««««««¬¬¬¬¬¬¬¬­¬¬¬¿¿11¿¿¿¿11¿¿11¿¿1¿¿¿®®®®®®®®®®®®®®®®®®­®®­®®­¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬«««¬«««¬¬««««««««««««««««««««««««««««««««««««««««««««««««««««­««««««««««¬«««««««««««¬¬¬¬¬­­¬««««««««««««««««¬¬¬¬¬¬¬­¬¿11¿¿¿¿¿1¿1111¿¿¿¿¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬­¬­¬­¬­­¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬««««««¬«««««««««««««««««««««««««««««««««««««««««««««««««««««­«««««««««««««««««««««««««««««««««««««««¬¬¬­¬¬¬­­¬¬««««««««««««««««««¬¬­¬¬­¬¬¬¬¬­¬1¿¿¿11¿¿¿¿1¿111¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­­­­¬¬­­¬¬­¬¬­¬­¬¬¬¬­­¬­¬¬¬­¬­¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬«¬«««««««««««««¬¬¬¬«««««««««¬¬­¬¬¬¬««««««««««««««««««««««««««¬««¬««««««««««««««­¬¬¬­¬¬««««««««««¬­¬¬­­¬¬«1¿¿¿111¿11¿11¿111¿®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬¬¬­­¬¬¬¬­¬­¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬«««««««««««««««««««¬««««««««­«««««««¬«¬¬¬¬«««««««««««««««««««««««««««««««««««««««««««««««««­­¬­¬¬««««««««¬¬¬¬¬¬­­¬¬1¿¿¿11¿¿¿111¿11¿11¿¿1¿¿¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­­­¬¬¬­¬¬¬­¬­¬¬­¬¬¬¬¬¬¬®­­¬­¬­¬¬¬¬¬¬¬«¬«««««««««««««««««««««««¬¬«¬¬¬¬«««««««««««««¬­¬¬¬¬««««««««««««««««««««««««««««­«««««««««««««¬¬¬­¬­««««¬­¬¬­¬¬¬¬¬¬1¿¿¿¿¿¿111¿11111111¿¿¿¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­¬­¬¬¬¬¬­¬­­­¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬­¬¬¬«­««««««««««««««««««««¬«««««««««««¬­­¬¬¬­¬««««««¬«««««««««««««««««««««««««««««««­¬««««¬«¬¬¬­¬¬¬¬¬¬1¿¿1¿¿¿¿¿¿11¿1111¿¿1¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­­­­¬­­¬¬¬¬¬­¬¬¬­¬­¬¬­­¬­¬­­­­¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬«¬«««««««««««««««««¬¬««¬¬¬¬¬««««««««­­¬¬¬­­«¬««««««««««««««««««««««««««««¬«¬«¬«««««¬«««««««¬«¬­«¬«««««¬¬¬¬¬¬¬­¬¬¬1¿¿¿1¿1¿1¿111¿¿¿1¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®­­­­­¬¬­¬¬­­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬¬¬­¬­¬¬­¬­­¬¬¬¬¬¬«««««««««««««¬¬¬¬¬¬««««««««¬¬¬­¬­­««¬¬««««««««««««««««««««««««««««««¬¬¬««««««««««­«¬­««««««¬¬«­¬¬¬¬¬11¿1¿11¿¿1¿¿¿1¿1¿¿11111¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­­¬¬­¬­­­¬­­¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­®¬¬­­¬­¬¬¬¬«««¬¬««««««««¬««¬«¬­¬««««««««««¬­­¬­¬­¬«««¬««««««««««««««¬«¬«««¬«««««««««««««««¬¬¬«««¬¬¬¬¬¬11111111¿¿1¿1¿¿11¿¿¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­­­­¬­­­¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬­¬­¬­¬­®­¬­­¬¬­««««­««««««««««««««««¬¬¬¬««««««¬«¬¬¬¬««««««««««««««««««««««¬¬¬¬«««««««««««««««««¬¬«­«¬¬¬«¬¬¬¬¬¬¬­1111111111¿¿111111¿11¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬¬¬­¬­¬¬­¬¬¬­¬¬­­­¬¬¬«««««««««««««««««««««««««««­«­­¬¬««««««««««¬¬«««««««««««««««««««««¬«««««««««««««««¬¬®¬¬¬««««­¬¬¬¬«111111111¿11¿¿111¿111¿1¿®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­­¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­¬­¬¬­¬­¬¬­¬­¬¬««««««««««««««««¬¬¬¬¬«¬¬¬¬¬««««««««««¬¬¬¬««««««««««««««««««««¬««««««««««««««««««¬¬¬««««««­¬¬¬¬­¬¬111111111111¿¿¿¿¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬¬¬¬¬¬­­¬¬¬­¬¬­­¬­¬­¬¬¬­¬­¬¬¬­¬¬­­­¬¬¬­¬­­¬¬­­¬¬¬¬¬­««««««««««««««­­­«¬¬««««««««««««««¬­«¬««¬¬««««««««««««««««¬««««««««««««««««­««««««««¬¬¬¬¿111¿1111¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®¬­­®­¬¬­¬­­­¬¬­¬¬¬­¬¬¬¬­­¬­¬¬¬¬¬¬¬­­¬­­¬¬­­¬­­­¬¬¬­««««««««¬«««««¬¬¬¬¬¬­¬¬««««««««««««¬¬«««¬««««««««««««««¬««««««««««««««««««¬¬«««««««««¬¬¬¬¬­«¿111111¿¿¿¿¿¿¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬­­¬­¬¬¬¬¬­¬¬­­­¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬«««««««««««¬¬¬¬¬­¬¬­­¬««««««¬««¬¬­­¬­­¬««««««««««««««««««««««««««««««««««««««««««­¬¬««««««««¬«¬¬¬¬11111111¿¿¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®­­­¬­­­¬¬¬¬­¬¬¬­¬¬­¬¬¬¬­¬­¬¬¬­­¬­¬­¬­¬­­¬­¬­¬¬¬¬¬¬­«¬¬««««««««««««««¬­¬¬¬¬«««¬¬««¬¬¬¬««««¬«¬««««««««««««­««««««««««««««««««««««««¬¬¬«««««««««¬¬¿1111111¿¿11111®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®­­­­¬¬­­­­­­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬¬¬­­¬­¬¬¬¬­­­¬¬¬¬­¬«««««««««««««««««¬«¬­¬¬¬¬««««««««««­¬¬¬«¬­­¬«¬¬««««««­««««««¬¬«««¬«««««««««««««««¬¬­¬«««««««««¬««¬¬¬¬¬¬¬11111111¿¿¿¿¿¿¿¿1111¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬¬¬¬­¬¬­¬­¬¬­­¬­¬¬­¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬««««««««¬«««««««««¬¬­­¬¬¬¬«««««««¬­¬­­«¬«¬«««««««««««««««¬«««««¬««««««««««««««­­««««««««««««¬¬1111111¿11¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®®®®®®®®®­¬­­­­¬­­­­­¬¬¬¬¬¬¬®­­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬««««««¬¬¬­¬¬«¬«««««««¬¬­«¬­¬«««««««««««««¬«««««««««««««««¬«­¬¬¬«««««««««««««¬¬«¬¬1111111¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®­®®®®®®­­¬¬­¬¬­¬­¬­¬­¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬«¬¬¬¬¬¬¬¬«««««¬««««¬­¬««¬¬¬¬«««««««¬­««¬«««««««««««««««««««««¬­¬¬¬¬«««««««¬¬¬¬­11111111¿11¿11¿®®®®®®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬®­­­­¬¬­­¬¬¬¬­¬­¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬««¬««¬¬¬¬¬¬¬­¬««««««¬¬¬¬¬««¬¬«««««««­¬««««««««««««««««««««««««««­¬¬¬¬­««««­¬¬¬¬¬¬­11111111¿11¿11¿1®¿®®®­®®®®®®®®®®®®­®®®®®®®®®®®®®®®­®­®®­­¬­­­¬¬¬­¬¬¬­¬¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬««¬«¬¬¬­­¬¬¬¬¬«««¬««««««««¬¬«««­««¬«««««««««««««¬«««««««««««««««««««­¬¬¬¬«««««««¬¬¬¬¬¬¬¬¬111111111111¿¿11¿®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­¬¬¬¬­¬¬­¬­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬­¬¬««««««««««¬«¬­¬¬««««««««««««««««««««««««««««««¬¬¬¬¬¬«««««««««««¬¬¬¬¬¬¬¬¬11¿11111111111¿1¿¿®®®¿®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­­¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬­¬¬¬¬­­­¬¬¬¬­¬¬¬¬­­¬¬­­­«¬¬­¬¬¬¬­¬¬¬«««««««««««««­¬«««¬¬¬¬««««««««¬«««««¬««««««««««««¬¬¬¬¬¬««««««««««««««¬¬¬¬¬¬¬1111111111¿¿1¿¿¿®®®®®­®®®®®®®®®®®®­­®®®®®®®®®®®­¬¬­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬¬¬¬««««««««««««­««­¬««¬«««««««««««««««««««««««««««««¬­¬­¬¬­­¬­«««««««««««¬¬¬¬¿1111111111¿¿¿¿¿®®®®®®®®®®®®®­®®®®®®®®­®®®®¬¬¬­¬­­¬¬¬­¬¬­¬¬¬¬­­®­­¬¬­­¬¬­­¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬««­¬¬¬¬¬¬­¬­¬¬«««««««¬««««««¬¬««««««««««¬««««««««««««««««««¬¬¬¬¬­¬­¬¬¬«««««««««¬¬¬¬¬¬¬¬¬¬11111111111¿¿¿¿1¿¿¿¿®®®®®®®®­­®®­®­­®®®­®®®®®®®®®®®­­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬­­¬­­­­¬­¬¬­¬­¬¬¬¬­­¬¬¬­¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬¬­­¬¬¬¬«««««««««««¬¬¬«««««­¬«««««­«««««««««««««««««««««¬¬¬­¬¬«««««««««««¬¬¬¬1111111111¿¿1¿¿¿11¿¿¿®®®®®®®®®­®®®®­®®­®®­­®­®®­®®®®®®®®®­¬¬­­­¬¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬««««¬¬¬¬¬¬­««««««««¬¬««¬¬¬¬¬««¬«¬¬«««««««««««­«««««««««««««««««««¬¬¬¬¬¬¬«««««««««««««««««¬¬­«¬¿11111111¿1¿¿¿¿¿¿¿®®®®®®®®®®®®­®­®®®­­®®®®®®­­­®¬­­¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬¬««««¬¬¬¬¬¬¬¬««««««««««««««­¬¬««««¬¬¬¬¬«««««««««««««««««««««««««««««««¬­¬¬¬¬­««««««««««««««¬¬¿111111111¿¿1¿¿¿®®®®®®®­®®­®®®®®®®­®®®®®®­¬­­­¬¬¬­¬¬¬¬­­¬¬­¬¬­¬¬­¬¬¬­¬­­­¬­¬­¬¬¬­¬¬¬¬¬¬«««¬¬«¬¬¬¬¬¬««««««««««««¬¬¬«««««­«««««¬«««««««««««««««««««««¬¬¬¬¬­¬¬«««««««««««««««¬¬¬1111¿11111¿¿¿¿¿¿¿®®®®®®®®­®­®®®®­®®®®®®®®®®®­­­®¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬«¬¬¬¬¬««««««««««¬««¬¬«¬«««««««««««««««««««««««««««¬«¬¬­®¬¬¬­­¬¬¬«««««««««««¬¬¬¬¬«1¿1¿¿111¿¿¿1¿¿1¿¿®®®®®®®®®®®®®®­®®­®®­®®­­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬­­­¬¬­¬¬­¬¬¬¬¬­­­¬¬¬¬¬¬«««¬¬¬¬¬­¬¬¬¬¬¬¬«¬«««««««««««¬¬««­¬¬«¬««¬««««««««««««««¬«««««««¬¬­¬¬¬«««««««««««««««¬¬¬¬¬¿1¿¿¿¿¿¿¿¿¿¿¿®®®®­®®®®®®®®®®­®­­®®®®®®®®­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬¬­¬­­¬¬¬­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««¬¬­¬­­¬¬­«««¬¬««¬«««¬¬¬¬­««««««««««««««««««««««««««««¬¬¬¬­¬¬¬¬­¬«««««««««¬¬¬¬¬¬¬¬111¿¿1¿¿¿1111¿11®®®®®­®®®®®®®®®®­®®®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­­­¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬«««««««¬««­¬¬«¬­¬««««««««««««««««««««««««««««««¬­­¬¬­¬««««««««««««««¬­¬¬11¿¿11¿11¿¿11¿1¿¿¿¿¿¿¿®®­®­­­®­®­­­­­­­­­­­®®®­®­®®®®­¬¬­¬¬­­­¬­¬­¬¬¬¬­­¬¬­¬¬­¬¬­­¬¬­¬¬­¬¬¬­¬­¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«¬«¬«¬¬¬¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««¬­¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬111111¿11¿¿¿¿1¿111¿¿¿¿®®­­®­­­­­­­­­­­­­­­­®®®­®®®®®­­­­¬¬¬¬¬¬¬­¬¬¬¬­¬­­¬¬­¬­­¬­¬¬¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬­¬¬¬¬¬¬¬««««¬«««¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««¬¬­­¬­¬¬¬­¬­­­¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬1111111111¿¿11¿11¿¿¿®®­®®­­­­­®­­­­­­­­­­®®®®®®¬®­­¬¬­¬­­¬­¬¬¬­¬¬­¬­¬¬­¬­¬­¬­¬­¬¬­¬¬­­¬¬­¬­¬­¬¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬«««¬««««««««¬¬¬¬­¬¬­­¬¬­­¬¬¬¬«««««««««««««««««««««««««««««¬¬¬­¬¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¿1111111¿¿111¿1¿¿¿®­®­­®­­­­­­­­­­­­­­­­­­®®®®¬­­¬­­¬­¬¬¬¬¬­­¬¬­­¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬­¬­­­¬­¬­­¬­­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬­¬«¬¬¬¬¬¬¬¬¬¬¬«««««««¬¬­««¬¬¬¬¬¬¬¬¬¬­­¬«««««««««««««««««««««««««««««««¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬111111¿¿1111¿¿¿¿¿¿®®®®®­­­­­­®­­­­­­­­­­­®®­®®®®¬¬­­­­­¬­­¬­¬¬­­¬¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­­­­¬­¬¬¬­¬¬¬¬­­¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬««««««¬«««««¬««¬¬­¬­­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬1111¿¿¿1¿11¿¿1¿111¿¿®®®­­­­®­­­­­­­­­­­®­®®­®®®®®®®¬­¬¬­­¬­¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬­¬­¬¬­­­¬­¬­¬¬¬­¬­¬¬­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬«¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬«««¬¬¬¬¬¬¬¬­¬¬¬¬¬¬««««««««««««««««««««««««««««««««««¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬1111¿1¿¿1111¿¿1¿111¿¿¿1®¿®®­­­­­®­®®­®­­­­­­­­­®­­­­®­­®­­¬¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­«¬¬¬¬¬¬¬¬¬¬¬««¬«««¬««««¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««¬¬¬­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬111111¿¿11¿¿1¿1¿¿¿¿¿¿¿1®¿®®®®®®®­­­­®­­®­­­­­­­­­­­­®­®®­¬­­­¬¬¬­¬­¬¬­¬¬­¬­¬­¬¬¬­­¬¬­­¬­¬¬­­­¬­¬¬­¬­­¬¬­¬¬­¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬«««¬¬¬¬¬¬«««««««¬««««¬¬¬¬¬¬¬­¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬11¿1¿111111¿¿¿1¿1¿¿®®®®®®®­­­­­®®­­­­­­®­®®­®­­®­¬­­¬¬¬¬­¬¬­¬¬­¬­­­¬­­¬¬¬­­¬­¬­¬­¬¬­¬­¬­¬¬­¬­¬­­­­­¬¬¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬¬«««««««««««««­¬¬¬¬¬­¬¬¬¬¬¬¬«««««««««««««««««««««««««««­¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¿1¿1¿11¿1111¿¿¿¿¿¿®¿¿1¿®®®®®®­­­­­­­®­­­­­­­­­­®­®­®­®®®®­­¬¬­­­¬¬­¬­¬¬­¬¬­¬¬¬­¬¬­­­¬¬­¬­¬¬­­¬¬­­¬¬­¬¬­¬¬¬­­­¬¬¬­­­­«¬«¬¬¬¬¬¬¬¬­¬¬¬¬«¬«««¬«««¬«««¬««­¬­­­¬¬¬¬¬­¬«¬««««««««««««««««««««««««««««­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¿1¿11111111¿¿¿¿¿®®¿®®®®®®­­­­­­­­®­­­­­­­­®­®®­®®®®®®­­­¬¬­­­¬­¬­­®¬­­¬¬¬­¬­¬¬­¬¬­­¬¬¬¬¬¬¬¬­¬¬­­¬¬­¬­¬¬­¬­¬¬­­®­­¬­¬®®¬­¬¬¬¬«¬¬¬¬¬¬¬­¬¬¬««««¬««¬««¬­­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««««««««¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬­­­¬¬­¬¬¬¿¿1111111111111¿111®®®®®®­­­­®­­­­­­®®®®®®®®®®­­­­­¬­¬¬¬­¬¬­­­­­­­¬­¬¬¬­¬­­¬­­­­¬¬­­­­¬¬¬­¬¬­¬­­­¬¬­¬­¬¬®¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««­¬¬«¬¬¬¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««««««««««««®¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­¬¬¬11111111111111¿¿¿1¿®®®®®®®®­­­­­­­­­­­­­­­­­®­®®­­®®®®®­­­¬­­¬¬¬¬­¬­¬¬¬¬­¬­¬¬­­­­¬¬¬¬¬­¬­­¬¬­¬­¬¬¬­­¬¬­­¬­­¬¬¬­¬¬­­¬­¬¬¬¬­­¬¬¬¬¬­¬¬¬¬¬«««««««««««¬¬«¬¬¬­¬­­­­¬«««««««««««««««««««««««««««««««««­­¬¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¿111111111111¿¿¿¿11¿®®®®®®®®®®®­­­­­­­­­­­­­­­­­®®®­­­­®®®­­­¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­¬¬­¬­¬­¬­¬¬­­¬­¬­¬¬¬­¬¬¬¬­­¬¬¬¬¬¬«¬¬¬¬¬¬¬¬«««««««««««¬¬¬¬¬¬¬¬¬¬¬¬­­«««««««««««««««««««««««¬«¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬1111111111¿111¿1¿1¿¿1®®®®®®®®®®®­­­­­­­­­­­­­­®­­®®®®®®­®®®®®­¬¬¬¬­¬¬­¬­¬­¬¬¬­¬¬­¬­­­¬¬¬¬¬¬¬­¬¬¬­¬­¬­­­¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬««««««««««««««««««««¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬®¬¬«««««««««««««¬«««««««««««««««¬¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¿11111111¿11111¿¿1¿¿1¿®®®®®®®®­­­­­­­­­­­­­­­­­­®®®®®®®­®­®®®®¬­¬¬­­­¬¬¬¬­¬­¬¬­­­­¬­¬­¬­¬­¬­­¬­­¬¬­­¬¬­¬­­¬¬¬­¬­¬¬­¬­­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬««««««««««««««««««¬¬¬¬­¬¬¬¬¬¬­¬««««««««¬«««««««««««««««««««­¬¬¬¬­­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¿1111¿¿1111¿1¿1¿®®®®®®®®®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­­¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­­­¬¬­­­­­­¬¬¬¬­­¬¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬¬¬­¬­­­¬­¬¬¬­­¬¬¬«««««««««««««««««««¬¬¬¬­¬­­¬¬¬¬¬««««««««¬«««««««««««¬¬¬¬¬­­­¬¬¬¬­­­¬¬¬­¬­¬¬¬¬¬­­­¬¬­¬¬¬­­¬¬­¬¬1111111¿1¿¿11¿¿1®®®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬¬¬­­­­¬­¬­­¬¬¬¬­¬­¬¬¬¬­¬¬¬­­¬­­­­¬¬¬­­­­­¬¬­­¬­­¬¬¬¬¬¬¬­¬­¬¬¬­¬­¬¬¬¬¬­­­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬««««««««««««¬«¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««¬­¬­­¬¬­¬­¬­¬­¬¬¬¬¬­­¬¬¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬­¬¬1111111¿1¿1¿¿1¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬­¬¬­¬­¬­¬¬­¬¬¬¬¬¬­¬­­¬¬­­­¬­¬­¬­¬­­¬¬¬­­­­­­­¬¬¬­­¬¬¬¬¬­¬­¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬««««««««««««««««¬¬­¬¬­¬¬¬««««««««««««««««««««««««««««««««««««¬­¬¬­­¬¬¬¬­¬­¬¬­¬¬­¬¬¬­­­­­­¬­­¬­¬¬¬¬¬¬­¬­¬¬¬­¬­­¬¬¬­¿11111¿¿¿1¿¿11¿®®®®®®®®®®®­­­®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­¬­¬¬­¬¬¬¬¬­¬¬­¬¬­­­­­­¬¬­¬­¬¬­¬­¬¬­¬­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­­¬¬¬­¬¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬¬¬¬¬¬­¬¬«««««««««««««««««««««««««««¬¬¬¬­¬¬¬¬¬­¬¬¬¬­¬­­¬¬­­¬­¬¬¬¬¬¬­­¬¬¬¬­¬­¬¬¬¬­­¬¬¬­¬¬¬¬1111¿¿1¿¿¿¿¿¿11¿¿1¿®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬­¬­­­­®­­¬¬­­­­¬¬­­¬­­¬¬¬­¬­­¬¬¬­­¬­¬­¬­¬­¬¬­¬¬¬­¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­­¬¬¬¬¬­­¬¬­«««««««««««¬­¬­¬¬­¬¬¬¬¬­¬«««««««««««««««««««««««««««««««¬¬¬­¬¬­¬­¬¬­¬­­­¬¬­­¬¬¬¬­­­­­­­¬¬¬¬¬¬¬­­¬¬­­¬¬¬¬¬¬­¬¬­¿¿1¿¿1¿1¿¿¿1¿¿¿¿®®®®®®­®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬­­­¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­¬­­­­­­­¬¬¬¬­¬¬­¬¬¬­¬­­­­­­­­¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬­¬­¬¬­­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬««««««««««««««¬««­¬¬­¬¬¬¬¬¬­¬«««««««««««¬««««««««««««««««««««¬¬­¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬­¬¬¬¬­¬¬­­­­­­­¬­¬¬¬­­­­¬­¬¬¬¬¬¬¬¬11¿1111¿¿¿¿¿¿11¿®®®®­®­®®®®®®­­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®­®¬¬­­­­­­¬¬­¬­¬¬¬¬¬¬¬¬¬­­¬­®­­¬­¬¬¬¬­¬­¬¬­¬®­­­­­­¬¬¬­­¬¬¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬«««««««««««««««««¬¬«¬­¬¬­¬¬¬¬¬«««««««««««««««««««««««««««­¬­¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬¬­­¬­¬¬¬¬­¬¬­¬­­­­¬­­­¬¬­¬­­¬­­¿111¿1¿¿11111¿®®®®®®®­®®®®®­®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­¬­­¬­­¬¬¬­¬¬¬¬­¬¬­¬­¬­­­­¬¬¬¬¬­¬¬¬­¬­¬­­­­­­¬¬¬¬­­­¬¬­¬¬­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­­­¬¬­­­¬¬­­¬¬¬¬­¬¬¬­««««««««­­¬¬¬¬¬­­¬¬¬¬¬¬«««««««««««««««««««««««««¬­¬¬­¬¬¬­¬¬¬¬­¬­­¬¬¬¬­¬¬¬­¬¬­¬­¬¬­¬¬­­¬¬­¬¬­­¬¬¬¬¬¬­¬­­¬¬­1111111111¿¿1¿¿®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­­¬­­­®®¬­­¬¬¬­¬¬¬¬¬­­¬­¬­­¬­­¬­¬¬­­¬¬­¬¬®®­­­­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬¬­¬¬­¬­¬­¬¬¬­­¬¬­¬¬­¬¬¬¬«««««««««««­¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««¬¬¬¬¬­¬­¬¬­¬¬­¬­¬¬¬­¬­¬¬¬­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­¬­¬¬­¬¬­¬­¬­11111¿1111¿¿¿¿¿¿®®®®®®®®®®­®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®­¬­­¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­¬­¬­¬¬­¬¬­¬¬­¬¬­®­­­­­­­­­¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬­¬¬¬¬­¬¬­®­­®¬¬­­¬¬­­­­¬¬¬¬¬¬««««««««««¬«¬¬«¬¬¬¬­¬¬¬¬¬««««««««««««««««««««««««««««««¬¬¬¬¬­­¬­¬¬¬¬¬­¬­¬­¬¬­­¬¬­­¬¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬­¬­­¬¬111111¿111¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®­®®®®­®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬®¬­¬¬­¬¬­¬¬­¬¬¬¬¬­­­­¬¬­­­­­­­¬¬­¬¬­¬¬¬¬­­­­¬¬¬­¬®­­¬¬¬¬¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­®®®­¬­¬¬¬¬¬¬¬­­­¬¬««««««««««««««¬¬¬­­¬¬¬¬­¬¬¬¬««««««««««««««««««««««««««««««««««««««««¬«¬¬­¬¬¬¬¬­­­­­­­­¬¬­¬­¬­¬¬¬¬­¬¬¬­­¬¬¬¬­¬¬¬¬¬­¬¬­¿11111¿¿1¿¿111®®®®®®®®­®­®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬®­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬¬¬­¬¬­¬¬¬¬­­®®®­­­­­­¬¬­­­­­­¬¬¬¬¬­¬¬­¬­¬­¬¬¬­­­¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬®®®®®®®­­­¬¬­¬¬­¬¬­¬¬««««««««««­«¬«¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««¬­¬­¬¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬­­¬­¬­¬­¬¬¬¬¬¬­¬¬¬­¬­¬¬¬11111¿¿¿¿¿¿111¿®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­®­­¬­­¬¬­¬­¬¬­¬¬¬­­¬­®­­¬¬­¬­¬¬¬¬­¬­­®®®­­¬­¬­¬­¬­¬­­¬­¬­¬­¬¬¬¬¬¬­¬¬­¬¬­¬­­­¬­¬­¬¬­¬¬­¬­¬­¬¬¬¬¬­¬­®®®®®®®®¬¬­¬¬­¬­¬­¬¬¬««««««««¬¬«««¬¬­¬¬¬¬««««««««««««««««««««««««««««««««¬¬¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬­¬­­¬­¬­¬­¬¬­¬¬11111¿1¿¿1111¿¿®®®®®®®®®®®®®®®®®®­®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬¬­¬­­¬­¬­¬­­­­®­¬®­¬¬­¬¬­¬­¬¬¬­®­®®­­¬­¬¬¬­¬­­¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬¬­­®®®®¬¬¬­¬¬¬¬¬¬««««««««««­«¬¬¬¬¬¬¬¬­¬««««««««««««««««««««««««««¬¬¬­­­¬¬­¬­¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­­¬­­¬¬¬­111¿1¿¿1¿11¿¿¿¿¿®®®®®®®®®®®­®®®®­­®­®­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­¬¬­¬¬­­­­¬­¬®­¬¬¬¬¬¬¬­®®­­­¬­­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬­¬­¬­¬¬¬¬­­¬­¬¬¬¬¬­­¬¬¬¬¬­¬¬­¬­­­­­¬¬¬¬¬¬«««««««««««««««««««¬¬¬¬¬¬¬¬­«««««««««««««««««««««««««««««««««««««¬­®®¬­­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬111¿1¿¿¿¿¿¿®®®®®®®®®®­®®®®­®®®­®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®¬®­­­­­­­¬¬¬­¬¬¬­­¬¬­­­­¬­­­¬­¬¬­¬¬­®®­­¬­­¬­¬¬¬¬­­¬¬¬¬­¬¬­¬®®¬­¬¬­­¬­¬¬¬¬¬­¬¬¬­¬¬¬­¬¬­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬««««««««««¬««¬¬¬¬¬¬­¬¬¬¬­««««««««««««««««««««««««««««««««««««¬®¬¬¬¬­¬¬­¬­¬¬¬¬¬­­­¬­¬¬­¬­¬¬­¬¬¬­­­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­®®­­­­¬­­­­®­­­­­­­¬¬¬­¬¬¬¬¬¬¬­¬­­­®®®®­­­­¬¬­¬¬¬¬¬­¬¬¬¬­¬­¬­­¬­­¬­¬­­­­¬­¬­¬¬¬­¬««­­¬¬­­¬¬¬¬¬¬­­¬¬««««««««««««««««««««««««««««««¬­¬¬¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬­­­¬­¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬­­¿¿1¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®®­¬­­­¬¬­­®®®­®¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­­­­®­®®­¬¬­­¬¬­¬¬­¬¬¬­¬­¬­¬¬­¬¬­¬¬­¬­¬­­­®®¬­¬­¬¬­¬¬­¬¬¬¬¬«­¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬¬«««««««««««««««««««««¬­®¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬­­­¬¬¬¬¬¬­¬­­¬¬­­¬¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­¬¬¬­­­¬­­­®®­¬¬®¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬­¬­®­®®®®®­¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬­¬¬­­­­¬­­­­­­­¬¬­­­­¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­¬¬­¬¬¬¬««««««««««««««««««¬«­­­­­¬¬¬¬¬¬­¬¬¬¬­¬¬¬­­¬­¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬­­­­¬¬¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­¬­®®­¬­­­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬¬­­­­®®­®¬¬¬¬­¬¬¬¬­­¬¬­¬­¬¬­¬¬­¬¬­­­­­­­­¬¬¬¬¬¬­¬­­­¬««¬¬¬­¬¬­¬¬¬­¬­­¬¬¬¬««¬«««««¬««««««««««¬¬¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬¬­¬­¬¬¬¬¬­¬¬­¬¬­¬­­¬­¬¬­­­­¬¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­®®®®­¬­­­¬­¬­¬­¬¬¬¬¬¬­­­­­­®®­­®®¬¬­­¬­¬¬­¬­­­¬¬¬­¬¬¬¬¬¬¬­­­­¬­¬¬­­­­¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­¬«««««««««««««««««««¬­­¬¬­¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬­­­­¬­¬¬­¬¬¬­¿¿¿®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­¬®¬¬¬­¬¬­¬­¬­¬¬¬¬­­¬­­­®­®­­¬­¬¬­¬¬­¬¬­¬¬¬¬®­­¬­¬­¬¬­­­­­¬¬­­­­¬¬¬­¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬¬¬­¬­¬­­¬«««««««««««««««««­««­¬­­¬­­­¬­¬¬­­¬¬¬¬¬¬¬­¬­­¬­¬¬­¬­¬­¬¬­¬¬­­¬¬¬¬¬­¿¿®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®®¬­¬¬¬­¬¬¬¬¬­­¬¬¬­¬¬­­®­­­­®®®®®®®®¬¬¬¬­¬¬­¬¬®­­¬¬¬­­­­­¬­¬¬­¬­¬¬¬¬¬¬¬­¬­¬¬«¬¬¬¬¬¬¬¬¬¬¬¬­¬¬««««««««««««««««««««««¬­¬¬¬¬¬­¬­¬¬­¬¬­¬¬­¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬¬­¬¬­­­­­­¬¬¬¿®®®®®®®®¿®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®­®®®­­­­­­­­®®­­­­­¬¬¬¬¬­¬­¬¬­®­­­­­®®®®®®®®®®®¬¬¬¬­¬­¬¬­¬­¬¬­­¬­¬¬­­­­¬­¬¬­­­¬­¬¬¬­¬¬­¬¬­¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­­«««««««««««««««««««¬­¬¬­­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬¬¬¬¬­¬¬¬­­¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®­­¬­­­®®­®­­­­­­­¬¬¬­­­­­­­®®®®®®®­®®®­¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬¬¬¬­­­¬­¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­®®®¬­¬­­¬¬¬«««««««««««««««««««««¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬­­­¬­¬­¬¬®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®®®­­­­­­¬­­­­®®­¬­¬¬¬¬¬¬­¬¬­¬­­®®®®®®®®®®®®®®­­­¬­¬¬­¬¬¬¬¬¬­­¬­¬¬¬¬¬­¬­­­­¬¬¬¬­¬­¬¬¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­­¬¬­¬­­­¬««««««««««««««««««««««¬¬¬­¬­­­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬­­¬¬­¬¬¬¬¬¬¬­­­­¬¬­®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬­­­®­­­¬­¬¬¬­­­­­­­®®®®®®­®­®¬­¬­¬¬¬­­­¬¬¬¬¬¬­­­­­­­­­­¬¬¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬­¬­¬¬®­¬­¬­­­««««««¬«««««««««««««¬®­«¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­­­­­­­­­­¬®¬¬­¬¬¬­­­­­®®®®®®®®®®®®¬­¬¬­¬¬­®­¬­¬­­¬­¬¬¬­¬­­­®®®®­­¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬­­«¬­­¬­¬­¬­­¬¬¬­­¬«««««««««««««¬¬«­¬­­­¬¬­¬¬¬­­¬¬­¬­¬¬­¬¬¬­­­­¬­­¬­­¬¬¬­­­¬¬­¬¬®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­­­­®®®®­­¬¬¬®®­­¬¬¬¬¬¬­­­­®®®®®®®®®®®®®¬¬¬­¬­®¬­¬¬®®¬­­¬­­­¬­­­®¬­­¬­­­­¬¬­¬¬¬¬¬¬¬¬¬«¬¬¬­­­¬¬­¬¬¬¬¬­­¬«««««««««««««««««¬¬¬¬«¬¬­­­­¬¬¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬­¬­¬¬­¬¬¬­­¬¬¬­­­­­¬¬­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬­®­¬¬¬­­­¬¬¬¬¬¬¬­­­­­®®®®®®®®®®®­®¬­¬¬­­¬¬­¬¬­­¬¬¬¬­¬®­­­®®¬­¬¬­­¬¬­­­®¬¬¬¬¬¬¬¬¬¬¬¬«¬¬¬¬­¬¬­¬¬­­¬­­¬¬­­¬¬««««««««««««¬¬««¬­¬­­­­­­¬­¬­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬­­¬¬¬­­®­­¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­®®­­­­­¬¬¬¬­¬¬¬¬¬¬­­®®®®®®­®®®­¬¬­¬¬®­¬¬­®­¬­¬­­¬­¬­®­­­­®®­¬­¬¬¬­¬®­­¬¬­¬¬¬¬¬¬¬«¬¬¬­¬­­­¬¬¬­¬¬¬­¬­¬¬¬««««¬««««««««««¬¬¬¬¬­­­¬¬­¬¬­¬­¬¬¬¬­¬­¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬­­­­¬¬®­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬­­­­¬­­­­®­­­­­­­­¬¬­­­¬¬¬¬­¬¬­­®®®®­¬¬¬­­¬¬¬­­¬¬¬­¬¬¬¬¬­¬¬­­­¬®®­¬¬­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬«­­­­¬¬¬­­­­¬¬­¬¬¬¬¬««««««««¬««««««««««««¬¬«¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬­¬¬­¬¬­¬­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­¬­­­­­­­­­¬¬­¬­¬¬­­­­­¬­¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬­­­®¬­¬­¬¬¬¬­­¬­­¬­¬¬¬­¬¬¬¬¬¬­­¬­­¬­¬­­¬®®®®­­­­®®®¬­¬¬­­­¬­¬­¬­¬¬¬¬­¬¬¬¬¬¬¬«¬­¬¬¬¬¬­¬­¬­¬¬¬¬­¬­¬¬«««««««¬­¬¬¬«««¬¬¬¬­¬­¬¬¬¬­¬¬¬­¬¬¬¬¬­¬¬¬¬­­­¬¬¬¬¬¬­¬­­¬­®¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­¬­¬­­¬­­¬¬¬¬­¬¬­­¬¬­¬¬¬­¬¬­¬­­­­¬¬­¬­¬¬­¬¬¬­¬­¬¬¬­¬­­¬¬¬¬¬­­­¬­¬¬­®¬¬­­¬¬¬¬­­­­¬¬­¬¬®®®®®®®®®­¬­­­¬­­­¬¬­¬¬¬¬­¬¬¬¬­¬­­¬¬¬­­¬­¬¬«««««««««««««««««¬«¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬¬­¬­®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­¬­­­­­¬­¬¬­¬­¬­­¬¬­­­¬­­­¬¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­­­­­­¬¬¬­¬­¬¬­¬¬­­­¬¬¬¬­¬­¬®®®®®®®®­®®®¬¬¬¬­¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­¬¬®­­­¬­¬¬­¬«««««««««««««««¬¬¬¬¬­­­¬¬¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­­¬¬­¬®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­¬­¬­¬¬­¬­¬­¬­­­­­­­­­¬¬­­¬¬­¬­¬¬¬¬¬¬­­¬­­¬­¬¬­¬¬­­­¬­®­¬¬¬¬­­­¬­¬­¬­¬¬¬¬­¬¬­¬¬®®®®®­®®®­­­¬¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬­¬®®®¬¬­­¬¬«««««««¬«««««««««­¬¬­­­¬¬¬¬­­¬¬¬­¬¬¬­¬¬¬¬­¬¬­®¬¬®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­®­­­­­­­¬­­­­¬­­­¬­¬­­­­¬­­­¬­¬¬¬­­­¬­¬¬¬­¬¬¬¬­¬¬¬¬­¬­¬­¬¬¬­­¬¬¬¬¬­¬­­­®®®®®®®®®¬­­­¬¬­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬­­­­¬¬­¬¬««««««««¬«««««««««««««««¬¬­­¬¬­­¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬­­­­®¬­­­®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬­­­­­­­­¬¬­¬­¬¬­¬­¬­­­­¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬­¬­­¬¬­¬­¬­¬­¬¬­¬¬¬¬­¬¬­­­­®®®®®®­­¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬¬®­¬®­­­¬¬¬¬««««««««««¬«««««««««¬««««¬¬¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­­­¬¬¬­­®®¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­¬¬¬¬¬¬­¬¬¬­­­­­­¬­¬­¬¬¬¬­¬¬¬¬¬­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬­­­®­­­®®®®¬­¬®¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬­­­¬¬®­¬¬¬««««««««¬««««««««««««««¬­¬­­¬­¬¬­­¬¬¬¬¬¬¬­¬¬¬­­­¬­­®­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬­­­¬¬­­¬¬­­­­¬­¬¬­­­¬¬­¬¬­¬­¬­¬¬­¬­­¬­¬¬­­¬­¬¬­¬­¬¬­¬¬­­¬¬¬¬­­¬¬­¬­¬­¬¬­­®­®®®®®®®­­­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­­¬­­¬¬¬««««««««««««««««««««««««««««¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­­¬­­­­­­­¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­­­­­­­­­­¬¬­¬¬¬¬¬­­¬¬­­­­¬¬¬­­¬¬­¬¬­¬¬¬­­¬­¬¬¬¬­­­¬¬­¬¬­¬¬¬¬­­¬¬¬­¬­¬¬­­¬¬­¬¬¬¬­¬¬¬¬­­®®®®®®®¬­¬¬­­­¬¬¬­¬¬¬­¬­¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬««««««««««««««¬¬¬¬­®­¬¬­¬­¬¬¬¬­¬¬­­¬­¬¬­­­­­­­¬­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬­­­­­­¬­¬¬¬­¬­¬­­¬¬­­­­¬­¬­®­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬­¬¬­¬­¬¬­¬¬­¬¬­¬¬­¬­­­®®®®®®®¬¬¬­¬­­¬­¬¬¬¬­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬­­¬¬­¬¬­¬¬¬¬«««««««««««««««««««¬¬¬¬¬®­¬­¬­¬¬¬¬­­¬¬¬¬¬­­¬­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®1¿¿¿1¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­®®®­­­®­­­­¬¬¬¬¬­¬­­­­¬­­­¬¬­­¬­¬­­­­¬­¬¬¬¬¬­­¬¬­¬­¬¬­¬¬¬¬­­¬¬¬­¬¬¬­¬­­¬­¬­¬¬­¬¬­¬¬¬¬­®®®®®®¬¬­¬­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬­­­¬¬­¬¬­­¬­«««««¬««««««««««««««««««««««««««¬¬¬­­¬­¬¬¬¬¬¬¬¬¬¬­­¬¬¬­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­®­¬¬­¬­¬­­­¬­­­¬­¬¬­¬­¬¬¬­¬¬¬¬­¬¬¬­­¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­­¬¬­¬¬¬¬­¬­¬¬­­­¬­®®®®­­¬­®®®®¬­¬¬­­¬¬¬­¬¬¬¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­¬­­­­­¬¬¬¬«««««««««««««««««««««««««¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­®­­­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­¬­­­­¬¬­­­­¬¬¬¬¬¬¬¬­­­­­¬­­­­­­¬¬­¬­¬¬¬¬¬­­¬¬®­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬­¬­­­®®®®­­¬­­®®®¬­¬¬¬­­­¬­¬¬­¬¬­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­­¬«««««¬¬¬««««««««««««««««««¬¬­¬­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬­­¬­¬­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­®­­­¬­­­¬¬¬¬­­¬¬­¬¬­­­­¬¬­­­¬­¬¬¬­¬¬¬¬¬¬­¬­¬­¬­­¬¬­¬¬­¬¬¬¬­¬¬¬­¬­¬¬¬¬­¬¬­¬¬­¬­¬­®®¬¬­­®®­­­­¬¬¬­­¬­¬¬¬­¬­¬¬­­¬­¬­¬­¬­¬¬¬¬¬­¬­¬¬­¬¬¬¬««««««¬«««¬««««««««««««««««¬¬¬­­¬¬¬¬¬¬¬­¬¬¬¬¬­­­­­­¬­­­­¬¬­®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®¿1¿¿¿¿¿¿¿1®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®¬¬­­®­­­­¬­­­­¬¬­¬¬­­¬¬­­­¬­­­­¬­­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­­­­­¬¬­¬¬¬­¬­¬­¬­¬¬¬­¬­¬¬¬¬­¬­¬¬­¬­¬­®®®®®®­­­¬¬­­®®­­­¬­¬­¬­¬¬¬¬¬­¬¬­¬¬¬¬¬­¬­­­¬¬¬­¬­¬¬¬««¬«««««««««««««««««««««¬¬­­­­­¬¬¬¬¬­­¬¬¬­­¬¬­¬¬­­­­­­¬¬­­­­¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­­­­¬­­¬¬¬¬¬¬­­­­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬¬­®¬­­¬¬­¬¬­¬¬­¬¬­¬®®®®®­¬¬¬¬­®¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬­¬­¬­¬«««««««««««««««««««««¬¬¬­­¬¬¬­¬¬¬¬¬­¬¬¬¬¬­­¬¬­­­­­­­¬­¬­¬¬­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­¬¬¬¬¬¬­­­¬­¬®®®­¬­¬¬¬¬¬¬¬¬­­¬­¬¬¬¬¬¬­¬­­¬­¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬¬®®®®­¬®¬¬­¬­¬¬®¬­­­­¬¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬««««««««««««««««««¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬­­¬¬­­­­­­­­­­­­­¬¬­­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®­­­­­­­­­­¬¬­¬¬¬­¬¬­¬­­­¬­­¬­­­¬¬¬¬¬¬­¬­¬­­¬¬¬­­¬¬¬­­¬¬¬¬¬¬­¬¬­¬¬­­­¬¬¬­­®¬­­­¬¬¬­­­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««¬¬¬¬¬­­¬¬¬­¬¬­¬­¬­¬¬¬¬­¬­¬­¬­­­¬­­­­¬­¬¬­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬­¬­­¬­¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­­­¬­­­­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­­­­­­¬­®­¬­¬¬­¬­¬­­­­¬®­­¬¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬®¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬«««««««««««««««¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­­¬¬¬­­­­­­­­¬¬­¬­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬­¬¬­­¬­­¬­¬¬¬¬¬¬¬­¬­­¬¬¬­¬¬­­¬­­¬­­­¬­¬¬­¬¬­¬¬¬¬¬­­¬­­­¬­®­­¬­¬¬¬­­­­­¬®®­­¬­¬¬­¬¬­­­¬­¬­¬¬­¬­¬¬¬¬¬¬¬¬­¬¬­¬¬¬­­¬­­¬¬­¬¬­­¬¬­­¬¬¬¬¬¬¬««««««««««««««««««¬¬¬­­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­¬¬¬­­­­­­­­¬­¬­­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­¬­­­¬­¬­­¬­¬¬­­¬¬­¬¬¬¬¬¬¬­¬¬­­¬¬¬¬¬¬­­¬­¬­­¬­­¬¬­¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬®­®®­­­¬¬­¬¬­¬­®­¬­¬¬¬¬­¬¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­¬¬¬¬«««««««««««««««««««¬¬¬¬¬­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­­­­­­­­­­­­­®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­­­­¬­­­¬¬­¬¬¬­­¬¬­­¬¬¬¬¬¬¬­¬­¬¬­¬¬¬¬¬­¬¬¬­¬­¬­¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬¬­®­¬­­­¬­¬¬­¬¬¬¬­­¬¬¬­­­¬¬¬­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬¬¬­­¬¬¬¬¬­¬¬­¬¬­¬««««««««««««««««««««¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬­­­¬¬­­­­­®­­®®­­­­¬­­¬¬¬¬­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®­­­­­­¬­­¬­¬¬­­¬¬­­¬­¬¬¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬¬­¬­¬¬­­¬­¬¬­¬­¬­¬¬­¬­¬¬­­­­¬­­¬­¬­­­¬­¬­¬¬­­­®­­¬¬­­¬­¬­­­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬­­¬¬««««««««««««««««««¬­¬¬¬­¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­¬­­­¬­­­­®­­­¬¬¬­¬­­®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬­¬­¬­¬­¬­¬­¬¬­­­¬­¬¬¬¬¬­¬­­¬¬¬­¬­¬­¬­­­¬¬­¬­¬¬­­¬­¬­¬¬­¬­¬­­­­­¬¬¬¬¬­­­¬­¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­¬¬¬«««««««««««««««««««««««««¬¬­­¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬­¬­­­­­¬­¬¬­­­­­¬®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­­­­­­­¬¬­¬­¬­¬¬¬¬¬­¬¬­­­¬¬¬¬¬¬¬­­¬¬¬¬¬¬­¬­­­¬¬¬­¬­­­­¬¬­¬¬­¬¬­¬­¬¬­­­­­­¬­¬¬¬­®­­¬¬­¬­¬¬­­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬¬­¬¬¬¬¬¬¬««««««««««««««««¬¬­­¬­¬¬¬­¬­­­¬¬¬¬¬­¬­­­­­­­­­­­­­­­­­­®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬­¬¬­­­¬¬¬¬­¬­¬¬¬¬¬¬­®­¬­¬¬¬¬¬¬¬¬¬­­¬­­­¬¬­­¬­¬­¬­­­­­­¬­¬¬¬­­¬¬¬­­¬­­­­¬­¬¬­­­­­­¬¬­¬¬¬¬¬¬¬¬¬­­¬¬­¬¬¬­­­­­¬¬­¬­¬¬¬¬¬«««««««««««««««««««««¬¬­¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­­¬¬­¬­­­­­­­¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­­¬¬­¬¬­¬¬­­¬­¬¬­¬¬¬¬­­¬¬¬¬¬¬¬­­¬¬­¬­¬­­¬­¬¬¬¬¬­­¬¬­­­¬®­­­­¬¬­¬­­­¬¬¬¬¬¬­¬¬­¬¬¬­¬¬­­­¬¬¬¬­¬¬¬¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬«¬¬«««««««««««««««««¬¬­­­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­­­¬¬¬¬¬­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­­¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬¬­­¬¬­¬­¬¬¬­¬®­¬¬­­­­­­­­¬¬­®­¬­­­­¬­¬¬­­¬­¬­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬¬¬¬­¬¬­¬­¬¬­¬¬¬¬¬¬«««««««««««««««««¬¬¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­¬­®­­®®­­­­­­®®®®®®®®®®®®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­¬­¬­­¬¬¬¬­­­¬¬­¬­­­­¬¬¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­¬­¬­¬¬­¬­¬­¬¬­®­¬­­­­¬­­­¬¬­­­­­­­¬­­¬¬­­¬­¬­¬¬­­­¬­¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬««««««««««««««««««««¬¬­¬­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬¬¬¬­­­®¬¬¬¬¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬­¬¬­¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬­­­­¬¬­¬¬¬­¬­­­¬­­­­­¬¬­¬¬¬­¬¬¬¬­¬­­¬¬¬­­¬­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­­¬¬¬¬¬¬­­­¬¬¬¬¬¬¬««««««««««««««««««««¬¬­­¬­¬¬¬­¬¬­¬¬¬¬¬¬¬­¬¬¬­­¬­­¬­­­­®®®­­­¬®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­¬­¬­¬­¬¬­¬¬¬¬­¬¬­¬­¬¬­­­¬¬¬¬­¬­­­¬¬¬­¬¬¬­­¬­¬¬¬¬­¬¬­­¬­¬¬­®­­­­­­­­­¬¬­¬¬­¬¬­¬­­­­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬­­­­­­®®­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­¬­¬¬¬­¬¬­¬­¬­¬­¬­¬­­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬¬­¬­¬­­­­­­¬¬­¬¬¬­­­­­¬¬­­­­¬­¬¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬­¬­¬­­¬¬¬¬¬¬¬«««««««««««««««««««««¬¬¬­¬¬¬¬¬¬¬­­­¬¬­¬­­­¬¬¬­­­­­­­­­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­¬­¬¬­¬¬­¬¬¬¬­¬­­­¬­­¬¬¬¬­­¬¬­¬¬¬¬¬­­¬­¬¬­­¬­¬¬­¬¬¬­­¬­­®­­­­­­­­­­®­­­­®­¬­­¬¬­­­­¬­­­­¬¬¬¬¬­­¬­¬¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««¬¬¬¬­¬¬­¬­­­­¬¬¬¬­¬­­¬®­®®­®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬­­­­¬­¬­­¬­¬¬­¬¬¬¬¬­¬­­­¬¬¬­¬¬­¬­­­­­­­­­­­­­­­¬­­¬­­­¬­­¬­­­¬¬­­­­¬¬­¬¬¬¬¬¬¬­¬­¬¬¬­¬¬­¬­­¬¬¬¬¬¬¬¬­¬­­¬­¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬­¬««««««««««««««««¬¬­­­¬¬­­­­­­­­­¬­­­­­¬­¬­­®®®®®®®®®®®®®­­­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®­­¬­­­­¬¬¬¬­¬¬­¬¬­­¬­­­¬­¬¬¬¬­­¬­¬¬¬¬­¬¬¬¬¬­­­­­¬­­¬­­­®­®­­­®­­­­­­­­­­­­­®®¬­¬­¬¬­­®­­­¬­¬¬¬­­­¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬¬­¬¬¬­­¬¬¬¬­¬¬¬¬¬¬«««««««««««««««««¬¬¬­¬­¬¬­¬­­­­¬¬­¬­¬­¬¬­­­®®­®®®®®®®®®®®®­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­¬­¬­¬¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬¬­­­®®­­®®­­­­­­­­­¬­­®­¬­¬­¬¬­­¬¬­¬­­¬­¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬­­¬¬¬­¬¬­¬­­­¬¬¬­¬¬­¬¬­­¬¬¬¬­­­¬¬¬¬««««««««««««««¬¬­­¬­¬¬­­­­­­­­¬¬­­­­¬­®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­¬¬­¬­¬¬­¬¬¬­¬­¬¬¬­¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬­¬¬¬­­¬­¬­¬­­­®­­®®®®­­­­­­­­­­­­¬­­®®¬­¬¬­¬®®®®®­­­­¬¬­¬¬¬¬¬¬­­¬¬­¬­­¬¬­­¬­¬¬¬­¬¬¬¬¬¬­­¬¬¬­¬¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬«««««««««««««««¬­­­¬¬­¬­­­­¬­­­¬­­­­­­­­­­­­­®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®­­¬­¬¬­¬¬­¬­¬¬¬­¬¬­¬­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­­¬¬­­­­®®®­­­­­­¬­­®¬­¬¬­®­¬­¬­¬¬­­¬­¬¬¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­­¬­¬­­¬­­­¬¬¬¬­­­­¬¬¬­­¬¬¬­¬««««««««««««««««¬­¬­¬¬­¬¬¬¬­­­­­­­­­­­®¬­­­­®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬¬­­­¬­­­¬­­­­¬¬¬¬¬¬¬¬¬¬­¬­¬­­­¬¬¬­®­­­­­­­­­­­­­­­®®®®­¬¬¬¬­¬¬­¬¬¬¬­¬¬­­¬¬­­¬¬¬¬¬­¬­­¬¬¬­­¬­­¬¬¬¬­¬¬­¬¬­¬­¬­­¬¬¬¬¬¬¬­¬¬¬«««««««««««¬­­¬¬¬¬­¬­­­¬¬¬¬¬­­­­­­­®®®­­­®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®¬­­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬¬¬­¬­­¬¬¬¬¬¬­¬­­­¬­¬¬¬­­­®­­­­­­®­­­­­­¬¬­¬¬­¬­­­®­¬­­¬­¬¬¬­¬¬­­­­¬¬­¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬­¬­¬­­­¬¬¬¬­¬¬¬¬¬««««««««««««¬­¬¬¬®¬¬¬¬­­­­­¬¬¬¬­­¬­­®®®­­®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®¬­¬¬­¬¬­¬¬¬¬¬­¬¬­¬¬­¬­¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬­­­¬­­­­­­­­®­­­¬­­­­­¬¬¬­®®­­­¬¬­¬¬¬¬¬­¬­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬«««««««««««««¬¬­¬­­­¬­¬¬­­­­­¬­­­­­®®®®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­¬¬­¬¬­¬¬­¬¬­¬¬­­¬¬­¬¬¬­­¬¬­­¬­¬¬¬­¬­­¬­­¬¬¬¬¬¬­­­­­­®®­¬­­®­­¬­­¬­­­­­®®®­¬¬­¬¬¬¬­¬­¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬­¬¬­¬¬¬¬¬¬¬¬­««««««««««««««¬¬¬¬­¬¬®¬¬¬¬­­¬­­®­­­­­­®®®®®®®®®®®®®®®®®®­®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­­¬¬­­¬¬­¬¬¬­­­¬­¬­­¬¬­¬¬¬­­¬¬­­­­¬­­­­­­­­®­¬­®¬¬¬­¬­¬­­­®®®­­­¬¬­¬­¬¬­¬¬¬¬¬¬­¬­­¬¬¬­¬¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬­¬­¬­¬­¬¬¬¬­¬­­¬¬­­¬¬¬¬¬¬«««««««««««««¬¬­¬¬­¬­­¬¬­¬¬­¬¬­¬¬­®­®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­¬¬­­¬¬­¬­¬¬­¬¬¬­¬¬­­¬­¬­­­¬¬¬¬­­¬¬­­¬¬­­¬­¬­®­¬­®­¬¬¬¬­¬¬¬¬­­­­®®­­­­­¬¬­­¬­­¬­¬¬­¬¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬­­¬¬¬¬¬­¬¬­¬¬¬¬¬¬««««««««««¬¬­¬­­¬®¬¬­¬­­­­­¬¬¬­­­­­­®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬¬¬­¬¬­¬¬­­¬­¬­­­­­¬­­¬¬­¬­¬¬¬­¬­¬¬­¬¬¬¬¬­¬­¬¬¬¬­­®®®®®®®®®­®®¬­¬­­­¬­­¬­¬¬­­­­®®®®­­¬¬­¬¬­­¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬­­­¬¬¬­­¬¬¬¬¬­¬¬¬«¬«««««««««««­­¬­¬­¬¬­­­¬¬­¬¬¬­­¬¬­®®®­®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬­­­¬­¬¬¬¬¬¬­¬­¬­¬­¬­­¬¬­­¬­­¬¬¬¬­¬¬¬¬¬­­­­­­¬­­­­®®®®®®®®®®®®­­­¬®¬®®®®®­¬­­­¬­­­­­®­­­­¬¬­­¬¬­¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬¬­¬¬¬¬­¬­¬­¬¬­¬­¬¬­¬­­¬¬¬­­­¬¬¬¬¬¬¬¬¬«¬«««««««««««­¬¬¬­­­¬­¬­­¬¬¬­¬­­­¬­­­­¬­®®®®®®®®®®®®®®®®­®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬¬¬­¬¬¬­¬­­¬¬­­¬­¬¬­¬­¬­¬­¬¬¬¬¬­­¬­¬¬­¬¬­¬­¬¬­­­®®®®®®®­®­¬®®­¬­­­­­­®®­­¬­¬¬­­­­­¬¬­­¬­­¬¬¬¬¬¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬­¬¬¬««««««««««¬­¬¬­¬¬­¬­­­­¬­­¬¬­­­­®­®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­¬¬­¬­¬¬­¬­¬­¬¬­¬­¬¬¬¬¬­­­¬¬­¬¬¬¬¬¬­¬­¬­¬¬¬¬­¬¬­­­¬¬­­­®®®®®®®®®®®®®­­®­­­¬­­¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬¬¬­­­¬­¬¬¬¬¬¬­­¬¬¬¬¬¬¬­¬«««««­­­­¬¬¬­­®®®®­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬¬­¬¬¬­¬¬¬­¬­­¬¬­­­¬¬¬­¬¬­­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬­­¬­¬­¬­®®®®®®®®®®®®®®®®­®®­­­­­­­­¬­¬­¬¬­­­­­¬¬­­­­¬­­­¬¬¬¬­¬¬¬®¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­­­­¬­­­¬¬­¬¬¬­¬¬¬¬­­¬­¬­­­­¬¬¬««¬««¬­¬¬­¬¬­¬®¬­­®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­¬¬¬­¬¬­­¬­­¬­­­¬­­­­¬­­­¬­­¬­¬­¬¬¬­¬¬¬¬¬­¬¬­¬­­­­¬®®®®®®®®®®®®®®­­­®­­®®¬¬­­­­­­­¬­­¬¬¬¬¬¬¬¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬¬¬­­¬­­­­­­­­¬­¬­¬¬­­¬­¬¬¬¬¬¬­¬¬¬¬¬«¬««««««¬¬¬­¬­¬¬®¬¬®®®®®®®®®­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬¬­­¬¬¬­¬­­¬¬¬¬­­¬¬¬­­­­­¬­¬¬­¬¬­­¬®®¬­¬¬­¬­¬¬­¬¬¬¬­¬¬®®®®®®®®®®®®®®­®®®®®®®­¬­­­­®­­¬­­­­­­¬¬¬­­¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬­­­­­­­¬­­­¬­¬­¬­¬­¬¬¬¬¬­¬­¬¬¬¬««««««¬¬­¬¬¬¬®­®®®®®®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­¬­¬¬¬¬­¬¬­¬­­¬¬­­¬­­­­­¬¬­¬­¬­­¬¬¬¬®®­¬¬­¬­¬¬¬­¬­¬¬­®®®®®®®®®®®®®®®®®®­­­­­­­­­®­­¬­­¬­­­­­¬¬­­­¬¬­¬­¬­¬­¬­­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­­­­­­­­­­¬­¬¬­¬¬­­¬¬¬¬­¬¬­¬¬¬¬¬¬««¬­¬¬¬¬­­®­¬¬­®®®®®­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­­¬¬­¬¬­¬¬¬¬­­­¬­­­¬­­­­¬­¬­­®®®¬¬¬¬­¬¬¬­¬¬¬­¬®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬­­¬¬¬¬¬­­­¬¬¬­­¬¬¬­­¬¬¬¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬­¬­¬¬­­­­­­­­¬¬­¬­¬¬­¬¬­¬¬¬¬­¬­¬­¬¬¬«««¬¬¬¬¬¬­­­­­­­®®­¬­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬­­¬¬¬¬­¬­¬­¬¬­¬­¬¬¬­¬­¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬¬­­¬¬­®®®®®®®®®®®®®®®®­®­¬¬­¬¬­­­¬¬¬¬­­­­­¬¬­­­¬­­¬¬­­¬¬¬¬­¬­¬¬¬¬¬¬­­­­­¬¬­¬¬­­­¬­­­­­¬­¬¬­¬¬¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬««««¬¬¬­¬­­­­­®¬¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­¬¬­­­­­¬¬­¬¬¬¬­¬­­¬¬¬­­­¬¬­¬­­¬¬¬­¬­¬­¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬¬­¬­®®®®®®®®®®®®®®®¬­­­­­­­­­­¬¬¬¬¬¬¬¬­¬¬­¬¬¬¬­­­¬¬­¬­¬¬¬­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­¬¬­¬­­­­­­­­¬­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬¬«««««¬¬­¬­¬­®¬­­¬­­­­­¬®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬¬¬¬­­¬¬¬¬­¬­¬­­­­¬¬­¬¬¬­­¬­¬­¬¬­­¬¬­¬¬­¬¬¬­¬­¬¬¬¬­­¬¬­¬¬¬­¬®®®®®®®®®­®®¬¬­¬­­¬¬¬¬¬¬­¬­¬¬­­­­¬¬­¬¬­¬¬­¬­¬­­¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬­¬¬­­¬­­­­­­¬­¬¬¬­¬¬­¬­­­¬¬¬¬¬­¬­¬¬«««¬¬¬¬¬¬¬­¬®¬­­­­­­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­­­­­¬­¬¬­¬¬­¬¬­¬¬¬¬­­­­¬­¬¬­­­¬¬­­¬¬­¬¬¬¬­¬¬­¬­¬¬¬­­¬¬®®®®®®®®®®®­®­­¬¬­­­­¬¬­­­¬¬¬¬¬­­¬¬¬­¬­­¬¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­¬¬­­¬­­¬­­­­¬­¬¬¬­¬­¬­¬­¬¬¬¬¬­¬¬¬¬«««««««¬¬¬¬­¬­­­­­­­­¬­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­­¬¬­¬¬¬¬­¬¬­­¬­¬¬­­¬­­­­­­¬¬­¬­¬­¬¬­­¬¬¬¬¬¬­¬­¬­­¬¬¬¬¬®®®®®®®®®­­­®®­­­­­­­­­¬­¬¬­¬­­¬¬­­¬¬¬¬­¬¬­¬­¬¬­­¬¬¬¬¬¬¬­¬¬­¬¬­­¬¬¬¬¬­¬¬­­­¬¬­­¬­¬­­¬­¬¬¬¬­­¬¬¬¬¬¬¬¬«««««««««¬¬¬­¬¬¬¬­¬¬­¬¬¬­­®®®®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬¬­¬­¬­¬­¬­¬¬¬¬­­­­¬¬¬¬­­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­¬®®®®®®¬¬®®®®­­®­­­­­­­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬¬­­¬¬¬­­­­¬­­­­¬¬¬­¬¬¬¬¬¬¬¬¬««««««««««¬­¬¬­­¬¬­¬­¬¬®­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­¬¬­¬¬­­­¬¬¬­­­­¬¬­­­­­­­¬­¬¬¬­¬­­¬¬­¬­¬¬­¬¬¬­¬¬®®®®®®®¬¬®®­­­¬­­­­­­­­¬¬­­­­­¬­­­­­¬¬¬¬­¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­­­¬¬¬¬­¬¬­­­­­­­­¬¬­­¬¬¬¬¬¬­¬¬¬¬¬¬¬¬««««««««¬¬¬¬¬­¬­¬¬­¬¬­­¬­­­®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­¬¬¬¬­¬¬¬­­¬¬¬­­­¬­­¬­¬¬­­¬­¬¬¬¬­¬¬­¬¬­¬¬¬­­­¬­¬¬®®®®®®¬¬®­­¬­­­­¬¬­­¬¬­¬­¬¬­­­¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬­¬¬­¬­­­­­­­­­¬­­­¬¬­¬­¬­¬­¬««««¬¬¬­¬¬¬¬­¬¬¬¬¬­­¬¬­­­­®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬¬­¬¬­¬¬¬¬¬¬¬¬¬­®¬¬­¬¬­¬¬¬¬­¬­­¬¬­­¬­¬­­¬¬­­¬­¬¬®®®®®®®®®®­­®®®­­­­­­¬­­­¬­¬¬¬­­­¬­¬¬­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬¬­­­­­­­­­­­¬¬­­¬­¬¬­¬¬¬¬¬¬«««««««¬¬¬­¬­®¬¬­¬¬¬­­¬­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬­­®­­­¬¬­¬¬­¬­¬¬­¬¬¬­¬­¬¬­¬­¬¬¬¬¬­­¬­­­­­­¬¬¬­¬¬­¬¬­­¬®®®®®®®®®­­­®®®®­­­­­­­¬®­­­­­­­­­­­­­¬¬­¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­­­­­­­­­­­¬¬­¬¬­¬­­¬¬¬¬¬¬«««¬¬¬­­¬¬¬¬¬­¬­­¬¬¬¬®­¬¬­­­­­­­­­­­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®¬¬­­¬­¬­¬¬¬¬­¬¬¬­¬¬­­­¬¬­¬­­¬­¬¬­¬¬¬­¬­­¬­¬¬­¬¬­­­­¬¬¬­®®®®®®®®®­­®­®­®®­¬­­¬­®®­¬­­­­¬¬­­¬­¬­­¬­­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬­­¬¬­¬¬¬­¬¬¬¬­­­­­­­­¬¬­¬­¬­¬­¬¬¬¬¬¬«««««¬¬¬¬­¬¬­¬­®­¬­­¬­­¬­­­¬­­­­¬­­­®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬­¬¬¬¬­¬­­¬­¬­¬¬­­­¬¬¬­¬¬¬­­­­¬¬­¬¬¬¬¬­¬®®®®­­­­­­­®®­­­­­­­­­­­­­¬¬­¬¬­¬¬­­¬­­¬¬­­¬¬­¬­¬­¬¬­¬¬¬­¬¬¬¬¬¬¬­¬­­¬­¬­¬­­­¬¬­¬¬¬¬¬­¬¬¬¬¬­««¬¬¬­¬­¬¬­¬¬­¬¬¬¬­¬­­­¬¬¬­­­­­­­­®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬¬¬¬­¬¬¬¬­­¬­¬­¬­­¬¬¬­¬­¬­¬­­­¬­¬¬¬¬­¬­¬¬­¬¬­¬¬¬­¬®­­­­­¬­­­­­®­­­­­­®­­­­­­­¬¬­¬¬¬¬­¬¬­­¬¬­¬¬¬­­­¬¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­­­­¬¬­­¬¬¬­­­­­¬¬¬¬¬­¬¬«««¬¬¬¬¬¬­¬¬­¬®¬¬¬¬¬¬¬¬¬¬­®­­®®®®®®1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬®­­¬¬¬¬¬¬¬¬¬¬¬­­¬­¬­¬¬­­­­¬¬­¬­¬­¬¬­¬­¬¬¬¬¬­¬­­­¬¬­¬®®®®®­­¬­­­®­­®­­­¬­­­­®­­­­­­­¬­¬­¬­­¬¬­¬­­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­­¬¬­¬¬­­¬¬­¬¬­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬­­¬­­­­­¬¬­­­®­®®®®¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬®¬¬­¬¬¬­¬¬¬­¬¬­­­­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­¬¬¬­®®®®®®­¬­­­®®­¬­¬­¬­­­­­¬­¬­­­­¬¬­¬¬­¬¬¬¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬¬­­­¬¬¬­¬¬¬­¬­¬¬­­­­­¬¬¬¬¬¬¬¬¬¬«««¬¬¬¬¬¬¬­¬¬¬­¬­­¬®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®¬¬­­¬­­­­¬¬¬¬¬­¬¬¬¬¬¬­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­¬¬¬¬­¬¬¬¬­¬®®®®®®­­­­­­­¬¬¬­­®®®®­­­­­­­¬­®­­­­­¬­¬¬¬¬­­­¬­­¬¬¬­¬¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬¬¬¬­¬¬¬¬­­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬««¬¬¬¬­¬¬­¬­¬­­­­­¬®®®®®®®¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬¬¬­­¬­­­­¬¬­­­­¬­¬­¬¬­¬­¬­¬¬¬¬­­­¬¬¬¬­¬¬­­¬¬¬¬¬¬­¬¬¬­®®®­­­­­¬­­­¬­®®®®®®­­­­­­­­­­­­­­­­¬¬­¬¬¬¬­­¬­¬¬¬­¬¬¬­­­¬¬­­¬­¬¬­¬¬¬­¬¬­¬¬¬¬­¬­¬¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬¬­¬­¬­¬­®­®­­®®®®®®®®®¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬­­¬¬¬­¬¬¬¬¬­­¬¬­¬­¬¬­¬­¬¬­¬­¬¬¬¬­¬¬­¬¬¬¬¬­¬¬¬¬­¬®®­®¬¬¬¬­®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬­¬­­­­¬­¬¬¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬¬­¬¬­­­¬¬­¬¬¬¬­­¬­­¬¬­¬¬¬¬¬¬¬¬¬«¬¬¬¬¬¬¬¬­¬­­¬¬¬­­®®®®®®¿1111®­¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬¬¬¬­¬­¬­­¬¬­¬¬­¬¬­¬¬¬¬¬¬­­¬¬­­¬­¬­­­¬­¬¬­¬¬¬¬­¬¬¬®®®®®­­­­­­¬¬¬¬®®­®®®®®­­­­­­­­­­­­­­­¬¬­¬¬­¬¬­­¬¬­¬­¬­¬¬¬¬­¬­¬¬¬¬¬¬­¬¬¬­¬¬­¬­¬¬¬­¬­¬¬­­¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬­­¬­®®®®®®®®®111111111111®¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­¬¬­­­­¬¬¬­­­¬¬­¬­¬¬­¬¬­¬¬¬¬¬¬­¬¬¬­­¬¬­¬¬­­­¬­¬­¬¬­¬­¬­­®®®®®­­¬­¬¬­­­­®®®­¬­­®­­­­­­­®­­­¬¬¬¬¬­¬¬¬­­­­¬­¬¬­¬¬­¬­¬­­­¬­¬­¬­­­­¬¬¬­¬­¬¬­¬­¬¬¬¬­¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­¬­¬­®®®®®®®®®®®®®®®®®111111111¿1111111111111®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬­¬¬­¬­­¬¬­¬­¬¬­¬­­¬¬­¬­­¬­¬­­¬¬¬­­¬¬­­­¬­¬¬¬®®®®­­­­¬¬¬­­®®­­­­­­­­¬­­­­¬­¬¬­¬¬­¬­¬­­¬¬¬­­¬¬­¬¬¬¬­­¬­­¬¬­¬¬­¬¬­­¬­­¬­­¬­¬¬­¬­­¬¬­­¬­­¬¬¬¬¬¬¬­­¬«««¬¬¬¬¬¬¬­¬¬­­­¬¬¬­®1111111111111¿11¿111111111111111111111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­¬­¬¬¬­­¬­¬¬­¬¬­­­­¬­¬¬¬­­¬­¬­¬®­¬­¬­¬¬­¬¬¬­®®®­­­­­­­­¬¬¬­­¬­®®®®­­­­­­­­¬¬­­­¬¬¬¬¬¬­¬­¬­­­­­¬¬¬­¬¬­­¬¬­¬¬­¬¬¬¬­¬¬­­­¬¬­¬¬¬¬¬­¬­¬­­­­­­¬¬¬¬¬­¬¬­¬¬«¬¬¬¬¬¬¬¬­¬¬­­¬¬¬­11111111111111111111111111111111111111111111111111111®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­¬­¬­¬­­¬¬¬­¬­¬¬­¬¬­­­­®­­¬­¬­¬­¬­¬¬®¬¬­­¬¬¬¬­­¬¬­¬¬­­­­­­­­­­¬­­­®®®®®®­­­­¬­­­­­­­­­¬­¬¬­¬­¬­¬¬­­¬­¬¬­¬¬¬¬¬¬­¬­¬¬­¬¬¬¬­­­­¬¬¬¬­­­¬¬­¬¬¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­­¬¬­­­­­­­111111111111111111111111¿111111111111111111111®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­¬­­®¬­­­¬­¬­­¬¬­¬¬­¬¬¬¬¬¬¬¬­®­­­¬­¬¬­¬¬¬¬­¬­¬¬¬¬­­­¬­¬¬­¬®®®®­­­­®­­¬¬¬­¬®®­­¬­­­­­­¬¬­­­¬­¬­­­¬¬­¬­¬­­¬­¬¬¬¬¬­¬¬­¬¬¬­¬¬­­¬­­­­­¬­¬¬­­¬¬¬­­¬¬­¬¬¬­­¬¬¬­¬¬¬¬¬¬¬­¬¬«««¬¬¬­­¬¬­¬¬¬¬­¬¬­®11111111111111®®®¿¿11111111111111111111®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­¬¬¬­¬¬­¬¬­¬¬¬­¬­¬¬­¬®¬¬­¬¬¬­¬¬­¬¬¬¬¬­­­­¬¬¬¬¬¬¬®®®®­­­­­®­­­­¬¬¬­­¬­¬­­­­­­­­­­¬­¬¬­¬­¬­­¬­­¬¬¬­­¬¬­¬¬¬­­­­¬¬¬­¬¬­¬¬¬­¬¬¬¬¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­­­­¬¬¬¬¬¬¬­¬¬¬¬¬¬«¬¬¬¬¬¬­¬­¬¬­­­­11111111111111®®®®®®®111¿1111111111111111111®®1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­¬­­¬­¬¬­­¬¬¬­¬¬­­­¬­¬­¬¬­­­­¬¬­¬­­¬¬­­®¬¬¬¬¬¬­®®®­­­­­¬­¬­®®®­­­­¬¬­­­­­­­­¬¬¬¬­¬¬­¬¬¬¬­¬­¬¬­¬¬­¬­¬­­¬¬­¬¬¬¬­¬­¬­¬­­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬¬­¬¬¬¬¬¬¬¬«¬¬¬­¬­­­­¬­1111111111111®®®®®®11111111111111111111111111®®¿¿¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­¬¬­¬­¬¬¬¬¬¬­­­­­¬¬­¬¬­¬­¬­­­¬­­­­¬­¬­­®¬¬¬¬­­¬­®®®­­­­­¬­¬­¬­­­¬­­­­­¬­­­­­­¬­­¬­­¬¬­¬¬¬­­¬¬­­¬­¬­¬­¬¬­¬¬­¬­¬¬­­¬¬­­­­­¬­¬­­¬­¬¬­¬¬¬¬¬­¬­¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬««¬¬¬¬¬¬¬­¬­­¬­¬11111¿11111111111®111111111111111111111111111111®®®¿1¿¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®®®®®­®®®­­®®®®®®®®®®®®®­­­­­¬­¬­¬¬­¬¬­¬­¬­¬­¬¬­¬­¬¬­¬¬¬­¬­­­­­¬­­®®­®®®­®®­­­­­­¬­¬¬­¬¬­¬­­­­­­­­­®­­­­­­­­­­¬¬¬¬¬­¬­¬¬¬­­­­¬¬¬­­¬¬­¬¬¬¬­¬­­¬­­¬­¬¬­¬¬¬­¬­­¬¬­¬­¬­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­¬­­­­1111111111111111111¿111111111111111111111111111111111®®®¿11111¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®­®®®®®®®®®­®®­®­®®®®®­­®®®®®®®®®­­¬¬¬­­¬¬­¬­­­­¬­¬¬­¬¬¬­¬¬¬¬­¬¬­­¬­¬­¬­­¬®®®®®®®®­­­­­­­­­­¬¬­¬¬®­®­­­­­®­­­­­­­­­­­­­­¬­­¬¬­­­¬¬­­¬­¬­­¬­¬­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬¬­­­¬¬¬­¬¬¬¬­¬¬¬¬¬¬¬­­­¬­¬®111111111¿111111¿11¿11111111111111111111111®11111111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­®®®®®®®­®®®®®®®®®®®®®­­®®®®®®®®®®®­­­­­­­­®­¬­­­¬¬¬­¬­­¬¬¬­¬¬­­¬¬­¬¬¬¬¬­­­¬¬¬¬¬­­­¬­®®®®­­­­­­­­­®®­®­­­­­­­­­­­­­¬¬¬­­¬¬­­¬­­¬­¬­¬­­¬¬­¬¬­¬¬¬¬­¬¬­¬­¬­¬¬¬­¬¬­¬­­­¬­­¬¬¬¬­­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­¬­­­®®11111®®®11111111111111111¿1111111111®¿11¿11¿11¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­®®®®®®®®®®­­®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬­¬­¬­¬¬­­¬¬­¬­¬­¬­­­­­­­¬¬­¬¬­­®®®®®®­­­­­­­­¬­­¬­­­­­®­®®­­­­­­­®­­­­­­­­­­¬¬­¬­­­¬­­­¬­¬­¬­¬­¬¬¬¬¬¬­¬¬¬¬­¬¬­­¬¬­¬¬­­¬¬¬­¬¬¬¬­­­¬­¬¬¬¬¬¬¬¬¬¬¬®¬­¬­¬¬­®­­­­­­¿11111¿®­­®1111111®®®1111111®®1111111®¿11¿1¿11¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®®­®®®­®®®®®­®®®®®®®®­®®®®­®®­­®®®®®®®®®®®­®®®®®­­­­®­­¬­¬¬­­¬­¬¬­¬­¬¬­¬¬­­­¬¬­¬­­­­¬¬­¬¬­­­­­­­®®®®®­®­­­­®®­­¬­­¬­­­­­­­®­­­­­­­­­­¬¬¬­­¬­¬­­¬­¬¬¬­­­­¬¬¬¬­¬­­¬­¬­­¬­¬¬¬¬­¬¬­¬¬¬¬­­­¬­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­­­­­­­­11111®111¿1®®®®®1®®®®¿1111111®¿¿¿¿¿¿¿¿¿¿¿®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­®­­®®®®®®®®®­­®®®®­­­­­­®¬¬­¬¬­¬­¬­­¬­­­­¬­­¬¬­¬­¬­¬­­­­­­¬­®®®­®­­­­­­­­®­­­­­­­­­­­­­­­­®­­­­¬¬­­¬­¬­­¬¬­­­­­¬­¬­¬¬­¬­¬­¬¬­¬­¬­­¬¬­¬­¬¬­¬­­¬¬¬­¬¬¬­­­­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­®®1111®®1111®®®11®®®®®®®®®®¿1111111®¿1¿11¿¿¿¿¿¿¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®®­®­­®®®®®®®®®®®­­­®­®®®­­­­­­­¬¬­­¬¬¬­¬­­¬­¬­¬¬¬­¬¬¬­¬¬¬¬­¬¬¬­­­­­­­®®®®®­­­¬­­­­­­­­­­­­­­­­­­¬­®­­­¬­­­­®­­­­­­­¬­¬¬¬¬­­­­¬¬¬­¬­¬­­¬¬­­¬¬­¬¬¬­­¬¬¬­¬¬­¬¬¬­¬¬­¬¬¬­¬¬¬­¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬¬­¬­­­­¬¬­®11®®®111111111¿®®®®®1111111®1¿¿¿111¿¿¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®®®®­­®®®®­®®®®®®®®®®­®®®®®®®­­®­­®®®®®®®®®®®®®­®­­®®®®®­­­¬¬­¬¬¬¬¬­­­¬¬­­¬¬­¬¬­¬­¬¬­¬¬¬¬­¬¬¬­­¬­­­­­®®®®®®­­¬¬­­­¬­­­­®­­¬­­­­­­­¬­­­­­­­­­­­­­­­­­¬­¬¬­­¬­­­­¬­­­¬¬­¬­­­¬­¬­­¬¬¬­¬­­¬¬­­­¬¬¬­¬¬¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­­­¬¬¬­11­®®®111111111111¿®®®®1111111®1¿11¿¿¿¿¿1¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®®®­®®­®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­­­­®®®®¬­­­­­­¬­¬¬­­­­¬­­¬¬¬­¬­¬¬¬­¬¬­¬­­­¬­­­®®®®®®®®­­­­­­®­­­­®­­­­¬­­¬­­­­®­­­®­­­­­¬¬¬­¬¬­­¬­¬­­¬­¬­­­¬¬­­¬­¬­¬­¬­­¬¬¬­­¬¬­¬¬¬­¬¬¬¬¬¬¬¬­¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­¬¬¬¬¬®111®®®111111111111¿®®®®®1111111®¿1¿¿¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®­­®®®®®®®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­®®®®­®­­­­­­¬¬¬­­¬­­­¬­­­¬¬­¬¬­¬­­¬­­¬¬­­¬­®®®®®®®®­­­­­­­­®­­­¬¬­­­­­­­®­­­­­­­¬¬¬¬¬¬¬­¬­­¬­­­¬­¬¬­¬¬­­­­®­­¬­¬­¬¬­¬­¬¬¬¬­¬¬¬¬­¬­¬¬­¬¬­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­¬­¬¬­¬¬¬­®¿11¿­®®®11111111111111®®®®®1111111®®¿¿¿¿¿1¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®®®®®®®®®®®®®®®®­­®®­®­®®­®®­®®®®®­®®®®­®®®®­®®®®®­­®®®®®®®®®®®®®®®­­­­®®®®­­­­¬¬¬­¬­¬¬¬­­­¬¬­¬¬­¬¬­¬¬¬¬­¬¬­¬­­­­®®®®®®®®­­­­­®®­­­­®®­®­­­¬­®­¬­­­­­­­­­®­­­­­­­¬¬¬¬­­­¬­­­­­¬­­¬­¬­¬¬­¬¬®¬¬­­¬­¬­¬¬¬­¬¬¬¬¬¬¬¬¬­­­¬­­¬¬¬¬¬¬¬¬¬¬¬­¬¬­­­­­¬¬¬­¬¬­®11­®111111111111111111®®®®®®®®1111111®®¿1¿¿1¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®®­­®­®®®®®®®®­®­®®­®®­®®®­®®®®­­®®®®®­®®­®®®®®®®®­­­®®®®®¬­¬­­¬­­¬­¬¬­­­­¬­¬¬­¬¬­¬¬­¬¬­­­­¬¬¬¬®­­¬­®­®®®®®­­­®­­­­­­­­¬¬­­­­­­­­­­­®­­­­­­­­­­­¬­­­­¬­­­­­­­­¬­­­­¬¬¬¬­¬­­¬¬­­­¬¬­¬¬­¬¬­­¬¬¬¬¬­­­®¬¬¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­­­­­­­¬¬­¬­­®®®®11®­­®111111111111111111®®®®®®®1111111®®¿¿¿¿¿¿¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®®®®®®®®®­®­­®®­®®®®®®®­®®®®®®®®®®®®®®®®®­­­­®®®®®®®­¬­¬¬¬¬¬¬¬­¬¬­¬­­¬¬­¬¬­¬¬¬¬­­¬¬­¬¬¬­­­­­­­­®®®®®®®­­®­­­­­­®®­¬¬­­­¬­®®®­­­­­­­­¬¬¬¬­¬¬¬¬¬­¬­­¬¬­¬­¬­­­¬­¬­­¬¬­¬­­¬¬­¬¬­­¬¬­­¬¬­­¬¬­­¬¬¬¬¬¬¬¬­¬­­¬¬­¬¬¬¬¬­®®®111®®®1111111111111111111®®®®®1111111®¿¿¿¿¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­®®®®®­®­®®®®­®­®®®®®®­­®­­®®­®®®®­®­­®®®®­®®®®®®®®®®®®®­­¬®­¬¬¬­¬¬¬¬­­¬­¬¬­­¬­­­¬­­¬­¬­®­¬­­­®®®®®®®®­­­­­­­®®®®­­¬­­­¬®­­­­­¬­­®­­­­­­­®­­­­¬­­­¬­¬­¬¬¬¬¬¬­­­¬¬­­¬¬­¬­­¬­­¬¬­¬¬­¬¬¬¬¬­¬­¬­¬¬­­¬¬¬­¬­­¬­¬¬¬¬¬¬¬¬­¬­¬¬­¬¬­¬¬­®®®®1111¿11111111111111111111111®®®®®1111111®¿¿¿¿¿¿¿¿¿®®®¿®®­®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­®­­®®®®­­®®­®®®®®®­­®­®­­®­­®®­­­®­­®®­®®®®®®­®®®®­­­­­­­¬­­­¬¬¬­¬­­¬­¬¬­­­­¬­¬¬­¬¬®¬­­­¬­­®®®®®®­­­­­­®­®®®­­¬¬­®­¬­­­­­­­­¬®­­­®®®®­­­­­­­­¬­­¬­¬¬¬¬¬­¬¬­¬¬¬­­¬¬­­­¬¬­¬¬­¬¬­­¬­¬­­¬¬­¬¬­¬­¬­¬­¬­­­¬­­­­¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬­¬­­¬¬­®®®®11111111111111111®111®®®®®®®1111111®¿¿¿1¿¿¿¿®®®®¿¿®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­®®®®®®®®®®®®®®®­­®­­®®®­­®®®®®®®®®®­®­­®­®®®®®­­®­®®®®®®­®®®®®®­­­­®¬¬­­­­¬­¬­¬­¬¬­¬­¬­¬¬­¬¬­¬­­¬¬­­­­­­­®®®®®®­­­­­­­­®®­­­­­¬¬­­¬¬­®¬­­¬¬®®­®®­­­­®­­­­­­­­¬¬­¬¬¬¬¬¬¬­­¬­­­¬­­¬­¬¬­¬¬¬­¬¬­¬­¬­¬¬­­¬­­­¬­¬­¬­¬­¬­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­­­­¬­¬¬­­®®1111111®®®®1111¿®®®®®®®®®®®1111111®®®¿¿11¿1¿¿¿®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®­®®®®®®®­®®®®®­®®­­®­®­­®­­®®®®®®®®­­¬­­­­­­­­¬¬­­¬­¬¬¬¬­¬¬¬­­¬¬¬­¬¬­¬¬­¬­¬¬­¬­¬¬¬¬­­®®­®®®­­­­­­­®®®®­­®¬­­®¬­­­­­­­®®®®®­­®­­­­­­®­­¬­­¬¬­¬­­­­¬¬­­¬­­­­¬­­¬­­¬¬¬­¬¬¬¬¬¬­¬­­¬­­¬¬¬¬¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­­¬¬­¬¬­­¬¬11®®®®®®®®®®111®®®®®®®®®®1111111®®®1¿¿11¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®­­®®­®®®®®­®­®­­­®®­­­­­­­®®®®®­®­¬­¬¬¬¬¬­­­­¬¬¬¬­­­¬¬­­¬¬­¬¬¬¬¬¬­¬­­¬¬­¬¬¬­¬¬¬­­­­­®­®®­­­­­®®®®®®­­­®®®¬®®®¬¬¬­®®®®­­­­­­­­­¬¬­­­­­¬­¬¬­¬­­­­­¬­¬¬¬­¬¬­¬¬­­­­­­¬­¬¬­­¬­¬¬¬¬­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬¬­¬¬¬¬¬­­¬¿11®®®®®®®®®®11®®®®®®®®®®®1111111®®1¿1111¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®®®®­®­­®®®®®®­­®­­®­®­®®­®®®®­®®®®®®®®¬­¬¬­­¬¬¬­­¬­­¬¬­­­­­¬­¬­¬¬­¬¬­­¬¬­¬¬­¬¬­¬­­¬­­­­­®®®®­®­­­­­­­®®®®®®®®®­­­®­®®®®®¬¬­­­®­®®®®®­­­­­­­­­­­­­­­¬­­­¬¬­¬¬­­­­¬­¬­¬¬¬­¬­¬¬¬­¬­­¬­­­¬¬­¬¬¬¬¬¬¬­¬¬¬¬¬¬¬­¬­¬­¬­­­­­¬¬¬­®1®®®®®®1®®®®®®®®®®1111111®®®¿1¿¿¿¿¿1¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®®®®­®®®®®®­­®­­®­­®­®­­­®®®®®®­®®­®­­¬¬­­­­®­¬¬¬­¬¬­­­­­­¬¬­­­­¬­¬¬­¬¬­­¬­¬¬­¬¬­­­¬¬­­­¬­®®®­­­­­­­®®®®®®®®®®®®¬¬­¬¬¬®®®®¬¬­¬­­­®®­­­­®­­­­­­­­­­­­¬¬­­¬­¬¬­­­¬¬­­¬¬¬­¬­¬­­¬¬­­¬¬­­­­­­¬­­­¬­­¬¬­­¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬­¬¬¬­¬¬­¬¬1®®®®®®®®®®®®®®®®®1111111®®®®®¿¿¿¿¿¿111¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­­®­®®®®®­­®­®®­­­®®®®®®®®®®®¬¬­¬¬­­­¬¬¬¬­­¬­­­­­­­¬¬­­­­­­­­­­¬¬­¬¬¬¬¬¬­¬­­®­®®­­­­¬­­­®®®®®®®®®®®­­­¬¬¬­­®®®®­¬¬¬­®­®­­­®­­­­­­­­­­­­¬­¬­¬¬­­­­¬­­¬¬­­¬¬­¬¬­¬­¬¬­¬¬­­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬­­¬­­­­¬¬­­­¬­®11®­®®®®®®1®®®®®®®®¿111111®®®®®¿¿¿1¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­®®®®­­®­®­­­­®­­®­­­®­®®®®®®®®®®®­­¬­¬­®¬¬¬¬¬­­­­­¬­¬¬¬­¬®­­¬¬¬¬­­¬¬¬­­¬¬¬¬­­¬­®®®®­­­­­­­­­®­­­®®®¬¬­¬­®®®¬¬¬­­­­®­®­­­­­®­­­­¬­­­­­­¬­¬¬­¬­­­¬¬­­­­¬¬¬­­¬¬­¬­¬¬­¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬¬­¬­¬¬­¬­¬®11¿1®­®®1®®111111®®®®®®®®111111®®®®®¿¿¿¿¿1¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®­®­­®­­­®­­®­­­®®®®®®­®®®®®¬¬¬­¬­­¬¬¬¬­­­­­­­­­¬­­­­¬­¬¬­¬¬­¬¬­¬­¬­­¬¬¬¬¬­­­®®®®­­­­­­­­®®­­­®®®®®®®¬­¬¬­¬­­®®®®®­­¬¬¬­¬­¬¬­®®®¬®­­­®­­­­­­­­­­­­­­¬¬­¬­­­­¬­¬­­­¬­­­¬­¬­¬¬­¬¬¬¬­¬­®¬­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬¬­¬¬­¬­­­®1111­1®®11111111®®®®®®®111111®®®®®®11¿¿1¿¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®­®®­®®®®®­®­­®­­­®­­®­­®­®®®®®®®®®®®®­®®®­¬¬­¬­­¬¬¬¬­¬¬­¬­¬­­­­­­­­­¬¬­¬¬­¬¬­¬¬­¬­¬¬­¬­­­­­­­­®®®®®®®­­­­®®®®­®¬­¬­¬¬­­¬®®®®­­­­­¬­¬­®®®®®®®®­­­¬­­­¬¬­¬­¬¬¬¬­­­­­­¬­­¬¬­¬¬¬­¬­¬¬­­¬­­­®¬­¬­­­¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­¬­­¬­¬­¬­®1111­­®11®¿11111111®®®®®111111®®®®1¿1¿1¿¿1¿¿¿1¿®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­®®®­®­­®­­®®­­®®­®®­®®®®®­­­­­¬­®­¬¬­¬­¬­­­­­­¬­­­­¬¬¬¬­­¬¬­¬¬¬­¬¬­¬¬¬¬­­­­¬¬®®®­­­­­­­®¬­­­¬¬­­­®®®®­­­­­®®®®­®®­®­­­­­­­­­­­­­­¬­¬­­­¬¬­­¬­¬­­­­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­­­­­¬¬­¬¬­­¬­¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬¬­­­­­¬­¬¬¬¿11111­­11®111111111®®®®®®®®®®®®®®®¿11¿¿111¿¿¿¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­®­®®®®®­®­­®®®­­®­®®­®­­®®­­®­®®®®®¬­­¬¬­­¬­­¬¬¬­¬­­¬­­­­¬­¬¬¬­¬­¬¬­¬¬­­¬­®®­­®­­¬­­­­­¬­­¬¬­¬­¬¬­¬­­®®®®­­­­­­®­®®®­­­­­­­­¬­­¬¬­­¬¬­¬¬¬­¬¬­¬­­­­¬¬­¬­­¬­­­­¬­¬¬¬­­­­¬­­¬¬¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬­­¬­¬¬­¬­¬­¬¬­¬¬­¬­¿11111­­­11111111111®®®®®1®®®®®®®1111111¿¿¿¿11¿®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®­®®®®®­®®®®®­®­®­­®­­®­­®­­­­®®®®®®®®®®®­¬­¬­­­¬¬¬­­­¬­­­¬­­¬­­­¬¬­­¬­¬¬­¬­¬¬­¬¬­¬­­¬­­­®®­®­­­­­­­­­¬­¬¬­­¬­¬­­­­­­­­­­­­­®­®®®®®®­­­­­­­­­­­­­­­¬­¬¬¬­¬­¬¬­¬¬­¬­¬­¬­®¬¬­¬¬­­­­­¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­­­¬¬¬­­¬¬­¬­­¬¬­­¿1111111®®1111111111®®®®111®®®®®®®¿¿¿11111111¿¿¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®®®®®®­®®®®­®­®®®®®®®®­­®­­®­­®­­®®®®®®­®®®¬­¬­­¬¬¬­­­­­¬­­­­¬­¬­­­­­­­­­­¬¬­¬¬¬­¬¬¬¬¬­­¬­­¬­­®­­­­­­­­­­­­­­­®®¬¬­¬¬­­¬­­®¬­®®­­­­­®­®®®®®®­­­­­®­­­¬­­­­­­­¬­¬­­­­¬­­­¬¬­¬¬­­­­¬­¬¬­¬¬­¬¬­­­­­­­­­¬¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬­¬­¬­¬­¬­­­­­¬­¬¬®¿¿111¿­®1111111111111®®111®®®®®®1¿1¿¿1111111¿¿1¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®­®­®®®®®­®®®®®®®®®®­­­®­­­­­­®­­®®®®®®®®®­¬¬¬¬¬¬¬¬¬¬­­­­­­­­­­­­¬­¬­­­­¬¬­­­¬¬­¬¬­­­¬¬¬¬¬­­­­®­­­­­­­­­­­­­­®­­¬­¬­¬­­­®­¬¬¬¬­­®®®®®®®®®®®®®®­­­­®­­­­­­¬¬­¬®­¬­¬¬­¬¬­¬­­¬¬­¬¬­­¬­¬­¬¬­¬¬¬­­­­­­¬¬¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­¬¬¬¬¬­­¬¬¬­­¬­­¿1111­®1111111111111®®®®®®1111®®®¿11¿1111¿11¿11¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®®­®®®®®®­®­­®­®­­­­­®®­®­®®­¬¬¬¬­¬¬¬¬¬¬­­­­­­¬­­­­­­­­­¬¬¬­¬¬¬­¬­¬¬­­¬¬­­®®®­­®­­­­­­­®­®­­­¬­¬¬­®®­¬­¬¬­®­®®®®­®®®®®®®®®®®®®®®­­­­­­­­­­®­¬­¬¬­¬­­­¬¬¬­¬­­­­¬­­¬­¬¬­¬­¬¬¬­¬­­­¬­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬¬­­¬­¬¬­¬¬­¬¬¬­¬¬­¬¬®¿1¿11­­®111®1111111111®®®®1111®®®®11¿1111¿11¿¿¿¿1¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­®®®®­®­­­­®®®®­­®­­­­­®®®®®®­­¬¬¬­¬¬¬¬¬¬¬¬­¬¬¬­­­­­¬¬­­­­­­¬­¬­­­¬­­­­¬­­¬­®­­­­­®­®­®®®­­­¬­­¬®­®­¬­¬­¬¬®­­­­®­®®®®®®®®®®®®®®­­­­­®­­­­­­¬¬­­­­¬¬­¬¬¬­­¬¬­¬¬¬­­¬¬­¬¬­¬¬­¬­¬­­­®­­¬¬­¬¬­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­¬¬­¬­¬­¬¬­¬­­¬¬¬¬¬®11¿1­®111®1111111111®®®11111®®®1¿11¿¿11¿¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®­®®®®®®®®®®®®­­®®®­®­®®­®®®®®®®®®­­­¬¬¬¬¬­¬­¬¬­¬¬¬­­­­­¬­­­­­¬­¬¬¬¬­¬¬­¬­­­­­¬­­­®®­­­­­®­®®¬­­¬­¬¬­­­­¬­­­­­­­­®®®®®®®®®®­­­­­­­­­­­¬¬¬¬­­­¬¬­­­¬­­¬­¬­¬¬­¬­­¬­¬¬­­­­­­¬­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬­­­¬¬­­¬¬¬­¬¬­¬¬­¬¬®1­®1111111111111®®®®®11111®¿11111111¿¿¿¿1111¿¿¿®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­®®­­­­®­­­­­¬­¬¬­¬¬­¬¬¬­­­­­­­¬¬­­­­­­­¬¬¬­¬¬¬­­¬¬­­­®®­­­­­­­­­­®®®­­­­¬­­­¬­¬¬­­¬­®­¬¬­¬¬¬­­­­®®®®®®®®®­­­­®­­¬­­¬¬¬­­­¬­¬­¬­¬¬­­­­­®­®¬­¬¬­¬­¬¬¬­­­¬¬¬­¬¬­¬¬­¬¬¬¬­­¬¬¬­­­­­­­­­¬¬¿1­¬­®111­­1111111111®®111111111111111¿111¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®®®®­­®­­­­­­­­­­­­®­­­­­­­­¬¬­¬¬­¬­¬­¬¬­¬­­­¬¬­­­­­­¬­­­­¬¬­¬­­­¬­­­­¬­­­­­­­­¬­¬­¬¬¬¬¬¬­¬­­®®­­­­¬­¬­®®®®®®®®®®®­­­­®­­­¬­¬­¬¬­¬¬­­­­¬­­­¬­¬¬¬­­­®®®­­¬­­¬­¬¬¬¬¬¬¬­¬¬¬­­­¬­¬­¬­¬­­¬¬¬­­­­­­­­­­­­¬­®®®®¿­­®111­­­1111111111®®®11111111111111¿¿11¿1¿11¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­®®®®®®­­­­­­­­­®®®­­­­­­¬­­¬¬¬¬¬¬­¬¬¬­¬¬¬­¬¬¬­¬¬¬¬­¬­¬­¬­¬¬¬¬­®­­­­¬­­­­­®­­­­­®­¬­¬¬¬­­®­­¬¬­­®®®­¬­¬­­­­®®®®®®®®®®®®®®®®­­­­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬¬­¬­®­®¬¬­­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­­¬¬¬¬­­¬­­­­¬­­­­¬¬®®®1­­11­®111111111®11111111111111¿11111¿¿1¿¿1®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­®®®®®®®­­®­­­­®­­­­®­®®®­­­­­­¬­­¬¬­¬¬­¬¬­¬¬­­­¬­¬­­¬¬­¬¬¬­­­¬­¬­­¬­­­¬­­­­­­­­­¬­­¬¬­¬­¬­­­­­­®®­¬­¬­­­®®®®®®®®®®®®®­­®­­­®­­¬­­­¬¬­¬­¬¬¬¬­¬­­­®®®®®®®­¬­¬­¬­¬­¬¬¬¬¬­­¬¬¬­¬¬­¬¬¬¬­­­­­¬­­®®1­­­­11®111111111®®1111111111111111111¿11111¿¿¿¿¿¿®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­­­­­­­­­­­­­­®®­­­¬­¬¬­¬­¬¬¬­¬­­­­­­­­­­¬­¬¬¬­­­­­­­­­­­­­­­­­­­®­­­­­­­¬¬¬­­­­­­­®¬­®®®®®®®®®®®®®®®®­­­­®®­­¬¬¬­¬­¬¬­¬¬¬¬¬­­¬¬­­­­®®®®®®®­¬¬¬¬­¬­¬¬¬­¬¬­¬­­­­¬­­¬­¬­¬­­­¬¬­­­­­®­­­­­®®®1­­­®11­111111111®®111111¿¿1¿11111¿¿1111111¿¿¿1¿¿¿1¿®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®­­®­­­­­­­­­­®®®®­­¬¬¬­¬­¬­­­­®­¬­­­­¬¬­­­¬­¬¬­¬¬­­­­­­­­­­­®®­­­®­­­­­­­­¬­¬­­®­¬­¬­­­­®®­­­®®®®®®®­®­­­®®®­¬­­­­¬¬¬¬¬­¬¬­¬­­¬­­¬­­­­®®®¬¬­¬¬­­¬¬¬­¬­­­¬¬¬¬¬­¬¬­¬­¬¬¬¬¬­­¬­­­­­¬­¬­®11­11®1111111®®®111111®¿11111111¿1111111¿1¿¿1¿11¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®­®®®­®®­­­­­­­­­­­­­­­­­®®®®­­­­¬¬­¬¬­¬¬­­­­­­¬­­­­­­¬®­­¬­¬¬­­­­­­­­¬­­­­¬­­­­­®®­­­­­­¬¬­¬­¬­¬®­¬­¬¬­­­­­­­­®®®®®®­®­­®­­­­¬¬­¬­­­¬­­¬¬­¬­­¬­­¬­­­®®®®®®¬­¬¬­¬¬­­¬­­¬¬­¬¬­­¬¬­¬­­¬­¬¬¬­¬­­­­­­­­¬11­­­­®1®­­1111®®®111111®1111111111¿1111¿¿¿1111¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­®­­­­®­­­­­­­­®®®®®­­­­¬¬­¬­¬¬­¬¬­­­­­­­¬­­­¬­­­­­­¬¬­¬­¬­­­­­­­­­¬¬¬¬­­­­­­­­­­­¬¬¬¬­¬­­¬­®­­­­®­­­­®®®®®®­­­­­­­­¬¬¬¬¬¬­­­¬¬­¬­¬¬­¬­¬¬¬­­­®®®®®¬¬­¬­¬­­¬­­­¬¬­¬¬­¬­¬­¬¬¬¬­¬¬­¬­¬­­­­­­­­­­­­¬­­­®®­¬¬¬11®®®®®1¿11111111111¿¿1¿¿11¿¿¿11111¿11¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­®­­­­­­­­­­­®­­­­­­®®®®­­­­¬­¬­¬¬¬¬­¬¬­¬­­¬­­­¬¬¬¬¬¬¬­¬¬­­­­­­­­­­­­­¬­­­­­®­­¬¬­¬­¬­®­­­­­­®®­­­®®®®®®®®­­­­­­­­­¬¬­­­­¬­¬­¬¬­¬¬­¬¬­­­­­­®­¬­­¬­­¬­¬­¬­¬¬¬¬­¬­­¬¬­­­­¬¬­­¬­¬­­­­­­­­­®1¿®®­¬­­®111­®®®®®®11®®11111111111111111¿1111111111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­®®®®®®®®­­­­­­­­­­­­­­­­®®®­­­¬¬­¬¬­¬¬¬­¬¬­¬¬¬¬­­­­­¬¬¬¬­¬¬¬­­­­­­­­­­­­¬­®­­­­¬¬¬¬¬­­¬­­­­­­­­­­®®®®­­­­­®®®®®®®®®®®®­­­­­­­¬¬­¬­­¬¬­¬­­¬­¬­­¬­¬­­­­®¬¬¬¬¬­¬¬­­¬¬­¬­­­¬¬­¬­¬­¬¬¬­­­­­­­­­­­­­11®®­­­­®111­®®®®®®11®11111111111111111111111111®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®­®­­®­®®­®­­®­­­­­­­­­­­­­­­­­­­®®®¬¬¬¬¬¬­­­­¬¬­­­­¬­¬¬¬­­­¬¬¬¬­­­¬­­­­­®­­­­­­­®¬­­­­®®­­­­­­¬¬­¬¬¬¬®­­­­­®®®­­­­®®®®®®®­­­­¬­¬­­­­­¬¬­¬¬­­­­­¬­­­­­­­®®®®¬¬­¬­­­¬­­­¬¬­¬¬­¬­­­¬¬­¬¬­­¬¬¬¬¬¬¬­­­¬­­­­­­­­­­­­®11®­­­­®11®®®®®®®®®11®111111111111111111¿111¿11111111®®®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­­­­­­­­­­­­­®­®­­¬¬¬­¬¬­­­­­­­­­­­­¬¬¬¬­¬¬¬¬¬­­¬¬¬­­­­­®­­­­­­­­®®®®­­®­¬­­­®­­­­­®­®®®®®­®®®®®®®­­­­­­­­¬¬­¬¬¬¬­­­¬¬­¬¬­¬­­­¬¬¬­­­­­®®®®®­­­¬­¬­¬­¬¬­¬¬­¬¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­­­®­¬®111­­­®®®®®®1®11111111¿111111111111111111111¿¿®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®­®®®®®­­­­­­­­­­­­­­­®®®­­­¬¬­¬¬­­®­¬¬­­­­­­¬¬­¬¬¬¬­­­­¬¬¬­­­­­­­­­­­­­®­®®­­­¬­¬­­­­®¬¬¬®­­®®®®®®®­®®®­­­¬¬­¬­¬­¬¬¬¬­¬¬­¬¬­¬¬­¬­­¬¬­­®®®­¬¬­¬­¬­¬¬­­¬­¬­¬¬­­¬­¬­¬­¬¬¬¬¬­­®­­­¬¬®®111®®®®®®¿1®®®®®®®®®®®11¿11111111111¿1111111111111¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­®­­­­­­­­­­­­­­­­­¬­®®®®®®­­¬¬­¬¬­¬­­­¬­¬­­­­­­­¬¬¬¬­­¬­­­¬­­­­­­­­­­­¬­­¬®®®®®®®­­¬­­­­­­¬­¬­­®­¬­­®­®®®®®®®®®®®­­­­¬¬¬¬­­¬­¬¬­¬¬­¬¬­¬­­¬­¬®­­¬¬­¬­¬­­­­¬­­¬¬­¬¬­¬­¬­¬¬¬¬¬¬¬¬¬¬­¬¬¬¬­­­­­­­­­®®1111111111¿1111111111111¿1111111111¿®®®11111111111¿¿111¿1111111¿¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­®®®­®­­­­­­­­­¬­­­­­­­­­®®®®®®®¬¬­­¬¬¬¬­­¬­­¬­­¬­­¬¬¬­¬¬­¬­¬­­­­­­­­­­­¬­­­­­­­®®®®®®­®­­­­¬¬­¬¬­¬­®­­­­®®­®­­®­®®®®®®®­­­­­­­¬¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­­¬­­­®®¬¬­¬¬¬¬¬­¬¬­¬­¬¬¬­­¬¬­­¬¬¬¬­¬­­¬¬¬­­¬­®­­­­­­­¬¬®®1111111111111111111111111111111111111111111111®®111111111¿111111¿¿11111¿¿¿¿®®®®®®®®®®®®®®®®®®®¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®­­­­­­­­­­­­­­®®®®®®­­­­¬­¬­¬­¬¬¬­¬¬­­­­­­¬¬­¬¬¬¬­­­­­­­­­­­­­­¬­®®®®­­­­­¬­­­­­¬¬®­­­®®­®®®®®®®­­­­­­­¬¬¬­¬¬¬¬¬¬­­¬¬­¬­¬­­­¬­­­­®®®­®¬¬¬¬­­¬¬­¬­­­­­¬¬­¬­¬¬¬¬­¬¬­­­­­­­­­­¬­®®®®¿11111111111111111111111¿111111111111111111111¿®1111111111111111¿111111¿¿¿11®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­®®¬¬¬¬­¬¬¬­¬­¬¬¬­­­­­­­­­­­­­®®®®®­­­¬­­­­¬¬­®®¬­­­­­­­­­­®­¬¬­¬­¬­¬­¬¬¬¬¬¬­­­­¬¬­¬¬­¬¬­­¬­¬¬­¬¬­¬­®®®®­­®®­¬­­­¬­¬¬­­¬­¬­¬¬­­¬­­¬¬­­¬­¬­­­­®¬¬­®®¿¿11111¿111¿1111111111111111111111®®111111111¿111111111111¿¿¿¿¿®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®®®®­­­®®®®®­­­­¬¬¬­¬¬¬¬¬¬­¬¬­­­­­­­­­­­­­­­­®®®®®®®®­­¬¬­­­­­¬­­­­­­­­­­­­­­­­®­­­­­¬­­­­­¬¬­¬¬¬¬¬¬¬­­¬¬¬­­¬¬­¬­¬¬­¬¬­¬®®­­­­­­®¬¬­¬¬­¬­¬­¬¬­¬¬­¬¬­¬¬¬­¬­­­¬­­¬¬­¬­¬­­­­­­­­­®®®®®®®®®¿111®®¿1111111111111111111111®111111111¿1111111111111111¿¿1®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­¬­­®®­­­­­­­­¬¬¬¬¬¬­¬¬¬¬¬­¬­­­­­­­®®®®®®®®®®­¬­¬­­­­­­­­­­­­­­­­­­­­­­¬­­­¬¬­­­¬­­¬¬¬¬­¬­¬­­¬¬­¬¬¬¬¬¬­¬¬­¬­¬¬­®®­­­­­­­­­­­¬¬­¬¬­¬¬­­¬¬¬¬­¬­¬­¬­¬¬¬¬¬­­­­¬¬­­¬¬¬­­­­®®­¬¬®®®®®®®¿11111®®¿1¿11111111111111111111®11¿11111111111111¿1111¿11¿11¿1®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®®®­­­­­­¬¬¬¬¬¬­­­¬¬­­¬­¬­­¬­­­­­­­­­®®®®­®®­­­­­­­­­­­­­®­­­­­­­­®®­­­­¬¬¬­­­¬­­­­­¬¬­­¬¬­¬¬­¬­¬­¬¬¬¬­­¬¬­¬¬¬¬­¬­¬¬­¬¬®®®­­­­­­­­­¬¬¬­­­¬­¬­­¬­¬­¬­¬­­­¬­¬¬­­­¬­­¬­­­®­­®®®®®®®®®11111111111®®®¿1111¿®®®®¿11¿¿11111111111111111¿11¿1¿¿¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­®®®­­­­­­­­¬­¬¬­­­¬¬¬¬¬¬­­¬¬¬¬­­­¬­­­­­®®®®®®­®®­­®­­®­­­­¬­®®­­­­­­­­­­®­­¬­­­¬¬­­­­­­­­¬¬­¬¬¬¬¬­­­¬¬¬¬­¬¬­¬¬­¬­­®®®®®®®­­®­­­­¬­¬­­¬­¬¬­¬¬­¬¬­¬¬¬­¬¬­¬­¬¬­¬­¬¬­¬¬­­­­­­­®­­­¬­®®®¿®®®®®®®®®®®®®1111111111111¿111111111¿1¿¿1¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­®®®®®­­®­­­­­­­®®­­¬­­­­­­¬¬¬­¬­¬­¬¬­­­­­¬¬¬¬­­­­­­­­®®®®®®®®®­®­­­­­­­­­¬­­­®®®­­­­­­­­­­­­­­­­­­¬¬­­­­­¬­­¬­­­­­¬¬­¬¬­¬¬­­¬­­¬­¬¬¬¬­¬­¬­¬­®®®®®®®®®®­­­­¬¬¬­­¬¬­¬¬¬­¬¬­¬¬­¬¬­¬¬­¬¬­¬­­¬­¬¬­¬­­¬¬¬­­­­­­¬¬®®®®®®®®1®®®®®®®®®®®®®­111111111111¿¿11111111¿¿1¿11111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®­­­­®®®­­­­­¬­­­­¬­¬¬­¬¬¬¬¬¬­­­­­­¬¬­­­­­­­­­®­®®®®®®­­­­­­­­­­®­­­­­­­­­­­­­­­­­¬­¬­­­­­­­¬¬¬¬­¬¬­¬­­­­­­­­­¬¬¬¬¬­¬¬­®®®®®®®®®­­­­­­¬¬­­­¬­¬­­­¬­¬­¬¬­¬¬­­¬¬­­¬­¬­¬­­­¬­­¬­­­­­®­­®®®®®®®®®¬¬®®®®®®®®111111111111¿1111111111¿1111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®®®®®®®­­­­­®®®®®®­­­­­¬­¬­¬¬¬¬­¬¬­­¬¬­­­­¬­­­­­®­®­­®®®­­®®®­­­­­­­­­­­­­¬­­­­­­­¬­­­­¬­­¬­­­¬¬­­­­­¬¬­­­¬¬­­­­¬­¬­¬­¬­¬­¬­¬®®®®®®®®­­­­­¬­¬­¬¬­¬¬¬¬­¬¬­¬¬­¬¬­¬¬­­­¬¬¬­¬­­­¬­¬¬­­­¬­­¬­­­­®­­®®­­­­®®®®®®®®®®111111¿11111111111111¿¿¿1¿¿¿11®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­®­­­­­¬­­­­­¬¬­¬¬­¬¬­¬¬­­­­­­­¬®­­­­­®­®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬­­­­­­­­­®­­­¬¬¬¬­­­¬­¬¬­­­­­­­­­­­­¬­¬¬¬­¬¬®®®®®®®®­­­­­¬¬­­¬­­¬¬¬¬¬¬­¬¬­¬¬¬­¬­¬­¬¬­­­¬­¬­¬­¬¬­¬¬­­­®­­­­¬¬¬¬­­­¬¬¬¬¬1111111111111111111111¿¿1¿¿1¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®®­­­­­­­­®­­­¬¬¬¬¬­¬­­­­¬­­­®­­­­­¬­®­®®®®®®®®­­­¬¬­­­­­®®­­­­­­¬¬¬­­­­¬­­­­­­­­­¬¬­¬­¬¬­¬¬­­­¬­¬¬­­¬¬­­¬¬¬­¬­­¬¬­¬­®®®®®®®®­­­¬­­­¬¬­¬¬¬¬­¬­­­¬¬­¬¬¬¬­¬¬¬¬­¬¬­¬­­­­¬¬­¬­­­®­­¬¬­¬¬¬¬­­¬¬­¬¬¬¬¬¬¬¬­­®®111111111111111111111¿¿1¿¿1111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®­­­­­®­­­­­­­­­¬¬¬­­¬­­­­­­­­­­¬¬¬­¬®­®®®®®®®®®®®®­­­­­­­­­­­®­­­­­­­­¬­¬­­¬­­­¬­­­­¬­¬­¬¬­­¬­­­¬­­­­­­­¬­¬¬­¬¬­¬¬¬¬­¬¬­¬®®®®®­­­¬¬¬­¬¬¬­­¬¬­¬¬¬¬¬­¬¬­¬¬¬¬­¬­¬­¬­­­­¬­­­­­¬­­®­­¬­¬¬¬¬­¬¬¬¬­¬¬¬¬¬­¬¬¬­®­11¿¿11111111111111111¿111¿¿111®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­®®­­­­­­­¬­­­¬¬¬¬­­­­­­­­­­­­­®®®®®®®®®®®®®®®®®­­¬­­¬­­­®­­­­­­­­­­¬­­­­­­­¬­¬­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬­¬­­¬­¬¬®®®®®­­­­­¬¬­¬¬­¬¬¬­¬¬¬¬­¬¬¬­¬­­­­­­­¬¬­­¬­¬­¬­¬¬­¬¬¬­¬¬­¬¬­¬­¬¬¬¬­¬¬¬¬­­¬¬­­­­®1¿¿1111111111111¿¿111¿11¿11¿1¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®­­­­­­­¬­­¬¬¬­¬­­­­­­®­­­­®®®®®®®®®®®®®®®®®®­¬¬¬­¬¬­­¬­­­­­­­¬¬­¬­¬­­­¬¬­¬¬­¬¬­¬­­­¬­­¬­­­­­¬­­¬­­­¬¬­­­¬¬¬¬¬¬¬¬¬­¬¬­­­­®®®®®­­­­­¬¬­¬­¬­¬¬­¬¬­¬¬­¬­¬¬¬­­¬­¬¬­¬­¬­­­¬­¬¬­¬­¬¬­®­®®­­¬¬¬¬¬¬¬­¬¬¬¬¬¬­¬­¬­­®1¿11111111111111¿1¿¿¿1¿11¿¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­®®®®­­­®®­­®­­­­­­­­­­¬­¬¬­¬¬­¬¬¬­­­­­­­­­®®®®®®®®®®®®®®®®­¬­­¬­­®­¬¬­­®®®­­­­­­­­­¬­­­­¬¬¬¬¬¬­¬­¬­¬­­­­¬­¬­­­¬­¬¬­­¬¬¬¬¬¬­¬¬­¬¬­¬¬­¬­­®®®®®®­®­­­¬¬­¬­­¬¬­¬¬­¬¬­¬­¬¬¬¬¬­¬­­¬­­¬¬­¬­¬¬¬¬­­¬¬¬­¬­¬­¬¬¬¬¬¬¬¬¬­­­­­¬¬­¬¬¬­­¿1111111111111111¿¿¿¿1¿¿111¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­®­­­­­­­­­­­¬¬­­­­­¬¬®­­­­­­­®®®®®®®®®®®®®®®®®­­­­¬­­­­®®­­­­­®­­­­¬¬­¬­¬¬¬¬­­­­­­­­­­­¬¬­­­­­­¬¬­¬¬¬¬¬­¬­¬¬¬¬¬¬­¬¬­¬­®®®­­­­¬­¬­¬¬­¬­­¬¬¬¬­¬¬­¬¬­¬­¬¬¬­¬­­¬­¬­­¬¬¬­­­®¬­¬­­¬­¬¬­¬¬¬¬¬¬¬­¬¬­¬­­­®11111111111111111111111111111111111¿1¿¿1¿¿11¿1¿¿¿¿¿¿¿¿¿¿¿11¿¿¿1¿¿¿¿¿¿®®®®®®®®®®®®®®®®®®®®®®®®¿®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{ "worldtype" "2" "sounds" "6" "classname" "worldspawn" "wad" "gfx/base.wad" "message" "the Slipgate Complex" } { "classname" "info_player_start" "origin" "480 -352 88" "angle" "90" } { "classname" "light" "origin" "480 96 168" "light" "250" } { "classname" "light" "origin" "480 288 168" "light" "250" } { "classname" "light" "origin" "272 96 80" } { "origin" "272 288 80" "classname" "light" } { "classname" "light" "origin" "272 192 80" } { "origin" "688 192 80" "classname" "light_fluorospark" "style" "10" } { "style" "10" "classname" "light" "origin" "688 288 80" } { "origin" "688 96 80" "classname" "light" "style" "10" } { "classname" "light" "origin" "480 -280 168" "light" "200" } { "origin" "480 -144 168" "classname" "light" "light" "200" } { "classname" "light" "origin" "480 -376 120" "light" "200" } { "light" "160" "origin" "480 -40 168" "classname" "light" } { "speed" "400" "sounds" "2" "angle" "270" "classname" "func_door" "model" "*1" } { "speed" "400" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "250" "origin" "592 544 88" "classname" "light_fluoro" } { "origin" "456 600 104" "classname" "light" } { "light" "180" "origin" "688 648 136" "classname" "light" } { "classname" "light" "origin" "688 520 136" "light" "180" } { "origin" "688 480 80" "classname" "item_armor1" } { "angle" "180" "spawnflags" "768" "origin" "616 72 40" "classname" "monster_army" } { "light" "250" "origin" "0 576 120" "classname" "light" } { "light" "180" "origin" "160 576 72" "classname" "light" } { "light" "200" "origin" "560 -32 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "400 -32 72" } { "light" "200" "origin" "0 712 72" "classname" "light" } { "classname" "light" "origin" "0 728 -136" "light" "200" } { "light" "200" "origin" "0 592 -136" "classname" "light" } { "wait" "5" "angle" "-2" "sounds" "2" "targetname" "t1" "classname" "func_door" "dmg" "10" "model" "*3" } { "sounds" "1" "target" "t1" "angle" "180" "classname" "func_button" "model" "*4" } { "light" "200" "origin" "412 780 136" "classname" "light" } { "light" "200" "classname" "light" "origin" "328 904 72" } { "light" "200" "origin" "168 800 72" "classname" "light" } { "light" "200" "classname" "light" "origin" "-72 864 72" } { "origin" "264 888 -136" "classname" "light" } { "classname" "light" "origin" "-8 992 -136" "light" "200" } { "light" "250" "classname" "light" "origin" "272 1064 -136" } { "light" "250" "origin" "-8 1232 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "256 1272 -136" } { "light" "250" "origin" "312 1464 -136" "classname" "light" } { "light" "200" "origin" "128 968 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-48 1168 72" } { "light" "250" "origin" "312 1168 72" "classname" "light" } { "light" "220" "classname" "light" "origin" "128 1504 -120" } { "light" "250" "classname" "light" "origin" "-56 1464 -136" } { "sounds" "2" "classname" "func_door" "angle" "180" "speed" "400" "model" "*5" } { "classname" "func_door" "angle" "0" "speed" "400" "model" "*6" } { "classname" "light_fluoro" "origin" "176 1744 -152" } { "origin" "80 1744 -152" "classname" "light_fluoro" } { "light" "250" "origin" "-232 1600 -136" "classname" "light" } { "light" "250" "classname" "light" "origin" "488 1600 -136" } { "origin" "-56 1448 72" "classname" "light" "light" "250" } { "light" "250" "classname" "light" "origin" "312 1448 72" } { "light" "260" "classname" "light_fluoro" "origin" "416 2064 -112" } { "light" "260" "origin" "416 1968 -112" "classname" "light_fluoro" } { "light" "250" "origin" "128 1880 -112" "classname" "light" } { "origin" "616 1944 -88" "classname" "light" } { "style" "10" "classname" "light_fluorospark" "origin" "344 2216 -88" } { "light" "180" "origin" "352 2016 -112" "classname" "light" } { "classname" "light" "origin" "128 2056 -112" "light" "250" } { "light" "250" "origin" "-112 1984 -112" "classname" "light" } { "light" "350" "origin" "-472 2064 -88" "classname" "light_fluoro" } { "classname" "light" "origin" "-192 2208 8" "light" "250" } { "light" "250" "origin" "-424 2208 8" "classname" "light" } { "light" "250" "origin" "-248 2088 -96" "classname" "light" } { "origin" "-200 2384 -72" "classname" "light" } { "classname" "light" "origin" "-424 2384 -72" } { "light" "200" "origin" "-448 2408 -128" "classname" "light" } { "classname" "light" "origin" "-176 2408 -128" "light" "200" } { "sounds" "1" "classname" "func_plat" "model" "*7" } { "light" "350" "origin" "-352 2656 184" "classname" "light" } { "light" "350" "classname" "light" "origin" "-352 2464 184" } { "origin" "-576 2800 -40" "classname" "light" } { "light" "500" "origin" "160 2920 232" "classname" "light" } { "classname" "light" "origin" "160 2720 232" "light" "500" } { "origin" "-288 2992 8" "classname" "light" } { "classname" "light" "origin" "-168 2776 -40" } { "classname" "light" "origin" "160 2824 104" "light" "200" } { "light" "150" "origin" "-64 2760 136" "classname" "light" } { "light" "200" "origin" "16 2832 -152" "classname" "light" } { "classname" "light" "origin" "304 2832 -152" "light" "200" } { "origin" "504 2816 16" "classname" "light" } { "sounds" "3" "wait" "-1" "speed" "600" "targetname" "t2" "spawnflags" "1" "angle" "270" "classname" "func_door" "model" "*8" } { "classname" "light" "origin" "160 2840 -152" "light" "200" } { "light" "80" "origin" "16 2904 -88" "classname" "light" } { "classname" "light" "origin" "304 2904 -88" "light" "80" } { "classname" "light" "origin" "160 2904 -88" "light" "80" } { "wait" "-1" "sounds" "1" "target" "t2" "speed" "50" "angle" "270" "classname" "func_button" "model" "*9" } { "light" "100" "origin" "0 1800 -32" "classname" "light" } { "classname" "light" "origin" "248 1800 -32" "light" "100" } { "style" "32" "targetname" "t3" "origin" "8 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2392 200" } { "style" "32" "targetname" "t3" "origin" "56 2352 200" "classname" "light" } { "style" "32" "targetname" "t3" "classname" "light" "origin" "32 2312 200" } { "style" "32" "targetname" "t3" "light" "200" "origin" "32 2352 88" "classname" "light" } { "spawnflags" "2048" "origin" "112 2352 16" "classname" "weapon_nailgun" } { "sounds" "3" "targetname" "t3" "spawnflags" "3" "angle" "270" "classname" "func_door_secret" "model" "*10" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*11" } { "origin" "304 2368 96" "classname" "light" } { "angle" "180" "origin" "248 2392 40" "classname" "monster_army" } { "origin" "272 2352 64" "classname" "item_spikes" } { "style" "32" "sounds" "3" "target" "t3" "classname" "trigger_once" "model" "*12" } { "origin" "832 2608 16" "classname" "light" "light" "220" } { "light" "220" "classname" "light" "origin" "832 2480 0" } { "light" "240" "origin" "800 2816 24" "classname" "light" } { "style" "33" "targetname" "t11" "spawnflags" "1" "classname" "light" "origin" "752 2000 -88" "light" "400" } { "style" "34" "spawnflags" "1" "targetname" "t12" "origin" "1280 2000 -152" "classname" "light" "light" "400" } { "style" "35" "spawnflags" "1" "targetname" "t13" "classname" "light" "origin" "1280 2496 -216" "light" "400" } { "style" "36" "spawnflags" "1" "targetname" "t14" "origin" "784 2496 -280" "classname" "light" } { "classname" "light" "origin" "1368 2584 -488" "light" "200" } { "origin" "1368 1944 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2584 -488" "light" "150" } { "origin" "1016 2584 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "1016 1944 -488" "light" "200" } { "origin" "1368 2272 -488" "classname" "light" "light" "200" } { "classname" "light" "origin" "696 2272 -488" "light" "200" } { "classname" "light" "origin" "960 2296 -488" "light" "200" } { "light" "200" "origin" "1032 2352 -488" "classname" "light" } { "classname" "light" "origin" "888 2352 -488" "light" "200" } { "light" "200" "origin" "960 2408 -488" "classname" "light" } { "light" "100" "classname" "light" "origin" "984 2448 -304" } { "classname" "light" "origin" "832 2360 112" "light" "400" } { "classname" "light" "origin" "1144 2448 -488" } { "origin" "1232 2360 -488" "classname" "light" } { "classname" "light" "origin" "1320 2448 -488" "light" "200" } { "light" "200" "origin" "1232 2536 -488" "classname" "light" } { "classname" "light" "origin" "1232 2136 -488" } { "origin" "1144 2048 -488" "classname" "light" } { "classname" "light" "origin" "1232 1960 -488" "light" "200" } { "light" "200" "origin" "1320 2048 -488" "classname" "light" } { "classname" "light" "origin" "832 2336 -200" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "sounds" "3" "model" "*13" } { "classname" "func_door_secret" "angle" "180" "sounds" "3" "model" "*14" } { "classname" "light" "origin" "552 2480 -56" "light" "200" } { "light" "200" "origin" "544 2296 -56" "classname" "light" } { "classname" "light" "origin" "664 2480 -56" "light" "200" } { "classname" "func_door" "targetname" "t4" "angle" "-2" "spawnflags" "1" "sounds" "2" "model" "*15" "lip" "7" // svdijk -- added to prevent z-fighting } { "classname" "trigger_multiple" "target" "t4" "health" "1" "model" "*16" } { "spawnflags" "2048" "classname" "func_door" "angle" "90" "targetname" "t5" "wait" "-1" "sounds" "2" "model" "*17" } { "spawnflags" "2048" "classname" "trigger_once" "target" "t5" "model" "*18" } { "classname" "item_artifact_super_damage" "origin" "544 2480 -88" } { "classname" "light" "origin" "832 2104 -208" } { "classname" "light" "origin" "832 2048 -368" "light" "150" } { "classname" "light" "origin" "1120 2464 112" } { "origin" "1120 2080 112" "classname" "light" } { "classname" "light" "origin" "752 2080 112" "light" "200" } { "classname" "light" "origin" "1048 2280 -72" } { "classname" "func_button" "angle" "270" "target" "t1" "model" "*19" } { "classname" "light" "origin" "1136 1848 -504" "light" "220" } { "origin" "1136 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1008 1672 -504" "light" "220" } { "origin" "1008 1848 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1288 1848 -504" "light" "220" } { "origin" "1400 1584 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1584 -504" "light" "220" } { "origin" "1400 1736 -504" "classname" "light" "light" "220" } { "origin" "880 1672 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "744 1672 -504" "light" "220" } { "classname" "light" "origin" "1312 1648 -392" "light" "220" } { "light" "170" "origin" "1312 1520 -392" "classname" "light" } { "classname" "light" "origin" "1200 1760 -392" "light" "220" } { "light" "170" "origin" "1072 1760 -392" "classname" "light" } { "classname" "light" "origin" "944 1760 -392" "light" "170" } { "origin" "832 1992 -208" "classname" "light" "light" "220" } { "origin" "744 1832 -504" "classname" "light" } { "light" "170" "origin" "832 1760 -392" "classname" "light" } { "light" "220" "origin" "680 1936 -504" "classname" "light" } { "classname" "light" "origin" "1312 1392 -352" "light" "170" } { "light" "170" "origin" "1312 1264 -288" "classname" "light" } { "classname" "light" "origin" "1312 1136 -232" } { "origin" "1224 1456 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1456 -504" "light" "220" } { "origin" "1400 1328 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1224 1328 -504" "light" "220" } { "origin" "1224 1200 -504" "classname" "light" "light" "220" } { "classname" "light" "origin" "1400 1200 -504" "light" "220" } { "origin" "1312 960 -208" "classname" "light" } { "classname" "trigger_teleport" "target" "t6" "model" "*20" } { "classname" "light" "origin" "1312 912 -472" } { "classname" "light" "origin" "1312 1080 -368" } { "classname" "light" "origin" "1128 1064 -504" "light" "170" } { "origin" "1128 856 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1496 856 -504" "light" "170" } { "origin" "1496 1064 -504" "classname" "light" "light" "170" } { "classname" "light" "origin" "1312 776 -504" "light" "170" } { "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*21" } { "origin" "1072 1024 -168" "classname" "light" } { "spawnflags" "1" "height" "400" "angle" "-1" "sounds" "1" "classname" "func_plat" "model" "*22" } { "targetname" "t8" "spawnflags" "2" "angle" "90" "classname" "func_door_secret" "model" "*23" } { "target" "t8" "classname" "trigger_multiple" "model" "*24" } { "light" "220" "origin" "792 888 -248" "classname" "light" } { "light" "180" "classname" "light" "origin" "944 608 -248" } { "light" "150" "origin" "792 512 -248" "classname" "light" } { "classname" "light" "origin" "792 512 -56" "light" "150" } { "classname" "light" "origin" "624 928 -240" "light" "220" } { "light" "220" "origin" "504 1200 -248" "classname" "light" } { "origin" "936 800 -248" "classname" "light" "light" "180" } { "light" "180" "classname" "light" "origin" "960 984 -208" } { "classname" "light" "origin" "792 512 128" "light" "150" } { "spawnflags" "2" "origin" "944 1008 -272" "classname" "item_health" } { "spawnflags" "1792" "origin" "144 2352 16" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1216 1040 -432" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "1392 1024 -432" "classname" "item_rockets" } { "targetname" "t6" "origin" "-32 1800 -56" "classname" "info_teleport_destination" } { "spawnflags" "1792" "origin" "832 2448 -368" "classname" "weapon_supernailgun" } { "spawnflags" "1792" "origin" "128 1216 -208" "classname" "weapon_supershotgun" } { "origin" "296 2136 -192" "classname" "item_shells" } { "spawnflags" "1" "origin" "1424 904 -432" "classname" "item_health" } { "classname" "item_health" "origin" "1376 808 -432" } { "origin" "1176 936 -432" "classname" "item_health" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*25" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*26" } { "spawnflags" "2048" "target" "t9" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*27" } { "target" "t10" "targetname" "t9" "count" "3" "classname" "trigger_counter" "model" "*28" } { "message" "You must press the three buttons..." "spawnflags" "2048" "sounds" "2" "wait" "-1" "targetname" "t10" "angle" "180" "classname" "func_door" "model" "*29" } { "light" "150" "origin" "832 1928 -384" "classname" "light" } { "style" "33" "sounds" "3" "target" "t11" "classname" "trigger_once" "model" "*30" } { "style" "34" "sounds" "3" "target" "t12" "classname" "trigger_once" "model" "*31" } { "style" "35" "sounds" "3" "target" "t13" "classname" "trigger_once" "model" "*32" } { "style" "36" "sounds" "3" "target" "t14" "classname" "trigger_once" "model" "*33" } { "sounds" "1" "wait" "-1" "targetname" "t11" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*34" } { "targetname" "t12" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*35" } { "targetname" "t13" "sounds" "1" "wait" "-1" "spawnflags" "1" "angle" "-2" "classname" "func_door" "model" "*36" } { "targetname" "t14" "classname" "func_door" "angle" "-2" "spawnflags" "1" "wait" "-1" "sounds" "1" "model" "*37" } { "angle" "90" "origin" "1312 880 -248" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "1376 1024 -272" "classname" "item_spikes" } { "origin" "1184 992 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1376 856 -272" "classname" "item_health" } { "spawnflags" "1" "origin" "1256 1704 -432" "classname" "item_health" } { "angle" "90" "origin" "480 48 24" "classname" "info_player_deathmatch" } { "angle" "180" "origin" "528 1888 -168" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "-272 2928 -56" "classname" "info_player_deathmatch" } { "angle" "0" "origin" "832 2048 -152" "classname" "info_player_deathmatch" } { "speed" "300" "message" "This door opens elsewhere..." "spawnflags" "2048" "targetname" "t15" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*38" } { "spawnflags" "2048" "target" "t15" "classname" "trigger_once" "model" "*39" } { "spawnflags" "1792" "origin" "480 576 0" "classname" "weapon_nailgun" } { "spawnflags" "1793" "origin" "464 728 64" "classname" "item_spikes" } { "origin" "328 848 -224" "classname" "item_health" } { "classname" "item_health" "origin" "344 920 -224" } { "spawnflags" "1" "origin" "-16 2064 -208" "classname" "item_health" } { "spawnflags" "1792" "origin" "-480 2240 -160" "classname" "item_rockets" } { "spawnflags" "1793" "origin" "-96 2456 16" "classname" "item_shells" } { "classname" "item_rockets" "origin" "-104 2216 16" "spawnflags" "1793" } { "classname" "item_artifact_invulnerability" "origin" "256 1808 -40" "spawnflags" "1792" } { "classname" "monster_army" "origin" "0 576 24" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "8 1520 -200" "angle" "270" } { "classname" "monster_dog" "origin" "88 1520 -200" "angle" "270" } { "classname" "monster_army" "origin" "224 1552 -200" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "-8 936 -200" "classname" "monster_army" } { "classname" "monster_army" "origin" "648 736 104" "spawnflags" "768" "angle" "180" } { "classname" "item_artifact_envirosuit" "origin" "712 2040 -408" "angle" "90" } { "classname" "light" "origin" "712 2040 -360" "light" "100" } { "classname" "item_rockets" "origin" "1328 2536 -528" "spawnflags" "1793" } { "classname" "item_health" "origin" "916 2416 -136" "spawnflags" "2" } { "spawnflags" "1" "classname" "monster_army" "origin" "1312 936 -248" "angle" "90" } { "classname" "monster_dog" "origin" "1336 1784 -408" "angle" "180" "spawnflags" "257" } { "spawnflags" "257" "angle" "90" "origin" "1392 928 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1384 1008 -248" "angle" "90" "spawnflags" "768" } { "spawnflags" "768" "angle" "90" "origin" "1240 1008 -248" "classname" "monster_army" } { "classname" "monster_army" "origin" "1256 1760 -408" "angle" "180" "spawnflags" "257" } { "classname" "monster_army" "origin" "824 1784 -408" "spawnflags" "257" "angle" "90" } { "classname" "monster_dog" "origin" "1128 1760 -408" "angle" "180" "spawnflags" "769" } { "classname" "path_corner" "origin" "880 2048 -168" "target" "t16" "targetname" "t17" } { "origin" "1232 2048 -232" "classname" "path_corner" "targetname" "t16" "target" "t17" } { "classname" "monster_army" "origin" "1232 2088 -216" "target" "t16" } { "classname" "monster_army" "origin" "1232 2448 -280" "angle" "270" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2464 -344" "angle" "0" "spawnflags" "256" } { "classname" "monster_army" "origin" "832 2072 -408" "angle" "90" } { "classname" "monster_dog" "origin" "840 1960 -408" "angle" "90" "spawnflags" "768" } { "classname" "trigger_multiple" "target" "t18" "health" "1" "model" "*40" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t18" "model" "*41" } { "classname" "weapon_supershotgun" "origin" "-360 2912 -80" } { "classname" "trigger_multiple" "target" "t18" "model" "*42" } { "classname" "light" "origin" "-352 2912 -24" "light" "120" } { "classname" "light" "origin" "160 3024 0" "light" "120" } { "classname" "item_shells" "origin" "528 720 80" } { "classname" "monster_army" "origin" "416 1912 -168" "angle" "180" "spawnflags" "768" } { "classname" "monster_dog" "origin" "432 2120 -168" "angle" "180" "spawnflags" "256" } { "classname" "path_corner" "origin" "248 1992 -200" "targetname" "t19" "target" "t20" } { "origin" "-200 1992 -200" "classname" "path_corner" "targetname" "t20" "target" "t21" } { "classname" "path_corner" "origin" "-136 1912 -200" "targetname" "t21" "target" "t22" } { "origin" "248 1912 -200" "classname" "path_corner" "target" "t19" "targetname" "t22" } { "classname" "monster_army" "origin" "80 2024 -184" "target" "t20" } { "classname" "monster_army" "origin" "-16 1888 -184" "spawnflags" "256" "target" "t22" } { "classname" "monster_dog" "origin" "-248 2144 -136" "spawnflags" "768" "angle" "315" } { "classname" "path_corner" "origin" "-560 2352 40" "targetname" "t23" "target" "t24" } { "origin" "-104 2352 40" "classname" "path_corner" "target" "t23" "targetname" "t24" } { "classname" "monster_army" "origin" "-432 2352 56" "spawnflags" "768" "target" "t23" } { "angle" "0" "classname" "monster_dog" "origin" "-544 2584 56" "spawnflags" "256" } { "classname" "monster_army" "origin" "-344 2656 -104" "angle" "270" } { "classname" "monster_dog" "origin" "-72 2896 -56" "spawnflags" "256" "angle" "225" } { "classname" "monster_army" "origin" "432 2920 -56" "target" "t25" } { "classname" "monster_army" "origin" "424 2832 -56" "spawnflags" "256" "angle" "180" } { "classname" "path_corner" "origin" "368 2936 -72" "targetname" "t25" "target" "t26" } { "origin" "368 2696 -72" "classname" "path_corner" "targetname" "t26" "target" "t27" } { "classname" "path_corner" "origin" "480 2696 -72" "targetname" "t27" "target" "t28" } { "origin" "480 2936 -72" "classname" "path_corner" "target" "t25" "targetname" "t28" } { "classname" "monster_army" "origin" "424 2672 -56" "target" "t27" } { "classname" "monster_army" "origin" "424 2880 -56" "angle" "180" "spawnflags" "768" } { "classname" "monster_army" "origin" "424 2760 -56" "spawnflags" "768" "angle" "180" } { "classname" "path_corner" "origin" "832 2712 -88" "targetname" "t29" "target" "t30" } { "origin" "832 2416 -104" "classname" "path_corner" "target" "t29" "targetname" "t30" } { "classname" "monster_army" "origin" "848 2584 -72" "spawnflags" "257" "target" "t29" } { "classname" "monster_army" "origin" "824 2008 -152" "angle" "90" "spawnflags" "768" } { "classname" "item_health" "origin" "-376 1704 -224" "spawnflags" "1" } { "angle" "180" "spawnflags" "768" "origin" "248 2352 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "270" "origin" "-72 2464 40" "classname" "monster_army" } { "spawnflags" "768" "angle" "225" "origin" "904 1024 -248" "classname" "monster_army" } { "light" "100" "style" "10" "classname" "light" "origin" "688 0 80" } { "message" "Shoot this secret door..." "spawnflags" "1" "angle" "0" "classname" "func_door_secret" "model" "*43" } { "origin" "672 -40 48" "classname" "item_shells" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_secret" "model" "*45" } { "classname" "trigger_secret" "model" "*46" } { "classname" "trigger_secret" "model" "*47" } { "classname" "trigger_secret" "model" "*48" } { "classname" "trigger_secret" "model" "*49" } { "light" "100" "origin" "0 632 -88" "classname" "light" } { "classname" "item_health" "origin" "600 2200 -128" "spawnflags" "1" } { "light" "220" "classname" "light" "origin" "832 1880 -504" } { "origin" "72 2056 -208" "classname" "misc_explobox" } { "light" "200" "origin" "-128 584 72" "classname" "light" } { "light" "200" "origin" "-128 568 -136" "classname" "light" } { "light" "100" "origin" "-56 632 -168" "classname" "light" } { "light" "200" "origin" "-56 864 -136" "classname" "light" } { "light" "200" "origin" "40 1672 -40" "classname" "light" } { "classname" "light" "origin" "216 1672 -40" "light" "200" } { "classname" "light" "origin" "128 1080 -152" "light" "200" } { "light" "200" "origin" "128 1096 72" "classname" "light" } { "light" "250" "classname" "light" "origin" "-352 1656 72" } { "origin" "608 1640 72" "classname" "light" "light" "250" } { "origin" "-48 1144 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "-48 1256 -320" } { "origin" "320 1256 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "312 1128 -320" } { "origin" "136 1128 -320" "classname" "light" "light" "170" } { "light" "170" "classname" "light" "origin" "136 1272 -320" } { "spawnflags" "3072" "wait" "5" "sounds" "2" "message" "You can jump across..." "classname" "trigger_multiple" "targetname" "t32" "model" "*50" } { "spawnflags" "3072" "wait" "5" "message" "You can jump up here..." "sounds" "2" "classname" "trigger_multiple" "targetname" "t31" "model" "*51" } { "light" "150" "origin" "1008 2128 -408" "classname" "light" } { "light" "250" "origin" "1312 544 -184" "classname" "light" } { "light" "200" "classname" "light" "origin" "1208 456 -184" } { "origin" "1416 456 -184" "classname" "light" "light" "200" } { "light" "170" "origin" "1312 728 -56" "classname" "light" } { "map" "e1m2" "classname" "trigger_changelevel" "model" "*52" } { "classname" "item_health" "origin" "1224 2464 -304" "spawnflags" "1" } { "classname" "light" "origin" "688 1680 -160" "light" "160" } { "light" "160" "origin" "-392 1688 -160" "classname" "light" } { "spawnflags" "768" "angle" "270" "origin" "288 1536 -200" "classname" "monster_army" } { "spawnflags" "768" "origin" "968 2432 -112" "classname" "monster_army" } { "wait" "5" "message" "Walk into the slipgate to exit." "classname" "trigger_multiple" "sounds" "2" "angle" "270" "model" "*53" } { "classname" "trigger_once" "killtarget" "t31" "target" "t31" "spawnflags" "3072" "model" "*54" } { "classname" "trigger_once" "spawnflags" "3072" "target" "t32" "killtarget" "t32" "model" "*55" } { "classname" "item_armor2" "origin" "1312 1048 -432" } { "classname" "ambient_comp_hum" "origin" "250 194 72" } { "origin" "714 194 72" "classname" "ambient_comp_hum" } { "classname" "ambient_comp_hum" "origin" "626 2058 -104" } { "origin" "466 2226 -104" "classname" "ambient_comp_hum" } { "classname" "info_intermission" "origin" "-112 704 56" "mangle" "20 45 0" } { "classname" "info_intermission" "origin" "-208 2736 192" "mangle" "20 225 0" } { "classname" "info_intermission" "origin" "240 2664 104" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1376 1936 64" "mangle" "20 135 0" } { "angle" "90" "origin" "528 -296 72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "432 -296 72" "angle" "90" } { "angle" "90" "origin" "480 -240 72" "classname" "info_player_coop" } { "classname" "func_wall" "spawnflags" "1792" "model" "*56" } { "classname" "func_wall" "spawnflags" "1792" "model" "*57" } { "classname" "ambient_drone" "origin" "1314 450 -200" } ��{ "message" "Castle of the Damned" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" "sounds" "8" } { "angle" "270" "origin" "1496 1664 296" "classname" "info_player_start" } { "origin" "1432 672 336" "classname" "light" "light" "250" } { "light" "200" "origin" "1496 888 272" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "932 640 340" } { "classname" "light_torch_small_walltorch" "origin" "1104 812 340" } { "classname" "light" "origin" "1104 640 544" "light" "300" } { "light" "175" "origin" "1216 536 353" "classname" "light" } { "light" "250" "origin" "1816 328 448" "classname" "light" } { "light" "200" "origin" "1632 472 208" "classname" "light" } { "light" "200" "origin" "1792 -392 240" "classname" "light" } { "light" "200" "origin" "1452 -124 508" "classname" "light" } { "light" "150" "origin" "1196 -124 508" "classname" "light" } { "light" "150" "origin" "1044 -124 508" "classname" "light" } { "light" "200" "origin" "756 -124 508" "classname" "light" } { "light" "250" "origin" "744 336 145" "classname" "light" } { "light" "200" "origin" "1176 -912 672" "classname" "light" } { "origin" "1328 -544 552" "classname" "light" } { "classname" "light" "origin" "1528 -912 640" "light" "200" } { "light" "200" "classname" "light" "origin" "880 -648 672" } { "origin" "240 -264 392" "classname" "light" "light" "250" } { "classname" "light" "origin" "-352 -504 464" "light" "200" } { "origin" "-448 -608 804" "classname" "light" "light" "450" } { "light" "250" "origin" "776 -912 472" "classname" "light" } { "light" "300" "origin" "1630 -806 428" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "1528 -912 464" "classname" "light" } { "classname" "light" "origin" "1180 -484 560" } { "classname" "light" "origin" "1184 -612 560" } { "classname" "light" "origin" "1016 -368 472" "light" "100" } { "classname" "light" "origin" "1016 -464 472" "light" "100" } { "classname" "light" "origin" "1020 -560 472" "light" "100" } { "classname" "light" "origin" "1208 -776 472" "light" "100" } { "classname" "light" "origin" "1288 -776 472" "light" "100" } { "classname" "light" "origin" "1360 -776 472" "light" "100" } { "light" "200" "origin" "1792 120 376" "classname" "light" } { "origin" "1538 182 356" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "1640 80 360" "classname" "light" } { "light" "200" "origin" "1928 80 360" "classname" "light" } { "light" "250" "origin" "1792 296 208" "classname" "light" } { "light" "150" "origin" "1800 40 160" "classname" "light" } { "light" "200" "origin" "1776 -392 160" "classname" "light" } { "light" "200" "origin" "1304 -392 152" "classname" "light" } { "light" "250" "origin" "1632 112 136" "classname" "light" } { "light" "250" "origin" "1432 312 136" "classname" "light" } { "light" "200" "origin" "1136 -656 160" "classname" "light" } { "light" "200" "origin" "1136 -416 160" "classname" "light" } { "light" "250" "origin" "1448 -552 160" "classname" "light" } { "light" "200" "origin" "1920 440 136" "classname" "light" } { "light" "200" "origin" "968 88 177" "classname" "light" } { "light" "300" "origin" "1088 312 129" "classname" "light" } { "light" "150" "origin" "1376 168 129" "classname" "light" } { "light" "250" "origin" "112 -384 392" "classname" "light" } { "origin" "300 -1004 508" "classname" "light" } { "origin" "296 -812 505" "classname" "light" } { "origin" "300 -1204 505" "classname" "light" } { "light" "150" "origin" "470 -1006 468" "classname" "light_torch_small_walltorch" } { "light" "250" "origin" "984 -1216 496" "classname" "light" } { "light" "250" "origin" "888 -1128 552" "classname" "light" } { "light" "200" "origin" "800 -1216 592" "classname" "light" } { "light" "200" "origin" "664 -1216 592" "classname" "light" } { "light" "200" "origin" "584 -1136 592" "classname" "light" } { "light" "200" "origin" "584 -968 592" "classname" "light" } { "light" "250" "origin" "584 -744 592" "classname" "light" } { "light" "200" "origin" "528 -1144 464" "classname" "light" } { "light" "200" "origin" "528 -856 464" "classname" "light" } { "light" "200" "classname" "light" "origin" "1496 1544 440" } { "origin" "1384 1392 440" "classname" "light" "light" "250" } { "origin" "1496 1104 520" "classname" "light" } { "origin" "1608 1400 440" "classname" "light" "light" "250" } { "light" "250" "origin" "1240 1712 360" "classname" "light" } { "light" "250" "origin" "1744 1696 360" "classname" "light" } { "classname" "light" "origin" "1384 1136 440" "light" "250" } { "classname" "light" "origin" "1608 1144 440" "light" "250" } { "classname" "path_corner" "origin" "1168 736 296" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "992 744 296" "targetname" "t6" "target" "t7" } { "classname" "path_corner" "origin" "1000 544 296" "targetname" "t7" "target" "t34" } { "classname" "item_health" "origin" "960 704 288" } { "classname" "item_shells" "origin" "952 512 288" } { "classname" "path_corner" "origin" "1344 -128 304" "targetname" "t9" "target" "t8" } { "classname" "path_corner" "origin" "898 -128 304" "targetname" "t8" "target" "t9" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1018 -126 320" "angle" "0" "target" "t8" } { "classname" "item_health" "origin" "1344 -224 296" "spawnflags" "1" } { "classname" "item_health" "origin" "1400 -224 296" "spawnflags" "1" } { "origin" "1528 192 296" "classname" "item_shells" } { "classname" "path_corner" "origin" "1496 1040 184" "targetname" "t22" "target" "t23" } { "classname" "path_corner" "origin" "1496 840 248" "targetname" "t23" "target" "t33" } { "spawnflags" "1" "classname" "item_shells" "origin" "1056 -648 288" } { "classname" "item_health" "origin" "1184 -736 288" } { "spawnflags" "257" "classname" "monster_army" "origin" "1646 -698 360" "angle" "180" "targetname" "t89" } { "classname" "path_corner" "origin" "1400 640 272" "targetname" "t30" "target" "t79" } { "classname" "path_corner" "origin" "1496 752 232" "targetname" "t33" "target" "t77" } { "classname" "path_corner" "origin" "1192 560 296" "targetname" "t34" "target" "t80" } { "classname" "item_shells" "origin" "1616 1280 176" } { "classname" "item_health" "origin" "1056 -840 416" "spawnflags" "1" } { "classname" "item_health" "origin" "1104 -840 416" "spawnflags" "1" } { "spawnflags" "1" "classname" "monster_army" "origin" "262 -458 320" "angle" "0" "target" "t96" } { "spawnflags" "1024" "classname" "item_health" "origin" "136 -296 296" } { "classname" "path_corner" "origin" "-536 -704 472" "targetname" "t42" "target" "t41" } { "classname" "path_corner" "origin" "-576 -416 472" "targetname" "t41" "target" "t42" } { "classname" "monster_knight" "origin" "-578 -654 480" "target" "t41" "spawnflags" "1" } { "classname" "item_shells" "origin" "-368 -752 456" } { "classname" "item_health" "origin" "-16 -520 360" "spawnflags" "1" } { "classname" "item_health" "origin" "-16 -576 360" "spawnflags" "1" } { "classname" "light" "origin" "1848 -568 320" "light" "200" } { "classname" "light" "origin" "1760 -560 408" "light" "200" } { "classname" "light" "origin" "1624 -560 352" "light" "150" } { "targetname" "t43" "angle" "270" "origin" "800 368 312" "classname" "info_teleport_destination" } { "origin" "752 168 296" "classname" "item_health" } { "target" "t43" "classname" "trigger_teleport" "model" "*1" } { "origin" "1712 -568 256" "classname" "item_health" } { "classname" "monster_ogre" "origin" "1494 1134 208" "angle" "270" "target" "t22" } { "light" "300" "origin" "1856 1288 384" "classname" "light" } { "classname" "light" "origin" "1136 1288 384" } { "light" "200" "origin" "1920 328 380" "classname" "light" } { "target" "t122" "spawnflags" "2048" "sounds" "1" "classname" "item_key1" "origin" "880 -300 464" } { "light" "300" "origin" "648 -384 430" "classname" "light_flame_small_yellow" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1104 -224 406" } { "light" "250" "origin" "1456 -128 406" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "988 532 353" "light" "175" } { "light" "125" "origin" "1100 648 328" "classname" "light" } { "origin" "1616 936 310" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "1360 936 310" } { "origin" "1792 504 390" "classname" "light_flame_small_yellow" "light" "300" } { "origin" "1972 -252 332" "classname" "info_null" "targetname" "t47" } { "light" "800" "origin" "1992 -252 336" "classname" "light" "target" "t47" } { "classname" "info_null" "origin" "1948 -292 332" "targetname" "t48" } { "light" "800" "classname" "light" "origin" "1948 -312 336" "target" "t48" } { "origin" "880 -328 562" "classname" "light_flame_small_yellow" "light" "300" } { "classname" "light" "origin" "1056 -1288 504" } { "origin" "1184 -1288 504" "classname" "light" } { "classname" "light" "origin" "1312 -1288 504" } { "origin" "1440 -1288 504" "classname" "light" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t50" "model" "*2" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t50" "model" "*3" } { "classname" "trigger_once" "target" "t50" "model" "*4" } { "classname" "light" "origin" "1368 -1016 504" "light" "200" } { "light" "200" "origin" "1120 -1024 504" "classname" "light" } { "classname" "light" "origin" "1248 -1184 464" "light" "175" } { "classname" "light" "origin" "776 -480 480" "light" "225" } { "classname" "light" "origin" "1904 -144 168" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1706 -206 316" "light" "300" } { "light" "300" "origin" "2134 -34 316" "classname" "light_torch_small_walltorch" } { "origin" "1152 -296 422" "classname" "light_flame_small_yellow" "light" "250" } { "light" "250" "classname" "light_flame_small_yellow" "origin" "1152 -760 422" } { "origin" "1528 -556 478" "classname" "light_flame_small_yellow" "light" "250" } { "targetname" "t52" "origin" "1532 -552 328" "classname" "info_null" } { "origin" "1340 -544 384" "classname" "item_armor2" } { "sounds" "1" "targetname" "t53" "lip" "64" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*5" } { "sounds" "1" "targetname" "t53" "classname" "func_door" "angle" "-1" "wait" "-1" "lip" "64" "model" "*6" } { "targetname" "t53" "target" "t54" "classname" "trigger_teleport" "spawnflags" "2" "model" "*7" } { "targetname" "t54" "angle" "180" "origin" "1408 -688 449" "classname" "info_teleport_destination" } { "targetname" "t53" "target" "t57" "classname" "trigger_teleport" "spawnflags" "2" "model" "*8" } { "targetname" "t57" "angle" "180" "origin" "1408 -400 361" "classname" "info_teleport_destination" } { "spawnflags" "768" "targetname" "t53" "angle" "180" "origin" "1912 -856 217" "classname" "monster_wizard" } { "spawnflags" "768" "targetname" "t53" "classname" "monster_wizard" "origin" "1912 -936 217" "angle" "180" } { "targetname" "t50" "angle" "90" "origin" "1320 -1112 441" "classname" "monster_knight" } { "spawnflags" "256" "targetname" "t50" "angle" "0" "origin" "1056 -1144 441" "classname" "monster_knight" } { "sounds" "1" "targetname" "t61" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*9" } { "sounds" "3" "lip" "64" "spawnflags" "1" "targetname" "t58" "angle" "270" "wait" "-1" "classname" "func_door" "model" "*10" } { "sounds" "1" "wait" "-1" "angle" "270" "target" "t58" "classname" "func_button" "model" "*11" } { "target" "t61" "classname" "trigger_once" "model" "*12" } { "light" "225" "origin" "984 -480 480" "classname" "light" } { "light" "175" "origin" "880 -368 176" "classname" "light" } { "classname" "light" "origin" "880 -592 240" "light" "175" } { "light" "200" "origin" "880 -488 184" "classname" "light" } { "light" "150" "origin" "880 -304 472" "classname" "light" } { "classname" "light" "origin" "-96 308 864" "light" "850" } { "origin" "-32 -440 624" "classname" "light" } { "sounds" "1" "targetname" "t73" "wait" "-1" "lip" "196" "angle" "-1" "classname" "func_door" "model" "*13" } { "light" "300" "origin" "104 144 688" "classname" "light" } { "classname" "light" "origin" "-264 144 688" "light" "300" } { "sounds" "1" "targetname" "t73" "wait" "-1" "classname" "func_door" "angle" "-1" "lip" "196" "model" "*14" } { "classname" "light_flame_small_yellow" "origin" "-24 -232 414" "light" "250" } { "lip" "-2" "sounds" "3" "speed" "350" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*15" } { "target" "t63" "targetname" "t62" "origin" "-12 312 264" "classname" "path_corner" } { "target" "t64" "targetname" "t63" "origin" "-12 312 356" "classname" "path_corner" } { "wait" "-1" "target" "t66" "targetname" "t64" "classname" "path_corner" "origin" "-13 440 355" } { "sounds" "1" "targetname" "t71" "wait" "-1" "target" "t65" "angle" "-2" "classname" "func_button" "model" "*16" } { "target" "t64" "targetname" "t66" "origin" "-13 440 355" "classname" "path_corner" } { "light" "200" "origin" "-96 440 376" "classname" "light" } { "light" "150" "origin" "8 456 376" "classname" "light" } { "targetname" "t70" "target" "t67" "classname" "path_corner" "origin" "-220 312 264" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "-220 312 356" } { "wait" "-1" "target" "t69" "targetname" "t68" "origin" "-221 440 355" "classname" "path_corner" } { "target" "t68" "targetname" "t69" "classname" "path_corner" "origin" "-221 440 355" } { "classname" "light" "origin" "-200 456 376" "light" "150" } { "targetname" "t65" "target" "t62" "classname" "func_train" "speed" "50" "sounds" "1" "model" "*17" } { "light" "250" "origin" "-96 632 406" "classname" "light_flame_small_yellow" } { "targetname" "t72" "origin" "-96 288 304" "classname" "info_null" } { "light" "450" "target" "t72" "origin" "-96 288 368" "classname" "light" } { "target" "t70" "targetname" "t65" "speed" "50" "classname" "func_train" "sounds" "1" "model" "*18" } { "lip" "-2" "sounds" "0" "speed" "350" "classname" "func_door" "wait" "-1" "angle" "0" "model" "*19" } { "targetname" "t65" "delay" "4.7" "target" "t73" "classname" "trigger_once" "model" "*20" } { "targetname" "t73" "angle" "270" "origin" "-96 552 320" "classname" "monster_demon1" "spawnflags" "1024" } { "targetname" "t74" "angle" "90" "origin" "132 -192 476" "classname" "info_teleport_destination" } { "targetname" "t75" "classname" "info_teleport_destination" "origin" "-328 -196 476" "angle" "90" } { "target" "t75" "classname" "trigger_teleport" "spawnflags" "1" "model" "*21" } { "target" "t74" "classname" "trigger_teleport" "spawnflags" "1" "model" "*22" } { "light" "200" "origin" "-418 306 356" "classname" "light" } { "classname" "light" "origin" "260 308 356" "light" "200" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*23" } { "sounds" "0" "targetname" "t73" "wait" "-1" "angle" "0" "classname" "func_door" "model" "*24" } { "sounds" "0" "wait" "-1" "angle" "0" "targetname" "t73" "classname" "func_door" "model" "*25" } { "sounds" "0" "targetname" "t73" "angle" "180" "wait" "-1" "classname" "func_door" "model" "*26" } { "sounds" "3" "wait" "-1" "angle" "-2" "targetname" "t73" "classname" "func_door" "model" "*27" } { "classname" "light" "origin" "-96 24 360" "light" "100" } { "light" "100" "origin" "-96 -40 360" "classname" "light" } { "classname" "light" "origin" "-160 -568 624" } { "origin" "-160 -440 624" "classname" "light" } { "classname" "light" "origin" "-32 -568 624" } { "classname" "light" "origin" "-96 -88 484" "light" "150" } { "classname" "light" "origin" "-440 -408 804" "light" "450" } { "classname" "light" "origin" "600 -128 352" "light" "200" } { "classname" "light" "origin" "576 -608 504" "light" "250" } { "classname" "light" "origin" "384 -504 392" "light" "250" } { "classname" "light" "origin" "1264 240 295" "light" "250" } { "light" "250" "origin" "944 240 295" "classname" "light" } { "classname" "path_corner" "origin" "1480 704 264" "targetname" "t77" "target" "t78" } { "classname" "path_corner" "origin" "1448 656 264" "targetname" "t78" "target" "t30" } { "classname" "path_corner" "origin" "1264 640 304" "targetname" "t80" "target" "t5" } { "classname" "path_corner" "origin" "1328 640 304" "targetname" "t79" "target" "t80" } { "light" "200" "origin" "1488 -392 216" "classname" "light" } { "classname" "path_corner" "origin" "816 80 304" "targetname" "t83" "target" "t82" "spawnflags" "256" } { "origin" "816 312 304" "classname" "path_corner" "targetname" "t82" "target" "t83" "spawnflags" "256" } { "classname" "monster_army" "origin" "806 206 320" "angle" "90" "target" "t82" "spawnflags" "256" } { "classname" "trigger_once" "target" "t84" "model" "*28" } { "classname" "monster_ogre" "origin" "1790 -146 312" "angle" "90" "targetname" "t84" } { "classname" "path_corner" "origin" "1088 -672 296" "target" "t85" "targetname" "t88" } { "origin" "1088 -376 296" "classname" "path_corner" "targetname" "t85" "target" "t86" } { "classname" "path_corner" "origin" "1088 -376 296" "targetname" "t87" "target" "t88" } { "origin" "1448 -376 296" "classname" "path_corner" "targetname" "t86" "target" "t87" } { "spawnflags" "1" "classname" "monster_ogre" "origin" "1086 -498 312" "angle" "270" "target" "t88" } { "spawnflags" "256" "classname" "trigger_once" "target" "t89" "model" "*29" } { "classname" "item_health" "origin" "352 -752 408" "spawnflags" "1025" } { "spawnflags" "1025" "origin" "352 -792 408" "classname" "item_health" } { "classname" "item_health" "origin" "352 -832 408" "spawnflags" "1" } { "classname" "path_corner" "origin" "408 -776 416" "targetname" "t94" "target" "t95" } { "origin" "400 -1088 416" "classname" "path_corner" "targetname" "t95" "target" "t94" } { "classname" "path_corner" "origin" "584 -1096 416" "targetname" "t92" "target" "t93" } { "origin" "584 -792 416" "classname" "path_corner" "targetname" "t93" "target" "t92" } { "classname" "monster_army" "origin" "390 -970 432" "angle" "0" "target" "t94" } { "classname" "monster_army" "origin" "566 -970 432" "angle" "270" "target" "t92" } { "classname" "path_corner" "origin" "208 -304 304" "targetname" "t97" "target" "t96" } { "classname" "path_corner" "origin" "208 -464 304" "targetname" "t96" "target" "t97" } { "spawnflags" "1280" "classname" "path_corner" "origin" "-344 160 304" "targetname" "t100" "target" "t99" } { "spawnflags" "1280" "origin" "168 152 304" "classname" "path_corner" "targetname" "t99" "target" "t100" } { "spawnflags" "1280" "classname" "monster_ogre" "origin" "240 152 320" "angle" "180" "target" "t99" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "-392 80 320" "angle" "0" "targetname" "t101" } { "spawnflags" "768" "classname" "trigger_once" "target" "t101" "model" "*30" } { "classname" "item_health" "origin" "40 -16 464" } { "origin" "80 -48 464" "classname" "item_health" } { "origin" "520 -72 296" "classname" "item_shells" } { "spawnflags" "1" "origin" "-424 -216 296" "classname" "item_shells" } { "spawnflags" "769" "angle" "270" "origin" "880 -400 568" "classname" "monster_wizard" } { "light" "200" "origin" "432 176 152" "classname" "light" } { "light" "150" "origin" "432 -56 256" "classname" "light" } { "origin" "264 -96 300" "classname" "item_health" } { "classname" "item_health" "origin" "264 -140 300" } { "spawnflags" "1" "origin" "1184 1568 240" "classname" "item_health" } { "classname" "item_health" "origin" "1184 1616 240" "spawnflags" "1" } { "light" "150" "origin" "1496 1112 108" "classname" "light" } { "light" "200" "origin" "1120 1152 96" "classname" "light" } { "light" "200" "origin" "1080 692 184" "classname" "light" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "832 1184 294" } { "origin" "464 536 358" "classname" "light_flame_small_yellow" "light" "300" } { "light" "300" "classname" "light_flame_small_yellow" "origin" "600 704 334" } { "light" "150" "origin" "1736 1096 110" "classname" "light" } { "light" "100" "origin" "832 1056 134" "classname" "light" } { "light" "150" "origin" "784 704 294" "classname" "light" } { "origin" "856 592 182" "classname" "item_health" } { "classname" "item_health" "origin" "824 552 182" } { "classname" "info_player_deathmatch" "origin" "-416 -144 320" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "168 -480 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1496 1328 200" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "1936 -136 312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "936 -1216 432" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "792 -992 440" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "1080 -720 312" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "408 -752 432" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "792 -208 320" "angle" "45" } { "classname" "info_player_deathmatch" "origin" "784 808 206" "angle" "225" } { "sounds" "3" "wait" "3" "angle" "90" "classname" "func_door" "model" "*31" } { "sounds" "0" "wait" "3" "angle" "270" "classname" "func_door" "model" "*32" } { "spawnflags" "1" "origin" "680 832 182" "classname" "item_shells" } { "origin" "1392 240 300" "classname" "weapon_supershotgun" } { "spawnflags" "769" "angle" "270" "origin" "954 -754 444" "classname" "monster_ogre" } { "spawnflags" "1" "origin" "520 -1280 408" "classname" "item_shells" } { "light" "200" "origin" "-612 -500 548" "classname" "light" } { "classname" "func_door" "angle" "91" // svdijk -- changed to prevent z-fighting (was "90") "targetname" "t110" "wait" "-1" "model" "*33" } { "sounds" "3" "classname" "func_door" "angle" "269" // svdijk -- changed to prevent z-fighting (was "270") "wait" "-1" "model" "*34" } { "classname" "trigger_once" "target" "t110" "model" "*35" } { "classname" "trigger_changelevel" "map" "e1m3" "model" "*36" } { "spawnflags" "1792" "origin" "680 728 184" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "1496 1256 176" "classname" "weapon_nailgun" } { "angle" "180" "spawnflags" "1792" "origin" "-96 -496 360" "classname" "weapon_supernailgun" } { "spawnflags" "1794" "origin" "-112 -8 464" "classname" "item_health" } { "spawnflags" "1793" "origin" "-112 -568 360" "classname" "item_spikes" } { "spawnflags" "1792" "origin" "1616 1424 176" "classname" "item_spikes" } { "spawnflags" "1792" "classname" "item_spikes" "origin" "1656 1424 176" } { "spawnflags" "1792" "origin" "1696 1424 176" "classname" "item_spikes" } { "spawnflags" "768" "target" "t34" "angle" "315" "origin" "1070 646 312" "classname" "monster_ogre" } { "spawnflags" "768" "targetname" "t84" "angle" "90" "origin" "1624 88 376" "classname" "monster_wizard" } { "spawnflags" "768" "angle" "90" "targetname" "t84" "origin" "1866 -378 312" "classname" "monster_ogre" } { "angle" "45" "origin" "1088 -1096 440" "classname" "monster_knight" "targetname" "t50" } { "spawnflags" "768" "classname" "monster_knight" "origin" "1400 -1144 440" "angle" "90" "targetname" "t50" } { "spawnflags" "256" "target" "t111" "targetname" "t112" "origin" "896 -1216 416" "classname" "path_corner" } { "spawnflags" "256" "target" "t112" "targetname" "t111" "classname" "path_corner" "origin" "704 -1216 416" } { "spawnflags" "257" "target" "t111" "angle" "180" "origin" "758 -1218 432" "classname" "monster_army" } { "spawnflags" "768" "target" "t114" "targetname" "t113" "origin" "-96 -520 368" "classname" "path_corner" } { "spawnflags" "768" "target" "t113" "targetname" "t114" "origin" "-96 -152 304" "classname" "path_corner" } { "targetname" "t116" "spawnflags" "769" "target" "t113" "angle" "270" "origin" "-98 -194 320" "classname" "monster_ogre" } { "spawnflags" "1536" "origin" "1936 -96 289" "classname" "item_health" } { "spawnflags" "1025" "origin" "1040 -1200 417" "classname" "item_health" } { "spawnflags" "769" "target" "t117" "angle" "315" "origin" "-560 -312 592" "classname" "monster_wizard" } { "spawnflags" "768" "target" "t118" "targetname" "t117" "origin" "-528 -344 576" "classname" "path_corner" } { "spawnflags" "768" "target" "t117" "targetname" "t118" "origin" "-352 -656 576" "classname" "path_corner" } { "classname" "light" "origin" "1360 976 224" "light" "150" } { "light" "150" "origin" "1616 976 224" "classname" "light" } { "classname" "light" "origin" "1208 1296 368" "light" "250" } { "origin" "1784 1288 368" "classname" "light" "light" "250" } { "classname" "light" "origin" "1496 1664 336" "light" "250" } { "classname" "light" "origin" "1752 1176 112" "light" "150" } { "light" "200" "origin" "1776 976 112" "classname" "light" } { "classname" "light" "origin" "1216 976 112" "light" "200" } { "light" "150" "origin" "1224 1176 112" "classname" "light" } { "classname" "light" "origin" "1496 1432 520" "light" "250" } { "classname" "light" "origin" "1496 1304 264" "light" "200" } { "classname" "light" "origin" "1496 1432 288" "light" "200" } { "classname" "light" "origin" "1608 1120 88" "light" "150" } { "light" "150" "origin" "1384 1120 88" "classname" "light" } { "classname" "light" "origin" "1496 864 368" "light" "150" } { "light" "175" "origin" "980 764 353" "classname" "light" } { "classname" "light" "origin" "1228 764 353" "light" "175" } { "classname" "light" "origin" "1104 464 353" "light" "200" } { "classname" "light" "origin" "1104 -40 423" "light" "200" } { "light" "150" "origin" "1416 -128 367" "classname" "light" } { "classname" "light" "origin" "1104 -184 367" "light" "200" } { "classname" "light" "origin" "1184 56 423" "light" "150" } { "light" "150" "origin" "1024 56 423" "classname" "light" } { "classname" "light" "origin" "1272 -64 399" "light" "150" } { "light" "150" "origin" "888 -64 399" "classname" "light" } { "classname" "light" "origin" "1104 152 129" "light" "300" } { "classname" "light" "origin" "976 392 129" "light" "200" } { "classname" "light" "origin" "1104 656 120" } { "classname" "light" "origin" "896 712 144" "light" "200" } { "classname" "light" "origin" "640 704 280" "light" "200" } { "classname" "light" "origin" "464 496 296" "light" "150" } { "classname" "light" "origin" "888 1152 96" "light" "200" } { "classname" "light" "origin" "840 880 240" "light" "200" } { "classname" "light" "origin" "848 584 240" "light" "150" } { "classname" "light" "origin" "784 160 144" "light" "200" } { "classname" "light" "origin" "440 336 144" "light" "150" } { "light" "150" "origin" "584 336 144" "classname" "light" } { "classname" "light" "origin" "432 24 136" "light" "150" } { "classname" "light" "origin" "656 328 224" "light" "200" } { "classname" "light" "origin" "432 -128 312" "light" "200" } { "classname" "light" "origin" "600 -384 360" "light" "200" } { "origin" "520 -128 406" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light" "origin" "424 -320 352" "light" "200" } { "classname" "light" "origin" "664 -1216 472" "light" "150" } { "classname" "light" "origin" "336 -1208 504" "light" "150" } { "light" "150" "origin" "336 -1008 504" "classname" "light" } { "classname" "light" "origin" "336 -816 504" "light" "150" } { "classname" "light" "origin" "880 -1000 496" "light" "200" } { "classname" "light" "origin" "880 -792 496" "light" "200" } { "classname" "light" "origin" "880 -376 304" "light" "200" } { "classname" "light" "origin" "1048 -912 480" "light" "225" } { "classname" "light" "origin" "1120 -1192 468" "light" "150" } { "light" "150" "origin" "1376 -1192 468" "classname" "light" } { "classname" "light" "origin" "1472 -912 464" "light" "175" } { "classname" "light" "origin" "880 -304 480" "light" "100" } { "classname" "light" "origin" "880 -680 480" "light" "175" } { "classname" "light" "origin" "1600 -704 484" "light" "150" } { "classname" "light" "origin" "1504 -704 348" "light" "175" } { "light" "175" "origin" "1336 -704 348" "classname" "light" } { "classname" "light" "origin" "1152 -640 332" "light" "200" } { "classname" "light" "origin" "1096 -552 348" "light" "150" } { "light" "200" "origin" "1160 -456 332" "classname" "light" } { "light" "150" "origin" "1216 -384 348" "classname" "light" } { "classname" "light" "origin" "1344 -384 348" "light" "150" } { "classname" "light" "origin" "1544 392 156" "light" "225" } { "light" "225" "origin" "1848 248 156" "classname" "light" } { "classname" "light" "origin" "1936 136 156" "light" "225" } { "light" "200" "origin" "2096 -80 156" "classname" "light" } { "light" "200" "origin" "2048 -408 156" "classname" "light" } { "classname" "light" "origin" "1456 -392 444" "light" "225" } { "classname" "light" "origin" "1640 -384 352" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "2134 -474 316" "light" "250" } { "light" "200" "origin" "168 216 496" "classname" "light" } { "classname" "light" "origin" "-328 208 496" "light" "200" } { "light" "200" "origin" "-96 360 432" "classname" "light" } { "classname" "light" "origin" "-96 144 432" "light" "200" } { "light" "200" "origin" "-376 32 432" "classname" "light" } { "light" "200" "origin" "208 -72 432" "classname" "light" } { "light" "150" "origin" "-96 72 360" "classname" "light" } { "light" "150" "origin" "-64 -232 368" "classname" "light" } { "light" "150" "origin" "-96 -320 560" "classname" "light" } { "light" "250" "origin" "-96 -496 448" "classname" "light" } { "light" "150" "origin" "-416 -104 392" "classname" "light" } { "light" "150" "origin" "-344 -152 528" "classname" "light" } { "classname" "light" "origin" "160 -152 528" "light" "150" } { "light" "150" "origin" "-96 8 528" "classname" "light" } { "light" "200" "origin" "-560 -504 688" "classname" "light" } { "classname" "light" "origin" "-440 -368 688" "light" "200" } { "light" "200" "origin" "-440 -656 688" "classname" "light" } { "classname" "light" "origin" "-336 -504 688" "light" "200" } { "classname" "light" "origin" "2084 -208 336" "light" "100" } { "light" "100" "origin" "2012 -252 332" "classname" "light" } { "classname" "light" "origin" "1948 -328 332" "light" "100" } { "light" "150" "origin" "1892 -452 332" "classname" "light" } { "sounds" "1" "targetname" "t120" "wait" "-1" "angle" "-2" "classname" "func_door" "lip" "4" "model" "*37" } { "target" "t120" "classname" "trigger_once" "model" "*38" } { "light" "100" "origin" "2076 -312 336" "classname" "light" } { "sounds" "3" "spawnflags" "2064" "angle" "0" "wait" "-1" "classname" "func_door" "model" "*39" } { "spawnflags" "2064" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*40" } { "light" "100" "origin" "332 -264 356" "classname" "light" } { "classname" "light" "origin" "144 -264 356" "light" "100" } { "light" "100" "origin" "1104 572 316" "classname" "light" } { "target" "t121" "wait" ".8" "classname" "trigger_multiple" "model" "*41" } { "targetname" "t121" "angle" "180" "origin" "2120 -256 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "targetname" "t121" "angle" "90" "origin" "1944 -456 332" "classname" "trap_spikeshooter" "spawnflags" "1024" } { "light" "150" "origin" "1312 -856 472" "classname" "light" } { "classname" "light" "origin" "1184 -856 472" "light" "175" } { "classname" "light" "origin" "1560 -568 224" "light" "200" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "50" "sounds" "1" "targetname" "t123" "lip" "6" "model" "*42" } { "classname" "trigger_once" "target" "t123" "model" "*43" } { "classname" "light" "origin" "1496 -552 330" "light" "700" "target" "t52" } { "classname" "light" "origin" "1288 80 140" "light" "250" } { "classname" "light" "origin" "1288 400 80" "light" "200" } { "classname" "light" "origin" "1328 -664 160" "light" "200" } { "classname" "item_armor1" "origin" "784 56 304" } { "classname" "light" "origin" "1544 464 352" "light" "75" } { "classname" "func_plat" "model" "*44" } { "classname" "light" "origin" "1496 1192 280" "light" "200" } { "classname" "light" "origin" "1608 1192 136" "light" "100" } { "light" "100" "origin" "1384 1184 136" "classname" "light" } { "light" "100" "origin" "1608 1048 136" "classname" "light" } { "classname" "light" "origin" "1384 1048 136" "light" "100" } { "classname" "light" "origin" "1200 1148 92" "light" "150" } { "light" "150" "origin" "876 -184 367" "classname" "light" } { "classname" "light" "origin" "768 -128 384" "light" "200" } { "classname" "light" "origin" "1104 388 552" "light" "250" } { "light" "200" "origin" "1392 240 384" "classname" "light" } { "classname" "light" "origin" "1392 80 368" "light" "200" } { "classname" "light" "origin" "1272 400 367" "light" "150" } { "light" "150" "origin" "920 400 367" "classname" "light" } { "classname" "light" "origin" "816 208 368" "light" "200" } { "classname" "light" "origin" "800 24 368" "light" "200" } { "classname" "light" "origin" "800 376 385" "light" "150" } { "light" "150" "origin" "1400 400 385" "classname" "light" } { "classname" "path_corner" "origin" "1104 336 300" "target" "t126" "targetname" "t127" } { "origin" "1104 24 300" "classname" "path_corner" "targetname" "t126" "target" "t127" } { "classname" "monster_army" "origin" "1104 424 316" "angle" "270" "target" "t127" } { "targetname" "t128" "origin" "1392 240 308" "classname" "info_null" } { "light" "300" "target" "t128" "origin" "1392 240 376" "classname" "light" } { "targetname" "t129" "angle" "0" "origin" "552 -128 320" "classname" "monster_army" } { "target" "t129" "classname" "trigger_once" "model" "*45" } { "sounds" "1" "classname" "trigger_secret" "model" "*46" } { "sounds" "1" "classname" "trigger_secret" "model" "*47" } { "classname" "light" "origin" "1104 24 536" "light" "350" } { "spawnflags" "1" "classname" "func_door_secret" "angle" "270" "model" "*48" } { "light" "200" "origin" "1680 1552 320" "classname" "light" } { "classname" "light" "origin" "1312 1552 320" "light" "200" } { "classname" "item_spikes" "origin" "1480 1104 68" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1760 -568 256" "spawnflags" "1" } { "classname" "item_spikes" "origin" "1232 -1200 416" } { "message" "This door is opened elsewhere..." "classname" "func_door" "sounds" "3" "angle" "180" "wait" "-1" "targetname" "t122" "speed" "35" "spawnflags" "2048" "model" "*49" } { "classname" "func_door" "angle" "0" "wait" "-1" "speed" "30" "spawnflags" "2048" "model" "*50" } { "classname" "light" "origin" "1496 1600 296" "light" "150 " } { "light" "150" "origin" "1568 1664 296" "classname" "light" } { "classname" "light" "origin" "1424 1664 296" "light" "150" } { "classname" "light" "origin" "1328 1424 296" "light" "200" } { "light" "250" "origin" "1696 1416 296" "classname" "light" } { "classname" "monster_army" "origin" "1592 1296 200" "angle" "270" } { "spawnflags" "768" "classname" "monster_demon1" "origin" "-96 576 320" "angle" "270" "targetname" "t73" "target" "t143" } { "classname" "path_corner" "origin" "1392 416 304" "targetname" "t131" "target" "t130" "spawnflags" "768" } { "origin" "1392 296 304" "classname" "path_corner" "targetname" "t130" "target" "t131" "spawnflags" "768" } { "classname" "monster_army" "origin" "1392 352 320" "angle" "270" "target" "t130" "spawnflags" "768" } { "target" "t132" "targetname" "t133" "origin" "296 -328 304" "classname" "path_corner" } { "target" "t133" "targetname" "t132" "classname" "path_corner" "origin" "472 -416 304" } { "spawnflags" "1" "target" "t132" "angle" "90" "origin" "472 -456 320" "classname" "monster_army" } { "spawnflags" "1" "targetname" "t89" "angle" "135" "origin" "1712 -784 376" "classname" "monster_army" } { "target" "t135" "spawnflags" "256" "targetname" "t134" "origin" "400 -1128 416" "classname" "path_corner" } { "target" "t134" "spawnflags" "256" "targetname" "t135" "classname" "path_corner" "origin" "400 -1248 416" } { "target" "t134" "spawnflags" "257" "angle" "90" "origin" "408 -1208 432" "classname" "monster_army" } { "targetname" "t101" "angle" "90" "origin" "-288 -24 488" "classname" "monster_army" "spawnflags" "768" } { "spawnflags" "768" "targetname" "t101" "classname" "monster_army" "origin" "136 -128 488" "angle" "90" } { "spawnflags" "1792" "origin" "-264 -24 464" "classname" "item_rockets" } { "spawnflags" "2048" "origin" "-240 -8 464" "classname" "item_spikes" } { "classname" "monster_ogre" "origin" "-304 -304 488" "angle" "225" "spawnflags" "769" } { "classname" "func_wall" "spawnflags" "768" "model" "*51" } { "classname" "trap_spikeshooter" "origin" "2048 -48 332" "angle" "270" "spawnflags" "769" "targetname" "t121" } { "origin" "2048 -476 332" "classname" "info_null" "targetname" "t136" } { "style" "32" "origin" "2048 -456 336" "classname" "light" "light" "800" "spawnflags" "1" "target" "t136" "targetname" "t137" } { "style" "32" "classname" "trigger_once" "spawnflags" "768" "target" "t137" "model" "*52" } { "classname" "monster_wizard" "origin" "672 328 384" "angle" "180" "spawnflags" "768" "targetname" "t138" } { "classname" "trigger_once" "target" "t138" "model" "*53" } { "style" "32" "classname" "light" "origin" "2004 -52 332" "light" "100" "spawnflags" "1" "targetname" "t137" } { "classname" "item_shells" "origin" "1416 224 300" "spawnflags" "768" } { "classname" "path_corner" "origin" "-344 136 304" "targetname" "t139" "target" "t140" "spawnflags" "768" } { "origin" "168 128 304" "classname" "path_corner" "target" "t139" "targetname" "t140" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "-400 168 320" "spawnflags" "768" "target" "t139" } { "classname" "trap_spikeshooter" "origin" "2120 -256 332" "angle" "180" "spawnflags" "769" "targetname" "t121" } { "classname" "trap_spikeshooter" "origin" "1944 -456 332" "targetname" "t121" "angle" "90" "spawnflags" "769" } { "classname" "item_spikes" "origin" "-336 -80 470" "spawnflags" "768" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t142" "spawnflags" "2" "model" "*54" } { "targetname" "t143" "classname" "trigger_teleport" "target" "t141" "spawnflags" "2" "model" "*55" } { "classname" "monster_demon1" "origin" "32 840 359" "angle" "270" "targetname" "t143" "spawnflags" "768" } { "angle" "270" "origin" "-192 840 359" "classname" "monster_demon1" "targetname" "t143" "spawnflags" "768" } { "classname" "info_teleport_destination" "origin" "80 216 303" "angle" "270" "targetname" "t141" } { "angle" "270" "origin" "-264 224 303" "classname" "info_teleport_destination" "targetname" "t142" } { "wait" "-1" "target" "t53" "health" "1" "classname" "func_button" "model" "*56" } { "spawnflags" "768" "angle" "270" "origin" "1408 1296 200" "classname" "monster_army" } { "classname" "item_shells" "origin" "772 -856 420" "spawnflags" "768" } { "spawnflags" "1792" "origin" "1248 -1128 420" "classname" "weapon_grenadelauncher" } { "spawnflags" "1793" "origin" "864 -312 440" "classname" "item_rockets" } { "classname" "trigger_once" "message" "Pass through the arch to exit..." "model" "*57" } { "mangle" "20 300 0" "classname" "info_intermission" "origin" "-224 424 512" } { "mangle" "20 45 0" "origin" "1048 -744 488" "classname" "info_intermission" } { "mangle" "20 270 0" "origin" "1104 424 528" "classname" "info_intermission" } { "mangle" "20 45 0" "origin" "1240 984 416" "classname" "info_intermission" } { "sounds" "1" "speed" "20" "classname" "func_button" "angle" "0" "wait" "-1" "target" "t144" "model" "*58" } { "classname" "light" "origin" "400 -1392 480" "light" "150" } { "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "20" "sounds" "1" "targetname" "t144" "model" "*59" } { "classname" "trigger_secret" "model" "*60" } { "classname" "item_artifact_super_damage" "origin" "400 -1360 432" } { "classname" "item_spikes" "origin" "808 -632 192" "spawnflags" "2049" } { "classname" "item_health" "origin" "924 -632 192" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "880 -616 192" "spawnflags" "1792" } { "classname" "ambient_drip" "origin" "842 978 344" } { "classname" "ambient_drip" "origin" "546 330 400" } { "classname" "info_player_coop" "origin" "1608 1664 264" "angle" "270" } { "angle" "270" "origin" "1392 1664 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1496 1560 264" "angle" "270" } { "spawnflags" "256" "angle" "270" "origin" "232 -176 320" "classname" "monster_ogre" } { "spawnflags" "1280" "angle" "225" "origin" "-368 -312 480" "classname" "monster_knight" } { "spawnflags" "1792" "classname" "func_wall" "model" "*61" } { "origin" "1810 274 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1802 -102 200" } { "origin" "2050 -214 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "2002 -390 200" } { "origin" "1738 -398 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1346 -398 200" } { "origin" "1138 -542 200" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "882 -494 200" } { "classname" "ambient_swamp1" "origin" "1722 1090 176" } { "origin" "1242 1090 176" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "1106 642 192" } { "origin" "1346 242 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "866 210 192" } { "classname" "ambient_swamp1" "origin" "1802 90 192" } { "origin" "1546 -398 192" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "2042 -310 192" } { "origin" "1178 -398 192" "classname" "ambient_swamp2" } { "classname" "ambient_swamp2" "origin" "1202 -678 192" } �{ "message" "the Grisly Grotto" "worldtype" "0" "classname" "worldspawn" "wad" "gfx/wizard.wad" "sounds" "5" } { "classname" "light" "origin" "464 480 1656" "light" "300" } { "light" "400" "origin" "712 296 1512" "classname" "light" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*1" } { "angle" "0" "classname" "func_door" "model" "*2" } { "classname" "light_flame_small_yellow" "origin" "560 -112 1374" } { "origin" "848 -112 1374" "classname" "light_flame_small_yellow" } { "origin" "760 656 1536" "classname" "light" } { "light" "400" "origin" "834 498 1040" "classname" "light" } { "light" "200" "classname" "light" "origin" "944 728 1456" } { "classname" "light_flame_small_yellow" "origin" "1016 80 998" } { "origin" "392 80 998" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "680 1224 516" "light" "200" } { "light" "200" "origin" "704 992 516" "classname" "light" } { "light" "200" "origin" "696 1608 628" "classname" "light" } { "origin" "704 1368 588" "classname" "light" } { "origin" "816 1616 444" "classname" "light" } { "classname" "light" "origin" "712 1728 444" } { "origin" "592 1608 444" "classname" "light" } { "classname" "light" "origin" "624 1096 444" } { "light" "300" "origin" "432 1328 816" "classname" "light" } { "classname" "light" "origin" "696 1112 816" "light" "300" } { "classname" "light" "origin" "704 -80 960" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "818 18 948" "light" "200" } { "origin" "586 18 948" "classname" "light_torch_small_walltorch" "light" "200" } { "light" "200" "origin" "688 496 880" "classname" "light" } { "origin" "489 483 1356" "classname" "light_flame_large_yellow" } { "light" "200" "origin" "704 -120 1360" "classname" "light" } { "classname" "light" "origin" "1216 936 1560" "light" "200" } { "origin" "1294 826 1576" "classname" "light_flame_large_yellow" } { "sounds" "1" "targetname" "t1" "wait" "-1" "angle" "180" "classname" "func_door" "model" "*3" } { "angle" "0" "wait" "-1" "classname" "func_door" "model" "*4" } { "target" "t1" "classname" "trigger_once" "model" "*5" } { "map" "e1m5" "classname" "trigger_changelevel" "model" "*6" } { "wait" "-1" "angle" "0" "classname" "func_door" "speed" "50" "model" "*7" } { "classname" "info_player_start" "origin" "-256 2272 1240" "angle" "270" } { "targetname" "t23" "classname" "func_door" "angle" "180" "wait" "-1" "speed" "50" "sounds" "3" "model" "*8" } { "classname" "light" "origin" "696 704 820" } { "classname" "light" "origin" "704 776 672" "light" "200" } { "classname" "light" "origin" "360 904 520" "light" "200" } { "origin" "704 856 400" "classname" "light" "light" "200" } { "classname" "light" "origin" "944 880 416" "light" "150" } { "origin" "1056 1176 424" "classname" "light" "light" "200" } { "classname" "light" "origin" "1096 1408 360" } { "classname" "light" "origin" "416 1696 360" } { "origin" "328 1368 360" "classname" "light" "light" "200" } { "light" "200 " "classname" "light" "origin" "696 752 896" } { "light" "250" "origin" "798 1850 1024" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "642 1850 1024" "light" "250" } { "light" "200 " "origin" "700 1364 952" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "1094 1494 1064" } { "origin" "324 1104 1064" "classname" "light_flame_large_yellow" } { "light" "200 " "origin" "704 1660 952" "classname" "light" } { "sounds" "3" "classname" "func_door" "angle" "180" "wait" "-1" "targetname" "t2" "model" "*9" } { "sounds" "3" "classname" "func_door" "wait" "-1" "angle" "0" "targetname" "t4" "model" "*10" } { "classname" "trigger_once" "targetname" "t4" "target" "t7" "model" "*11" } { "classname" "trigger_once" "targetname" "t2" "target" "t7" "model" "*12" } { "dmg" "90" "speed" "200" "classname" "func_train" "target" "t5" "targetname" "t8" "model" "*13" } { "classname" "path_corner" "origin" "-359 1528 1316" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-359 1528 880" "targetname" "t6" "target" "t5" "wait" "-1" } { "classname" "trigger_counter" "targetname" "t7" "target" "t8" "count" "2" "model" "*14" } { "targetname" "t11" "origin" "-96 1640 1256" "classname" "info_null" } { "targetname" "t9" "origin" "-416 1640 1256" "classname" "info_null" } { "light" "400" "target" "t9" "origin" "-396 1640 1256" "classname" "light" } { "light" "400" "target" "t11" "origin" "-116 1640 1256" "classname" "light" } { "classname" "light" "origin" "-328 1564 1532" } { "classname" "light_flame_small_yellow" "origin" "-88 1640 1514" } { "origin" "-424 1640 1514" "classname" "light_flame_small_yellow" } { "origin" "-248 1464 1154" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "-256 1808 1046" } { "light" "200" "origin" "-164 1732 1268" "classname" "light" } { "classname" "light" "origin" "-348 1732 1268" "light" "200" } { "light" "150" "origin" "-248 1500 1056" "classname" "light" } { "light" "150" "origin" "-256 1772 956" "classname" "light" } { "classname" "light" "origin" "-128 1636 920" "light" "150" } { "light" "150" "origin" "-124 1640 920" "classname" "light" } { "classname" "light" "origin" "-172 1524 920" "light" "150" } { "light" "150" "origin" "-284 1516 920" "classname" "light" } { "classname" "light" "origin" "-360 1580 920" "light" "150" } { "light" "75" "origin" "-360 1700 920" "classname" "light" } { "classname" "light" "origin" "72 1632 928" "light" "125" } { "light" "150" "origin" "80 1488 928" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "158 1308 1064" } { "classname" "light" "origin" "-192 1688 1380" "light" "100" } { "light" "100" "origin" "-192 1592 1380" "classname" "light" } { "classname" "light" "origin" "-320 1592 1380" "light" "100" } { "light" "100" "origin" "-320 1688 1380" "classname" "light" } { "classname" "light" "origin" "-384 1640 1452" "light" "125" } { "classname" "light" "origin" "-112 1640 1452" "light" "125" } { "light" "200" "origin" "-248 1504 1336" "classname" "light" } { "classname" "light" "origin" "-128 1640 1336" "light" "200" } { "light" "200" "origin" "-384 1640 1336" "classname" "light" } { "classname" "light" "origin" "696 1608 816" "light" "300" } { "light" "200" "classname" "light" "origin" "888 1328 424" } { "classname" "light" "origin" "160 1352 960" "light" "150" } { "light" "150" "origin" "368 1104 1000" "classname" "light" } { "classname" "light" "origin" "1048 1504 1000" "light" "150" } { "classname" "light" "origin" "992 992 1048" "light" "150" } { "classname" "light" "origin" "48 1384 1008" "light" "100" } { "light" "150" "origin" "1080 1144 1048" "classname" "light" } { "classname" "light" "origin" "968 824 976" "light" "150" } { "classname" "light" "origin" "896 1328 1000" "light" "150" } { "classname" "light" "origin" "368 1616 1000" "light" "175" } { "classname" "light" "origin" "256 1496 824" "light" "150" } { "light" "150" "origin" "256 1352 824" "classname" "light" } { "classname" "light" "origin" "856 1368 536" "light" "200" } { "light" "150" "origin" "552 1360 536" "classname" "light" } { "classname" "light" "origin" "872 1552 1056" "light" "150" } { "classname" "light" "origin" "1032 1056 904" "light" "150" } { "light" "150" "origin" "1080 1184 904" "classname" "light" } { "classname" "light" "origin" "864 848 904" "light" "150" } { "classname" "light" "origin" "312 1496 1016" "light" "175" } { "light" "200" "origin" "704 1368 808" "classname" "light" } { "light" "150" "origin" "464 816 904" "classname" "light" } { "light" "200" "origin" "944 888 800" "classname" "light" } { "light" "150" "origin" "888 1112 728" "classname" "light" } { "light" "175" "origin" "1008 1504 728" "classname" "light" } { "light" "200" "origin" "720 1848 1200" "classname" "light" } { "light" "200" "origin" "704 888 672" "classname" "light" } { "light" "175" "origin" "512 1456 1040" "classname" "light" } { "light" "150" "origin" "440 840 380" "classname" "light" } { "light" "150" "origin" "720 1848 1028" "classname" "light" } { "light" "150" "origin" "720 1936 1024" "classname" "light" } { "origin" "544 2128 984" "classname" "light" "light" "200" } { "light" "200" "origin" "712 1912 576" "classname" "light" } { "light" "300" "origin" "720 2544 856" "classname" "light" } { "light" "200" "origin" "888 2048 592" "classname" "light" } { "origin" "704 2496 1128" "classname" "light" } { "origin" "512 2048 976" "classname" "light" "light" "150" } { "origin" "952 2328 528" "classname" "light" "light" "200" } { "classname" "light" "origin" "700 2760 808" "light" "200" } { "classname" "light" "origin" "700 2800 616" "light" "200" } { "classname" "light" "origin" "584 2780 584" "light" "175" } { "light" "175" "origin" "808 2780 584" "classname" "light" } { "sounds" "3" "wait" "-1" "targetname" "t29" "classname" "func_door" "angle" "270" "model" "*15" } { "message" "This door is opened elsewhere..." "wait" "-1" "classname" "func_door" "angle" "90" "model" "*16" } { "classname" "light" "origin" "424 2304 1000" "light" "200" } { "light" "300" "origin" "696 2880 1062" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "864 2128 984" } { "classname" "light_flame_large_yellow" "origin" "702 2154 1228" "light" "0" } { "classname" "light" "origin" "832 2008 1136" "light" "200" } { "classname" "light" "origin" "584 2008 1136" "light" "200" } { "light" "200" "origin" "272 2016 1136" "classname" "light" } { "classname" "light" "origin" "272 2320 1136" "light" "150" } { "light" "150" "origin" "1104 2408 984" "classname" "light" } { "sounds" "2" "classname" "func_plat" "wait" "4" "model" "*17" } { "classname" "light" "origin" "700 2844 996" "light" "200" } { "classname" "light" "origin" "704 2216 1252" } { "origin" "936 2304 1252" "classname" "light" } { "classname" "light" "origin" "936 2536 1252" } { "light" "300" "classname" "light" "origin" "488 2536 1252" } { "origin" "488 2304 1252" "classname" "light" "light" "300" } { "light" "0" "origin" "998 2306 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "998 2534 1228" "light" "0" } { "light" "0" "origin" "426 2534 1228" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "426 2306 1228" "light" "0" } { "classname" "light" "origin" "704 2152 984" "light" "200" } { "classname" "light_flame_small_yellow" "origin" "1160 2400 1038" "light" "250" } { "origin" "840 2536 630" "classname" "light_flame_small_yellow" "light" "250" } { "classname" "light_flame_small_yellow" "origin" "584 2544 630" "light" "250" } { "light" "250" "origin" "408 2304 694" "classname" "light_flame_small_yellow" } { "light" "200" "classname" "light" "origin" "960 2632 528" } { "classname" "light" "origin" "960 2480 528" "light" "150" } { "classname" "light" "origin" "456 2304 624" "light" "175" } { "classname" "light" "origin" "704 2416 576" "light" "250" } { "light" "150" "origin" "728 2184 528" "classname" "light" } { "classname" "light" "origin" "512 2176 528" "light" "150" } { "classname" "light" "origin" "832 2336 656" "light" "175" } { "light" "175" "origin" "592 2336 656" "classname" "light" } { "classname" "light" "origin" "808 2584 576" "light" "150" } { "light" "150" "origin" "616 2576 576" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "488 2712 694" "light" "250" } { "classname" "light" "origin" "488 2624 608" "light" "175" } { "classname" "light" "origin" "480 2464 608" "light" "175" } { "light" "250" "origin" "184 2184 1038" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "512 2096 1038" "light" "250" } { "classname" "light" "origin" "224 2184 976" "light" "150" } { "classname" "light" "origin" "848 2024 992" "light" "200" } { "classname" "light" "origin" "256 1992 1000" "light" "150" } { "classname" "light" "origin" "272 2376 1000" "light" "200" } { "classname" "light" "origin" "1112 2208 1032" "light" "150" } { "light" "0" "origin" "698 2860 1228" "classname" "light_flame_large_yellow" } { "origin" "700 2808 1252" "classname" "light" } { "light" "250" "origin" "472 2632 1062" "classname" "light" } { "classname" "light" "origin" "952 2632 1062" "light" "250" } { "light" "150" "origin" "952 2424 894" "classname" "light" } { "classname" "light" "origin" "480 2424 894" "light" "150" } { "light" "150" "origin" "896 2296 966" "classname" "light" } { "classname" "light" "origin" "704 2304 966" "light" "150" } { "light" "150" "origin" "552 2304 966" "classname" "light" } { "light" "250" "origin" "704 2184 406" "classname" "light" } { "light" "150" "origin" "712 2000 406" "classname" "light" } { "light" "200" "origin" "504 2224 400" "classname" "light" } { "light" "200" "origin" "960 2576 400" "classname" "light" } { "light" "150" "origin" "960 2400 400" "classname" "light" } { "light" "150" "origin" "808 1112 592" "classname" "light" } { "classname" "light" "origin" "600 1112 592" "light" "150" } { "light" "175" "origin" "1016 128 936" "classname" "light" } { "classname" "light" "origin" "392 128 936" "light" "175" } { "light" "150" "origin" "848 -64 1304" "classname" "light" } { "classname" "light" "origin" "560 -64 1304" "light" "150" } { "light" "175" "origin" "1248 832 1496" "classname" "light" } { "light" "150" "origin" "1096 872 1496" "classname" "light" } { "light" "150" "origin" "896 2208 976" "classname" "light" } { "classname" "light" "origin" "1096 200 1512" "light" "350" } { "light" "350" "origin" "264 192 1512" "classname" "light" } { "light" "175" "origin" "488 448 1264" "classname" "light" } { "origin" "1048 728 1456" "classname" "light" "light" "200" } { "light" "125" "origin" "1328 928 1448" "classname" "light" } { "light" "225" "origin" "696 672 1320" "classname" "light" } { "origin" "816 -56 1640" "classname" "light" } { "classname" "light" "origin" "584 -56 1640" } { "light" "175" "origin" "880 96 1376" "classname" "light" } { "classname" "light" "origin" "528 96 1376" "light" "175" } { "light" "200" "origin" "960 344 1072" "classname" "light" } { "classname" "light" "origin" "1120 264 1072" "light" "200" } { "light" "175" "origin" "1208 96 1016" "classname" "light" } { "light" "175" "origin" "1024 360 968" "classname" "light" } { "classname" "light" "origin" "328 336 968" "light" "150" } { "classname" "light" "origin" "416 424 1072" "light" "200" } { "light" "200" "origin" "296 256 1072" "classname" "light" } { "light" "150" "origin" "704 48 1040" "classname" "light" } { "light" "175" "origin" "384 464 976" "classname" "light" } { "classname" "light" "origin" "224 264 976" "light" "150" } { "light" "150" "origin" "952 2192 416" "classname" "light" } { "classname" "light" "origin" "1040 -464 1016" "light" "150" } { "sounds" "2" "spawnflags" "1" "classname" "func_plat" "model" "*18" } { "light" "250" "origin" "1072 -272 1262" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1024 -272 1200" "light" "150" } { "light" "200" "origin" "624 -240 1328" "classname" "light" } { "light" "175" "origin" "1080 -624 1168" "classname" "light" } { "origin" "456 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "504 -576 1168" "light" "175" } { "origin" "624 -448 924" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "624 -704 924" } { "light" "125" "origin" "664 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -488 880" "light" "125" } { "light" "125" "origin" "584 -448 880" "classname" "light" } { "classname" "light" "origin" "624 -408 880" "light" "125" } { "light" "125" "origin" "624 -664 880" "classname" "light" } { "classname" "light" "origin" "664 -704 880" "light" "125" } { "light" "125" "origin" "624 -744 880" "classname" "light" } { "classname" "light" "origin" "584 -704 880" "light" "125" } { "light" "250" "origin" "296 -96 968" "classname" "light" } { "light" "250" "origin" "1112 -96 968" "classname" "light" } { "light" "175" "origin" "1056 -224 968" "classname" "light" } { "light" "500" "origin" "-264 2120 1504" "classname" "light" } { "spawnflags" "2064" "angle" "0" "classname" "func_door" "wait" "-1" "model" "*19" } { "sounds" "3" "classname" "func_door" "angle" "180" "spawnflags" "2064" "wait" "-1" "model" "*20" } { "light" "200" "origin" "704 32 952" "classname" "light" } { "target" "t101" "spawnflags" "256" "targetname" "t23" "angle" "90" "origin" "-248 1560 1224" "classname" "monster_wizard" } { "target" "t23" "classname" "trigger_once" "model" "*21" } { "origin" "-280 1560 1348" "classname" "item_armor2" } { "origin" "-488 2112 1220" "classname" "item_health" } { "classname" "item_health" "origin" "-488 2064 1220" } { "spawnflags" "1536" "origin" "-272 1784 1156" "classname" "item_shells" } { "spawnflags" "256" "target" "t25" "targetname" "t24" "origin" "888 1640 1028" "classname" "path_corner" } { "spawnflags" "256" "target" "t24" "targetname" "t25" "classname" "path_corner" "origin" "624 1264 1028" } { "spawnflags" "256" "target" "t24" "origin" "928 1672 1028" "classname" "monster_wizard" } { "spawnflags" "2304" "target" "t26" "classname" "trigger_once" "model" "*22" } { "target" "t27" "targetname" "t28" "origin" "568 2040 928" "classname" "path_corner" } { "target" "t28" "targetname" "t27" "classname" "path_corner" "origin" "368 2044 928" } { "target" "t27" "origin" "472 2040 944" "classname" "monster_ogre" "spawnflags" "1" } { "sounds" "2" "target" "t29" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*23" } { "sounds" "3" "angle" "180" "classname" "func_door" "model" "*24" } { "angle" "0" "classname" "func_door" "model" "*25" } { "light" "175" "origin" "1112 2520 964" "classname" "light" } { "origin" "104 1308 892" "classname" "item_health" } { "spawnflags" "2048" "origin" "704 1344 936" "classname" "item_key1" "sounds" "1" } { "target" "t34" "angle" "180" "origin" "920 2040 544" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t35" "targetname" "t34" "origin" "864 2044 528" "classname" "path_corner" } { "target" "t37" "targetname" "t35" "classname" "path_corner" "origin" "704 2048 528" } { "target" "t34" "targetname" "t36" "origin" "704 2048 528" "classname" "path_corner" } { "target" "t36" "targetname" "t37" "classname" "path_corner" "origin" "704 1808 528" } { "targetname" "t38" "angle" "270" "origin" "456 2476 544" "classname" "monster_ogre" } { "target" "t38" "classname" "trigger_once" "model" "*26" } { "targetname" "t29" "angle" "0" "origin" "336 2272 952" "classname" "monster_knight" "spawnflags" "768" } { "targetname" "t39" "spawnflags" "1" "wait" "-1" "angle" "-2" "classname" "func_door" "sounds" "1" "model" "*27" } { "targetname" "t39" "angle" "270" "origin" "712 2540 448" "classname" "monster_ogre" } { "target" "t39" "classname" "trigger_once" "model" "*28" } { "classname" "light" "origin" "712 2544 440" "light" "200" } { "classname" "item_health" "origin" "1064 2184 920" "spawnflags" "1024" } { "spawnflags" "1" "origin" "1128 2184 920" "classname" "item_health" } { "classname" "item_spikes" "origin" "816 2840 920" } { "origin" "816 2800 920" "classname" "item_spikes" } { "spawnflags" "256" "classname" "monster_wizard" "origin" "1656 1496 968" "angle" "180" "target" "t41" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t40" "targetname" "t39" "model" "*29" } { "classname" "info_teleport_destination" "origin" "984 1496 1000" "angle" "180" "targetname" "t40" } { "spawnflags" "256" "classname" "path_corner" "origin" "912 1496 1000" "targetname" "t41" "target" "t42" } { "spawnflags" "256" "origin" "528 1368 1000" "classname" "path_corner" "targetname" "t42" "target" "t41" } { "classname" "item_health" "origin" "408 2640 520" } { "classname" "item_shells" "origin" "456 2672 520" "spawnflags" "1" } { "angle" "0" "origin" "80 864 968" "classname" "monster_wizard" "target" "t44" } { "targetname" "t119" "spawnflags" "2" "classname" "trigger_teleport" "target" "t45" "model" "*30" } { "classname" "info_teleport_destination" "origin" "432 856 952" "angle" "45" "targetname" "t45" } { "classname" "path_corner" "origin" "496 872 952" "target" "t43" "targetname" "t44" } { "origin" "872 1056 952" "classname" "path_corner" "targetname" "t43" "target" "t44" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t39" "model" "*31" } { "spawnflags" "1024" "classname" "monster_knight" "origin" "1168 56 904" "angle" "135" "target" "t49" "targetname" "t67" } { "classname" "monster_ogre" "origin" "1800 224 920" "angle" "180" "target" "t116" "spawnflags" "256" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t49" "lip" "-24" "model" "*32" } { "wait" "-1" "angle" "-1" "classname" "func_door" "spawnflags" "1" "targetname" "t39" "lip" "-24" "model" "*33" } { "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t29" "lip" "-24" "model" "*34" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t46" "model" "*35" } { "classname" "info_teleport_destination" "origin" "1104 232 880" "angle" "180" "targetname" "t46" } { "classname" "path_corner" "origin" "1064 256 880" "target" "t47" "targetname" "t48" "spawnflags" "256" } { "origin" "312 232 880" "classname" "path_corner" "targetname" "t47" "target" "t48" "spawnflags" "256" } { "classname" "info_teleport_destination" "origin" "704 -40 1256" "angle" "90" "targetname" "t50" } { "classname" "trigger_once" "target" "t52" "model" "*36" } { "spawnflags" "2" "classname" "trigger_teleport" "target" "t50" "targetname" "t52" "model" "*37" } { "angle" "90" "origin" "570 -898 1300" "classname" "monster_wizard" "targetname" "t52" } { "classname" "item_shells" "origin" "216 120 880" "spawnflags" "1" } { "classname" "item_health" "origin" "192 232 880" } { "origin" "192 192 880" "classname" "item_health" "spawnflags" "1024" } { "classname" "item_shells" "origin" "-216 1464 888" } { "classname" "item_health" "origin" "816 1160 512" "spawnflags" "1024" } { "spawnflags" "1" "origin" "816 1120 512" "classname" "item_health" } { "classname" "monster_wizard" "origin" "944 840 956" "angle" "135" "target" "t53" "targetname" "t64" } { "classname" "trigger_once" "target" "t64" "model" "*38" } { "classname" "monster_knight" "origin" "704 392 1280" "angle" "270" "target" "t65" "spawnflags" "1" } { "classname" "path_corner" "origin" "704 208 1264" "targetname" "t65" "target" "t66" } { "origin" "704 496 1264" "classname" "path_corner" "targetname" "t66" "target" "t65" } { "classname" "trigger_once" "target" "t67" "model" "*39" } { "sounds" "1" "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t72" "model" "*40" } { "sounds" "1" "classname" "func_door" "wait" "-1" "angle" "-2" "targetname" "t72" "model" "*41" } { "classname" "trigger_once" "target" "t72" "model" "*42" } { "classname" "info_null" "origin" "852 -580 820" "targetname" "t73" } { "classname" "light" "origin" "856 -584 936" "light" "400" "target" "t73" } { "classname" "light" "origin" "920 -448 744" "light" "150" } { "light" "150" "origin" "760 -448 744" "classname" "light" } { "classname" "light" "origin" "552 -424 744" "light" "150" } { "light" "150" "origin" "560 -728 744" "classname" "light" } { "classname" "light" "origin" "776 -712 744" "light" "150" } { "light" "150" "origin" "936 -712 744" "classname" "light" } { "classname" "path_corner" "origin" "652 -576 952" "targetname" "t75" "target" "t74" } { "origin" "908 -576 952" "classname" "path_corner" "targetname" "t74" "target" "t75" } { "classname" "monster_ogre" "origin" "816 -260 952" "angle" "270" "targetname" "t72" } { "classname" "monster_ogre" "origin" "724 -260 952" "angle" "270" "targetname" "t72" "spawnflags" "256" } { "classname" "item_health" "origin" "632 -548 820" "spawnflags" "3072" } { "origin" "672 -548 820" "classname" "item_health" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t76" "model" "*43" } { "sounds" "1" "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t76" "model" "*44" } { "light" "150" "origin" "1040 -712 1016" "classname" "light" } { "origin" "1112 -576 942" "classname" "light_flame_small_yellow" } { "classname" "light" "origin" "1064 -576 896" "light" "150" } { "classname" "light" "origin" "888 -80 968" "light" "175" } { "light" "175" "origin" "512 -80 968" "classname" "light" } { "classname" "item_armor2" "origin" "1184 -96 920" } { "classname" "monster_ogre" "origin" "392 8 912" "angle" "315" "targetname" "t77" "spawnflags" "256" } { "classname" "trigger_once" "target" "t77" "model" "*45" } { "classname" "item_health" "origin" "336 -224 888" "spawnflags" "1" } { "classname" "item_spikes" "origin" "968 16 888" "spawnflags" "1" } { "classname" "item_health" "origin" "560 2808 516" "spawnflags" "1" } { "classname" "path_corner" "origin" "1056 -384 824" "targetname" "t78" "target" "t79" "spawnflags" "256" } { "origin" "1056 -736 824" "classname" "path_corner" "targetname" "t79" "target" "t78" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "1064 -656 840" "angle" "90" "target" "t78" "spawnflags" "257" } { "angle" "90" "origin" "848 -880 952" "classname" "monster_ogre" "targetname" "t72" } { "classname" "item_shells" "origin" "1160 16 1248" } { "classname" "item_health" "origin" "280 64 1248" } { "classname" "item_rockets" "origin" "648 -256 928" "spawnflags" "1025" } { "classname" "item_spikes" "origin" "648 -304 1248" } { "origin" "696 -304 1248" "classname" "item_spikes" } { "classname" "light_flame_small_yellow" "origin" "768 -352 1230" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*46" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*47" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*48" } { "target" "t83" "classname" "trigger_multiple" "wait" ".8" "model" "*49" } { "target" "t83" "wait" ".8" "classname" "trigger_multiple" "model" "*50" } { "targetname" "t83" "classname" "trap_spikeshooter" "origin" "1108 -576 1140" "spawnflags" "1" "angle" "180" } { "targetname" "t83" "angle" "90" "spawnflags" "1" "origin" "768 -796 1140" "classname" "trap_spikeshooter" } { "origin" "1112 -576 1230" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "768 -800 1230" } { "classname" "light" "origin" "712 -756 1168" "light" "175" } { "light" "175" "origin" "768 -424 1168" "classname" "light" } { "light" "175" "origin" "888 -744 956" "classname" "light" } { "classname" "light" "origin" "888 -408 956" "light" "175" } { "origin" "384 -224 888" "classname" "item_shells" } { "origin" "584 1784 920" "classname" "item_health" } { "origin" "832 2064 920" "classname" "item_shells" } { "target" "t72" "classname" "trigger_once" "model" "*51" } { "target" "t85" "targetname" "t84" "origin" "1560 216 896" "classname" "path_corner" } { "target" "t48" "targetname" "t85" "classname" "path_corner" "origin" "1456 216 896" } { "origin" "704 1368 516" "classname" "weapon_supernailgun" } { "spawnflags" "1" "origin" "184 1928 920" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "656 1816 528" "spawnflags" "768" } { "classname" "item_shells" "origin" "1072 -800 820" "spawnflags" "1024" } { "classname" "monster_ogre" "origin" "840 -40 1276" "angle" "180" "targetname" "t86" "spawnflags" "768" } { "classname" "trigger_once" "target" "t86" "model" "*52" } { "classname" "light" "origin" "472 -576 876" "light" "150" } { "classname" "item_shells" "origin" "656 680 1256" "spawnflags" "1536" } { "angle" "270" "origin" "880 2224 536" "classname" "monster_knight" "spawnflags" "256" } { "angle" "180" "origin" "1112 2424 944" "classname" "monster_ogre" "spawnflags" "1281" } { "targetname" "t88" "target" "t87" "origin" "360 384 880" "classname" "path_corner" "spawnflags" "1280" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "504 160 880" "spawnflags" "1280" } { "target" "t87" "angle" "315" "origin" "384 320 896" "classname" "monster_knight" "spawnflags" "1280" } { "spawnflags" "257" "targetname" "t86" "angle" "0" "origin" "376 120 1272" "classname" "monster_ogre" } { "origin" "776 1368 916" "classname" "item_shells" "spawnflags" "1024" } { "classname" "item_spikes" "origin" "184 1968 920" "spawnflags" "1" } { "classname" "monster_wizard" "origin" "312 936 944" "angle" "45" "spawnflags" "769" } { "classname" "monster_knight" "origin" "704 -80 908" "angle" "90" } { "angle" "0" "origin" "568 -56 1276" "classname" "monster_ogre" "targetname" "t86" "spawnflags" "768" } { "spawnflags" "1536" "targetname" "t90" "target" "t89" "origin" "720 1696 924" "classname" "path_corner" } { "spawnflags" "1536" "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "720 1416 924" } { "spawnflags" "1537" "target" "t90" "origin" "704 1784 948" "classname" "monster_ogre" } { "classname" "func_door_secret" "angle" "0" "spawnflags" "1" "targetname" "t91" "model" "*53" "t_length" "73" // svdijk -- added to prevent z-fighting } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*54" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*55" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*56" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t92" "model" "*57" } { "classname" "func_button" "wait" "-1" "angle" "-2" "target" "t92" "model" "*58" } { "classname" "trigger_counter" "count" "5" "target" "t91" "targetname" "t92" "model" "*59" } { "classname" "light" "origin" "680 -920 1144" "light" "150" } { "classname" "item_spikes" "origin" "752 -876 928" "spawnflags" "1" } { "spawnflags" "3" "angle" "0" "classname" "func_door_secret" "targetname" "t91" "model" "*60" "t_length" "73" // svdijk -- added to prevent z-fighting } { "light" "150" "origin" "680 -256 1144" "classname" "light" } { "classname" "trigger_once" "spawnflags" "1792" "target" "t72" "model" "*61" } { "classname" "trigger_secret" "sounds" "1" "targetname" "t8" "model" "*62" } { "angle" "270" "origin" "704 624 1280" "classname" "monster_knight" "spawnflags" "1" } { "angle" "225" "origin" "1016 -440 1128" "classname" "monster_knight" } { "angle" "180" "origin" "1024 -728 1128" "classname" "monster_knight" } { "spawnflags" "256" "angle" "180" "origin" "1008 -568 1128" "classname" "monster_knight" } { "spawnflags" "256" "classname" "monster_knight" "origin" "304 2312 952" "angle" "0" "targetname" "t29" } { "spawnflags" "1025" "classname" "monster_knight" "origin" "272 136 896" "angle" "45" } { "classname" "monster_wizard" "origin" "784 -576 960" "angle" "0" "target" "t74" "spawnflags" "1" } { "classname" "item_health" "origin" "732 -936 928" } { "origin" "772 -936 928" "classname" "item_health" } { "spawnflags" "256" "classname" "monster_knight" "origin" "704 -248 1272" "angle" "0" } { "classname" "path_corner" "origin" "1328 928 1396" "targetname" "t93" "target" "t94" "spawnflags" "512" } { "origin" "1176 928 1396" "classname" "path_corner" "target" "t93" "targetname" "t94" "spawnflags" "512" } { "classname" "monster_knight" "origin" "1192 884 1412" "angle" "0" "target" "t93" "spawnflags" "513" } { "classname" "monster_knight" "origin" "704 2376 944" "angle" "90" } { "classname" "monster_knight" "origin" "888 2312 944" "angle" "180" "targetname" "t95" } { "angle" "0" "origin" "512 2304 944" "classname" "monster_knight" "targetname" "t95" "spawnflags" "768" } { "classname" "trigger_once" "target" "t95" "model" "*63" } { "spawnflags" "256" "angle" "315" "origin" "728 2312 536" "classname" "monster_knight" } { "light" "200" "origin" "1296 1528 936" "classname" "light" } { "light" "250" "origin" "1432 1360 982" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1400 1360 920" "classname" "light" } { "light" "200" "origin" "1248 1344 680" "classname" "light" } { "lip" "-384" "wait" "-1" "angle" "90" "classname" "func_door" "targetname" "t98" "model" "*64" } { "light" "150" "origin" "1152 1328 584" "classname" "light" } { "origin" "1376 1480 864" "classname" "item_health" } { "sounds" "1" "classname" "trigger_secret" "model" "*65" } { "classname" "func_button" "sounds" "1" "angle" "0" "wait" "-1" "target" "t97" "model" "*66" } { "classname" "func_button" "wait" "-1" "angle" "0" "sounds" "1" "target" "t97" "model" "*67" } { "classname" "trigger_counter" "targetname" "t97" "target" "t98" "model" "*68" } { "classname" "light" "origin" "880 -888 968" "light" "150" } { "light" "150" "origin" "880 -264 968" "classname" "light" } { "angle" "180" "origin" "1112 2344 944" "classname" "monster_ogre" "spawnflags" "769" } { "angle" "270" "origin" "1120 880 1412" "classname" "monster_ogre" "spawnflags" "257" } { "map" "e1m8" "classname" "trigger_changelevel" "model" "*69" } { "light" "175" "origin" "824 -756 1168" "classname" "light" } { "classname" "light" "origin" "1080 -528 1168" "light" "175" } { "classname" "monster_wizard" "origin" "672 -392 1024" "angle" "315" "spawnflags" "257" } { "classname" "monster_knight" "origin" "1008 -656 1128" "angle" "180" "spawnflags" "768" } { "origin" "520 1064 440" "classname" "air_bubbles" } { "classname" "item_spikes" "origin" "16 1432 892" } { "classname" "trigger_once" "message" "A secret cave has opened..." "targetname" "t98" "model" "*70" } { "target" "t4" "health" "1" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*71" } { "target" "t2" "health" "1" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*72" } { "target" "t49" "spawnflags" "769" "angle" "135" "origin" "1208 128 904" "classname" "monster_demon1" } { "spawnflags" "769" "angle" "45" "origin" "288 160 896" "classname" "monster_demon1" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "692 -884 952" "angle" "90" } { "classname" "monster_ogre" "origin" "-312 1648 1372" "angle" "90" "targetname" "t23" "spawnflags" "768" } { "angle" "90" "origin" "-192 1648 1372" "classname" "monster_ogre" "targetname" "t23" "spawnflags" "768" } { "classname" "monster_ogre" "origin" "704 1288 540" "angle" "270" "spawnflags" "768" } { "targetname" "t101" "target" "t106" "spawnflags" "770" "classname" "trigger_teleport" "model" "*73" } { "spawnflags" "768" "targetname" "t101" "angle" "270" "origin" "-256 2424 1288" "classname" "monster_wizard" } { "targetname" "t101" "origin" "-248 2440 1280" "classname" "trigger_relay" } { "targetname" "t29" "spawnflags" "256" "angle" "0" "origin" "144 2648 1024" "classname" "monster_wizard" } { "targetname" "t29" "spawnflags" "768" "classname" "monster_wizard" "origin" "144 2592 1024" "angle" "0" } { "targetname" "t29" "spawnflags" "768" "angle" "0" "origin" "144 2536 1024" "classname" "monster_wizard" } { "targetname" "t29" "target" "t102" "spawnflags" "258" "classname" "trigger_teleport" "model" "*74" } { "targetname" "t29" "target" "t103" "spawnflags" "770" "classname" "trigger_teleport" "model" "*75" } { "targetname" "t29" "target" "t104" "spawnflags" "770" "classname" "trigger_teleport" "model" "*76" } { "angle" "270" "targetname" "t102" "spawnflags" "256" "origin" "704 2656 1008" "classname" "info_teleport_destination" } { "angle" "270" "targetname" "t103" "spawnflags" "768" "classname" "info_teleport_destination" "origin" "920 2520 1008" } { "angle" "0" "targetname" "t104" "spawnflags" "768" "origin" "592 2192 1008" "classname" "info_teleport_destination" } { "sounds" "1" "classname" "func_door" "angle" "0" "wait" "-1" "speed" "150" "targetname" "t105" "model" "*77" } { "classname" "monster_demon1" "origin" "1056 -880 1128" "angle" "90" "spawnflags" "768" "targetname" "t105" } { "classname" "trigger_once" "spawnflags" "768" "target" "t105" "model" "*78" } { "classname" "light" "origin" "1056 -920 1184" "light" "125" } { "classname" "trigger_relay" "origin" "1104 -864 1120" "target" "t105" } { "classname" "monster_ogre" "origin" "1120 768 1416" "angle" "180" "spawnflags" "769" } { "classname" "air_bubbles" "origin" "884 1616 440" } { "targetname" "t106" "spawnflags" "768" "angle" "270" "origin" "-264 2232 1296" "classname" "info_teleport_destination" } { "classname" "path_corner" "origin" "568 1984 928" "targetname" "t107" "target" "t108" "spawnflags" "256" } { "origin" "424 1960 928" "classname" "path_corner" "target" "t107" "spawnflags" "256" "targetname" "t111" } { "classname" "path_corner" "origin" "704 2024 928" "targetname" "t108" "target" "t109" "spawnflags" "256" } { "origin" "712 1712 928" "classname" "path_corner" "targetname" "t109" "target" "t110" "spawnflags" "256" } { "classname" "path_corner" "origin" "712 1416 928" "targetname" "t110" "target" "t109" "spawnflags" "256" } { "classname" "func_door" "angle" "-2" "wait" "-1" "lip" "-24" "targetname" "t26" "spawnflags" "2304" "model" "*79" } { "classname" "monster_ogre" "origin" "240 2048 944" "angle" "0" "spawnflags" "257" "target" "t111" } { "target" "t23" "spawnflags" "1792" "classname" "trigger_once" "model" "*80" } { "classname" "monster_knight" "origin" "576 2768 536" "angle" "0" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "824 2776 536" "classname" "monster_knight" } { "classname" "monster_wizard" "origin" "704 -1032 1024" "angle" "90" "spawnflags" "256" "targetname" "t52" } { "angle" "90" "origin" "760 -1032 1024" "classname" "monster_wizard" "spawnflags" "768" "targetname" "t114" } { "classname" "monster_wizard" "origin" "816 -1032 1024" "angle" "90" "spawnflags" "768" "targetname" "t114" } { "targetname" "t52" "classname" "trigger_teleport" "spawnflags" "258" "target" "t112" "model" "*81" } { "classname" "trigger_teleport" "spawnflags" "770" "target" "t113" "targetname" "t114" "model" "*82" } { "targetname" "t114" "classname" "trigger_teleport" "spawnflags" "770" "target" "t115" "model" "*83" } { "classname" "info_teleport_destination" "origin" "896 224 1352" "angle" "135" "spawnflags" "256" "targetname" "t112" } { "classname" "info_teleport_destination" "origin" "488 1648 1016" "angle" "315" "spawnflags" "768" "targetname" "t113" } { "classname" "trigger_once" "spawnflags" "768" "target" "t114" "model" "*84" } { "classname" "info_teleport_destination" "origin" "800 904 928" "angle" "90" "spawnflags" "768" "targetname" "t115" } { "angle" "270" "origin" "-256 2232 1242" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "-256 2096 1218" "classname" "weapon_supershotgun" } { "angle" "270" "origin" "704 424 1266" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 2488 946" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1968 546" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "704 104 898" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "704 1568 938" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "704 1344 912" "classname" "weapon_rocketlauncher" } { "angle" "0" "origin" "712 -576 840" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "944 -576 816" "classname" "weapon_nailgun" } { "angle" "180" "origin" "1064 -576 1128" "classname" "info_player_deathmatch" } { "spawnflags" "1792" "origin" "696 584 1256" "classname" "weapon_grenadelauncher" } { "classname" "light" "origin" "316 804 780" "light" "150" } { "classname" "light" "origin" "316 804 644" "light" "75" } { "classname" "item_rockets" "origin" "298 710 706" "spawnflags" "1" } { "classname" "item_shells" "origin" "988 -928 1104" "spawnflags" "1" } { "classname" "item_health" "origin" "-56 2112 1220" "spawnflags" "3584" } { "classname" "item_health" "origin" "-56 2072 1220" "spawnflags" "2305" } { "spawnflags" "1" "origin" "584 2416 512" "classname" "item_spikes" } { "origin" "688 1392 516" "classname" "item_spikes" } { "classname" "item_artifact_invulnerability" "origin" "712 2312 948" "spawnflags" "1792" } { "origin" "32 1392 916" "classname" "item_artifact_envirosuit" } { "mangle" "26 310 0" "origin" "384 488 1552" "classname" "info_intermission" } { "light" "100" "origin" "800 1160 464" "classname" "light" } { "classname" "light" "origin" "608 1160 464" "light" "100" } { "light" "100" "origin" "604 1472 464" "classname" "light" } { "target" "t40" "classname" "trigger_teleport" "model" "*85" } { "mangle" "-20 75 0" "origin" "456 2144 568" "classname" "info_intermission" } { "mangle" "10 80 0" "origin" "464 1032 1000" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "1080 -752 1008" "classname" "info_intermission" } { "classname" "trigger_secret" "model" "*86" } { "classname" "path_corner" "origin" "1632 216 896" "targetname" "t116" "target" "t84" } { "classname" "item_spikes" "origin" "1200 48 872" "spawnflags" "1" } { "classname" "item_spikes" "origin" "928 2664 320" "spawnflags" "1" } { "classname" "item_shells" "origin" "1240 768 1384" "spawnflags" "1" } { "classname" "item_shells" "origin" "-88 2160 1224" "spawnflags" "2049" } { "classname" "info_player_coop" "origin" "-208 2272 1240" "angle" "270" } { "angle" "270" "origin" "-304 2272 1240" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-352 2272 1240" "angle" "270" } { "light" "200" "origin" "1288 1648 956" "classname" "light" } { "classname" "item_spikes" "origin" "-264 1464 888" } { "spawnflags" "1792" "origin" "1296 1488 888" "classname" "item_artifact_invisibility" } { "spawnflags" "1792" "classname" "func_wall" "model" "*87" } { "classname" "func_wall" "spawnflags" "1792" "model" "*88" } { "target" "t118" "targetname" "t117" "origin" "-176 1640 888" "classname" "path_corner" } { "targetname" "t118" "target" "t117" "classname" "path_corner" "origin" "-320 1640 888" } { "target" "t117" "origin" "-256 1632 904" "classname" "monster_knight" "spawnflags" "1" } { "origin" "1376 1424 864" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*89" } { "target" "t120" "targetname" "t53" "classname" "trigger_once" "model" "*90" } { "target" "t120" "targetname" "t39" "classname" "trigger_once" "model" "*91" } { "classname" "light" "origin" "480 2568 568" "light" "125" } { "origin" "162 1482 976" "classname" "ambient_drip" } { "origin" "786 1010 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "778 1210 584" } { "origin" "594 1202 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "602 1010 584" } { "origin" "786 1514 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "794 1698 584" } { "origin" "618 1690 584" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "618 1522 584" } { "origin" "698 1362 584" "classname" "ambient_drip" } { "origin" "714 1970 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "898 2170 592" } { "origin" "938 2346 592" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "682 2298 592" } { "origin" "458 2306 592" "classname" "ambient_drip" } { "origin" "458 1690 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "322 1506 880" } { "origin" "338 1226 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "466 1090 880" } { "origin" "394 882 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "674 810 880" } { "origin" "914 818 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "922 1034 880" } { "origin" "1082 1266 880" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "994 1442 880" } { "origin" "898 1714 880" "classname" "ambient_drip" } { "origin" "706 1362 1080" "classname" "ambient_drip" } { "origin" "1194 1522 1032" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "1314 1354 1032" } { "origin" "442 354 920" "classname" "ambient_swamp1" } { "origin" "978 314 920" "classname" "ambient_swamp2" } �{ "message" "the Ogre Citadel" "sounds" "8" "wad" "gfx/wizard.wad" "classname" "worldspawn" "worldtype" "0" } { "origin" "160 -160 120" "classname" "light" } { "angle" "90" "origin" "-256 -1952 280" "classname" "info_player_start" } { "classname" "light" "origin" "160 -392 248" "light" "200" } { "classname" "light" "origin" "160 -648 184" } { "classname" "light" "origin" "-56 -392 248" "light" "200" } { "classname" "light" "origin" "376 -392 248" "light" "200" } { "classname" "light" "origin" "288 -416 -72" "light" "200" } { "classname" "light" "origin" "32 -416 -72" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "10 -270 148" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "314 -270 148" "light" "250" } { "classname" "light" "origin" "-264 -440 248" "light" "200" } { "classname" "light" "origin" "584 -440 248" "light" "200" } { "classname" "light" "origin" "544 -648 248" } { "light" "150" "origin" "648 -456 -184" "classname" "light" } { "origin" "376 -800 184" "classname" "light" } { "classname" "light" "origin" "-152 -752 184" "light" "250" } { "classname" "light" "origin" "-232 -592 184" "light" "250" } { "classname" "light" "origin" "160 112 184" } { "classname" "light" "origin" "160 352 120" } { "classname" "light" "origin" "160 216 -48" "light" "120" } { "classname" "light" "origin" "160 16 -48" "light" "120" } { "classname" "light" "origin" "160 544 144" "light" "225" } { "classname" "light" "origin" "480 576 88" } { "classname" "light" "origin" "480 448 88" } { "classname" "light" "origin" "480 576 168" "light" "250" } { "classname" "light" "origin" "480 448 168" "light" "250" } { "classname" "light" "origin" "160 896 312" "light" "350" } { "classname" "light" "origin" "288 896 312" "light" "100" } { "classname" "light" "origin" "32 896 312" "light" "100" } { "classname" "light" "origin" "160 1008 312" "light" "100" } { "classname" "light" "origin" "160 784 312" "light" "100" } { "classname" "light" "origin" "392 120 184" "light" "350" } { "classname" "light" "origin" "568 208 184" "light" "350" } { "classname" "light" "origin" "720 480 184" } { "classname" "light" "origin" "640 632 184" } { "classname" "light" "origin" "472 1152 56" } { "classname" "light" "origin" "512 896 152" } { "origin" "800 800 184" "classname" "light" } { "light" "200" "origin" "632 1264 -40" "classname" "light" } { "origin" "800 1032 184" "classname" "light" } { "origin" "760 1472 64" "classname" "light" } { "light" "200" "origin" "544 1416 56" "classname" "light" } { "origin" "672 1256 184" "classname" "light" "light" "200" } { "light" "200" "origin" "1024 1272 184" "classname" "light" } { "light" "200" "origin" "992 1440 184" "classname" "light" } { "origin" "1240 488 184" "classname" "light" } { "origin" "1280 136 176" "classname" "light" } { "origin" "160 1304 136" "classname" "light" } { "origin" "160 1648 256" "classname" "light" "light" "200" } { "origin" "240 1600 256" "classname" "light" "light" "200" } { "origin" "16 1616 168" "classname" "light" "light" "200" } { "light" "200" "origin" "-120 1304 40" "classname" "light" } { "origin" "-352 1144 16" "classname" "light" "light" "250" } { "origin" "-56 1096 64" "classname" "light" } { "light" "200" "origin" "-56 1152 280" "classname" "light" } { "light" "350" "origin" "-440 1144 200" "classname" "light" } { "light" "200" "origin" "-352 1336 -72" "classname" "light" } { "origin" "-488 368 56" "classname" "light" "light" "250" } { "light" "350" "origin" "-488 896 136" "classname" "light" } { "origin" "-216 896 184" "classname" "light" "light" "250" } { "origin" "-128 536 168" "classname" "light" "light" "200" } { "light" "150" "origin" "-104 480 32" "classname" "light" } { "light" "150" "origin" "-208 936 56" "classname" "light" } { "light" "150" "origin" "-208 736 32" "classname" "light" } { "origin" "-344 64 184" "classname" "light" } { "light" "350" "origin" "-648 384 184" "classname" "light" } { "light" "350" "origin" "-488 688 184" "classname" "light" } { "light" "150" "origin" "-680 496 -36" "classname" "light" } { "light" "350" "origin" "-600 1104 96" "classname" "light" } { "origin" "-824 896 168" "classname" "light" } { "origin" "-896 600 160" "classname" "light" } { "target" "t1" "classname" "trigger_teleport" "model" "*1" } { "spawnflags" "1792" "targetname" "t1" "origin" "-448 264 -56" "classname" "info_teleport_destination" "angle" "45" } { "light" "250" "origin" "-168 1320 160" "classname" "light" } { "origin" "-24 896 184" "classname" "light" } { "origin" "-104 896 184" "classname" "light" "light" "250" } { "spawnflags" "2" "origin" "-680 880 48" "classname" "item_health" } { "angle" "90" "origin" "-896 512 24" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "-184 560 128" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "160 1640 160" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "1272 112 80" "classname" "info_player_deathmatch" } { "angle" "90" "origin" "176 -784 24" "classname" "info_player_deathmatch" } { "spawnflags" "1" "origin" "208 -160 0" "classname" "item_rockets" } { "origin" "64 464 0" "classname" "item_health" } { "origin" "120 464 0" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 840 64" "classname" "item_health" } { "spawnflags" "1" "origin" "-8 952 64" "classname" "item_health" } { "spawnflags" "9" "origin" "568 880 0" "classname" "item_weapon" } { "origin" "456 1104 -88" "classname" "item_health" } { "spawnflags" "1" "origin" "664 1272 -88" "classname" "item_shells" } { "angle" "225" "origin" "888 1312 120" "classname" "info_player_deathmatch" } { "origin" "1328 168 56" "classname" "item_health" } { "origin" "1328 128 56" "classname" "item_health" } { "origin" "1328 208 56" "classname" "item_health" } { "spawnflags" "1" "origin" "816 1472 0" "classname" "item_shells" } { "light" "200" "origin" "-64 -120 -56" "classname" "light" } { "light" "200" "origin" "-296 -160 -56" "classname" "light" } { "light" "200" "origin" "-296 -208 -272" "classname" "light" } { "classname" "func_plat" "model" "*2" } { "spawnflags" "2" "origin" "-192 -176 -80" "classname" "item_health" } { "light" "350" "origin" "-816 128 184" "classname" "light" } { "light" "350" "origin" "-664 -80 184" "classname" "light" } { "light" "250" "origin" "-656 112 32" "classname" "light" } { "origin" "-352 208 56" "classname" "light" "light" "250" } { "origin" "-184 504 24" "classname" "light" "light" "250" } { "classname" "item_armor1" "origin" "400 -888 0" } { "spawnflags" "1792" "classname" "item_armorInv" "origin" "-104 448 104" } { "classname" "item_armor2" "origin" "-680 496 32" } { "classname" "weapon_grenadelauncher" "origin" "168 1608 136" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "1232 176 56" "spawnflags" "1792" } { "classname" "weapon_supernailgun" "origin" "-960 944 40" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "752 1464 0" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "160 120 24" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-224 792 104" "spawnflags" "1792" } { "classname" "item_rockets" "origin" "-640 120 -56" "spawnflags" "1792" } { "light" "200" "origin" "-70 -1126 212" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "186 -1134 148" } { "light" "200" "origin" "138 -966 100" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "-150 -1318 260" } { "light" "150" "origin" "184 -880 72" "classname" "light" } { "style" "6" "light" "200" "origin" "-46 -1742 340" "classname" "light_torch_small_walltorch" } { "style" "1" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-486 -1726 340" } { "origin" "-272 -1544 384" "classname" "light" "light" "200" } { "style" "6" "light" "200" "classname" "light_torch_small_walltorch" "origin" "-414 -2006 340" } { "style" "1" "light" "200" "origin" "-134 -1958 340" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-472 -1840 328" "classname" "light" } { "classname" "light" "origin" "-72 -1592 328" "light" "150" } { "light" "150" "origin" "-320 -1424 264" "classname" "light" } { "light" "150" "origin" "-256 -1952 280" "classname" "light" } { "light" "200" "classname" "light" "origin" "-272 -1720 312" } { "origin" "-232 -1280 168" "classname" "path_corner" "targetname" "t5" "target" "t6" } { "classname" "path_corner" "origin" "-64 -1176 120" "targetname" "t6" "target" "t7" } { "origin" "152 -1104 56" "classname" "path_corner" "targetname" "t7" "target" "t8" } { "classname" "path_corner" "origin" "192 -952 8" "targetname" "t8" "target" "t9" } { "origin" "184 -808 8" "classname" "path_corner" "target" "t3" "targetname" "t9" } { "classname" "path_corner" "origin" "512 -776 8" "targetname" "t3" "target" "t10" } { "origin" "-200 -648 8" "classname" "path_corner" "targetname" "t4" "target" "t11" } { "classname" "monster_knight" "origin" "-360 -1616 232" "target" "t5" "angle" "90" } { "origin" "512 -648 8" "classname" "path_corner" "targetname" "t10" "target" "t4" } { "classname" "path_corner" "origin" "-200 -776 8" "targetname" "t11" "target" "t3" } { "classname" "monster_knight" "origin" "24 -632 24" "spawnflags" "256" "target" "t4" } { "classname" "monster_knight" "origin" "336 -752 24" "spawnflags" "256" "target" "t3" } { "classname" "monster_knight" "origin" "56 -712 24" "angle" "270" "spawnflags" "768" } { "spawnflags" "768" "angle" "270" "origin" "160 -712 24" "classname" "monster_knight" } { "classname" "monster_knight" "origin" "264 -712 24" "angle" "270" "spawnflags" "768" } { "classname" "item_health" "origin" "-432 -1640 208" "spawnflags" "1" } { "classname" "item_shells" "origin" "-352 -592 0" } { "classname" "func_door" "angle" "90" "spawnflags" "1" "targetname" "t13" "wait" "-1" "sounds" "3" "dmg" "100" "model" "*3" } { "health" "1" "angle" "90" "classname" "func_button" "target" "t12" "wait" "-1" "sounds" "1" "model" "*4" } { "classname" "func_button" "angle" "90" "health" "1" "target" "t12" "wait" "-1" "sounds" "1" "model" "*5" } { "classname" "trigger_counter" "targetname" "t12" "count" "2" "target" "t13" "model" "*6" } { "classname" "monster_demon1" "origin" "160 -128 24" "angle" "270" "targetname" "t12" } { "classname" "light" "origin" "-8 -288 64" "light" "200" } { "light" "200" "origin" "328 -288 64" "classname" "light" } { "light" "200" "origin" "-296 -448 -248" "classname" "light" } { "classname" "light" "origin" "160 -368 -248" "light" "200" } { "light" "200" "origin" "616 -448 -248" "classname" "light" } { "classname" "light" "origin" "408 -456 -248" "light" "200" } { "light" "200" "origin" "-40 -424 -248" "classname" "light" } { "classname" "light" "origin" "-280 -448 32" "light" "200" } { "light" "200" "origin" "632 -424 32" "classname" "light" } { "classname" "light" "origin" "160 -480 8" "light" "150" } { "classname" "func_door" "angle" "180" "targetname" "t12" "sounds" "3" "speed" "200" "wait" "-1" "lip" "-2" "model" "*7" } { "classname" "func_door" "angle" "0" "speed" "200" "wait" "-1" "lip" "-2" "model" "*8" } { "classname" "light" "origin" "160 -304 112" "light" "170" } { "classname" "light" "origin" "160 640 104" "light" "200" } { "classname" "func_door" "angle" "-1" "targetname" "t14" "sounds" "1" "model" "*9" } { "classname" "trigger_multiple" "target" "t14" "wait" "10" "model" "*10" } { "targetname" "t28" "classname" "func_door" "angle" "-1" "wait" "-1" "sounds" "1" "speed" "200" "spawnflags" "2048" "model" "*11" } { "angle" "270" "classname" "func_button" "target" "t15" "wait" "-1" "lip" "2" "sounds" "1" "spawnflags" "2048" "model" "*12" } { "classname" "light" "origin" "-296 432 0" "light" "200" } { "light" "200" "origin" "-312 416 152" "classname" "light" } { "classname" "light" "origin" "-184 320 146" "light" "200" } { "classname" "item_key2" "origin" "-552 192 -40" "sounds" "1" "spawnflags" "2048" } { "classname" "item_spikes" "origin" "-680 88 -48" } { "classname" "func_door" "angle" "-2" "spawnflags" "33" "speed" "10" "sounds" "3" "wait" "-1" "targetname" "t16" "dmg" "100" "model" "*13" "lip" "7" // svdijk -- added to prevent z-fighting } { "sounds" "3" "classname" "func_door" "angle" "90" "spawnflags" "2056" "wait" "-1" "model" "*14" } { "spawnflags" "2056" "angle" "270" "classname" "func_door" "wait" "-1" "model" "*15" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t16" "lip" "12" "model" "*16" } { "classname" "light" "origin" "888 1080 -104" "light" "160" } { "light" "160" "origin" "888 888 -104" "classname" "light" } { "classname" "light" "origin" "880 696 -104" "light" "160" } { "light" "160" "origin" "696 888 -104" "classname" "light" } { "classname" "light" "origin" "696 1080 -104" "light" "160" } { "light" "200" "origin" "696 672 -104" "classname" "light" } { "classname" "light" "origin" "568 512 -104" "light" "200" } { "light" "160" "origin" "840 504 -104" "classname" "light" } { "classname" "light" "origin" "832 328 -104" "light" "160" } { "classname" "light" "origin" "936 512 96" "light" "200" } { "classname" "light" "origin" "-248 1080 56" "light" "150" } { "classname" "light" "origin" "888 1264 176" "light" "170" } { "classname" "func_door" "angle" "-2" "targetname" "t17" "sounds" "1" "model" "*17" } { "classname" "trigger_multiple" "target" "t17" "wait" "5" "model" "*18" } { "classname" "light" "origin" "160 120 -24" "light" "160" } { "spawnflags" "256" "classname" "trigger_multiple" "target" "t18" "targetname" "t23" "model" "*19" } { "wait" "0.5" "classname" "trap_spikeshooter" "origin" "88 368 40" "angle" "0" "targetname" "t18" } { "classname" "func_wall" "spawnflags" "2048" "model" "*20" } { "classname" "monster_demon1" "origin" "-160 608 128" "targetname" "t19" } { "classname" "trigger_once" "target" "t19" "model" "*21" } { "classname" "light" "origin" "-528 512 -104" "light" "200" } { "light" "200" "origin" "-344 592 -104" "classname" "light" } { "classname" "light" "origin" "-336 760 -104" "light" "200" } { "light" "200" "origin" "-432 856 -104" "classname" "light" } { "classname" "monster_ogre" "origin" "-416 440 -40" "spawnflags" "1536" "target" "t20" } { "classname" "monster_shambler" "origin" "-272 296 -40" "spawnflags" "256" "target" "t20" } { "classname" "path_corner" "origin" "-328 272 -56" "targetname" "t20" "target" "t21" } { "origin" "-400 480 -56" "classname" "path_corner" "target" "t20" "targetname" "t21" } { "classname" "item_health" "origin" "-600 144 -64" } { "classname" "weapon_supershotgun" "origin" "440 512 0" "spawnflags" "2048" } { "classname" "light" "origin" "-120 176 184" "light" "250" } { "origin" "-96 0 184" "classname" "light" "light" "200" } { "classname" "light" "origin" "312 116 -48" "light" "200" } { "classname" "path_corner" "origin" "8 112 32" "target" "t25" "targetname" "t24" } { "origin" "312 112 32" "classname" "path_corner" "targetname" "t25" "target" "t24" } { "classname" "monster_ogre" "origin" "112 112 48" "target" "t24" "spawnflags" "256" } { "classname" "item_shells" "origin" "88 -160 0" "spawnflags" "1" } { "light" "250" "classname" "light" "origin" "-352 144 56" } { "classname" "monster_demon1" "origin" "-80 -440 -296" "spawnflags" "256" "target" "t26" } { "classname" "path_corner" "origin" "-216 -456 -312" "targetname" "t26" "target" "t27" } { "origin" "536 -448 -312" "classname" "path_corner" "target" "t26" "targetname" "t27" } { "light" "200" "origin" "320 232 -104" "classname" "light" } { "classname" "light" "origin" "312 0 -104" "light" "200" } { "light" "160" "origin" "-8 0 -104" "classname" "light" } { "classname" "light" "origin" "-8 232 -104" "light" "200" } { "light" "200" "origin" "-16 112 -104" "classname" "light" } { "classname" "light" "origin" "-248 -8 -104" "light" "200" } { "light" "200" "origin" "-472 120 -104" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "-1" "targetname" "t15" "sounds" "1" "spawnflags" "2048" "model" "*22" } { "classname" "func_door" "angle" "-2" "spawnflags" "2081" "targetname" "t28" "wait" "10" "speed" "200" "sounds" "1" "model" "*23" } { "classname" "trigger_once" "target" "t28" "model" "*24" } { "classname" "trigger_once" "target" "t12" "model" "*25" } { "classname" "monster_ogre" "origin" "160 1432 128" "angle" "270" } { "classname" "monster_ogre" "origin" "-216 784 128" "angle" "90" } { "classname" "info_teleport_destination" "origin" "-360 888 24" "spawnflags" "2048" "targetname" "t1" } { "classname" "monster_zombie" "origin" "-288 -232 -296" "angle" "270" } { "classname" "monster_zombie" "origin" "168 -8 -104" "angle" "90" } { "classname" "monster_ogre" "origin" "648 1232 -64" "angle" "180" } { "sounds" "1" "classname" "func_door" "angle" "-2" "targetname" "t29" "model" "*26" } { "classname" "monster_ogre" "origin" "520 752 24" "angle" "90" "spawnflags" "256" "targetname" "t29" } { "classname" "trigger_once" "target" "t29" "model" "*27" } { "classname" "weapon_nailgun" "origin" "152 1608 136" "spawnflags" "2048" } { "classname" "path_corner" "origin" "544 896 8" "targetname" "t30" "target" "t31" } { "origin" "56 896 72" "classname" "path_corner" "target" "t30" "targetname" "t31" } { "classname" "path_corner" "origin" "168 552 8" "targetname" "t32" "target" "t33" } { "origin" "168 1392 8" "classname" "path_corner" "target" "t32" "targetname" "t33" } { "classname" "monster_knight" "origin" "240 584 24" "target" "t32" } { "classname" "monster_knight" "origin" "504 960 24" "target" "t30" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-16 896 88" "angle" "0" } { "classname" "monster_knight" "origin" "256 1224 24" "angle" "225" "spawnflags" "256" } { "target" "t49" "origin" "-24 1064 16" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "45" "origin" "-184 1080 128" "classname" "monster_ogre" } { "angle" "45" "origin" "-256 1216 128" "classname" "monster_knight" } { "targetname" "t16" "angle" "270" "origin" "784 520 56" "classname" "monster_demon1" } { "classname" "path_corner" "origin" "-352 888 16" "targetname" "t34" "target" "t35" } { "origin" "-120 888 8" "classname" "path_corner" "target" "t34" "targetname" "t35" } { "classname" "monster_ogre" "origin" "-184 912 24" "target" "t34" "spawnflags" "256" } { "classname" "monster_ogre" "origin" "-720 896 64" } { "classname" "monster_knight" "origin" "-344 760 24" "angle" "135" "spawnflags" "256" } { "classname" "monster_knight" "origin" "-392 584 24" "angle" "90" "spawnflags" "256" } { "angle" "90" "classname" "monster_knight" "origin" "-528 528 24" "spawnflags" "768" } { "targetname" "t37" "target" "t36" "origin" "-896 968 48" "classname" "path_corner" } { "target" "t37" "targetname" "t36" "classname" "path_corner" "origin" "-896 568 48" } { "spawnflags" "256" "target" "t36" "origin" "-840 600 24" "classname" "monster_ogre" } { "style" "32" "targetname" "t15" "light" "200" "origin" "-56 304 152" "classname" "light" } { "dmg" "100" "speed" "200" "targetname" "t15" "target" "t38" "classname" "func_train" "model" "*28" } { "target" "t39" "targetname" "t40" "origin" "-216 280 104" "classname" "path_corner" } { "target" "t40" "targetname" "t38" "classname" "path_corner" "origin" "-15 280 104" // svdijk -- changed to prevent z-fighting (was "-16 280 104") } { "target" "t38" "wait" "-1" "targetname" "t39" "origin" "-15 280 104" // svdijk -- changed to prevent z-fighting (was "-16 280 104") "classname" "path_corner" } { "angle" "180" "origin" "680 1472 24" "classname" "monster_ogre" "spawnflags" "256" } { "spawnflags" "256" "angle" "180" "origin" "864 1448 24" "classname" "monster_knight" } { "target" "t42" "targetname" "t41" "origin" "1024 1264 104" "classname" "path_corner" } { "targetname" "t42" "target" "t41" "classname" "path_corner" "origin" "704 1264 104" } { "target" "t41" "origin" "1056 1320 120" "classname" "monster_knight" } { "spawnflags" "257" "origin" "552 1280 128" "classname" "monster_knight" } { "spawnflags" "1" "origin" "432 1280 128" "classname" "monster_knight" } { "spawnflags" "2048" "angle" "0" "wait" "-1" "sounds" "0" "health" "1" "target" "t28" "classname" "func_button" "model" "*29" } { "origin" "-72 296 104" "classname" "item_health" } { "classname" "item_health" "origin" "-64 504 104" } { "origin" "-312 192 -64" "classname" "item_health" } { "spawnflags" "1" "origin" "-80 384 -64" "classname" "item_spikes" } { "origin" "-224 976 104" "classname" "item_shells" } { "target" "t666" "classname" "trigger_multiple" "wait" "10" "model" "*30" } { "target" "t45" "targetname" "t44" "origin" "984 568 8" "classname" "path_corner" } { "target" "t46" "targetname" "t45" "classname" "path_corner" "origin" "976 464 8" } { "target" "t47" "targetname" "t46" "origin" "1224 448 40" "classname" "path_corner" } { "target" "t44" "targetname" "t43" "classname" "path_corner" "origin" "1272 520 40" } { "target" "t48" "targetname" "t47" "classname" "path_corner" "origin" "1224 144 64" } { "targetname" "t48" "target" "t43" "origin" "1344 120 64" "classname" "path_corner" } { "target" "t45" "origin" "968 520 24" "classname" "monster_ogre" } { "spawnflags" "256" "target" "t43" "origin" "1312 328 80" "classname" "monster_knight" } { "spawnflags" "768" "target" "t47" "origin" "1240 296 80" "classname" "monster_ogre" } { "origin" "-360 1064 -8" "classname" "weapon_grenadelauncher" } { "target" "t50" "targetname" "t49" "origin" "-16 1168 0" "classname" "path_corner" } { "targetname" "t50" "target" "t49" "classname" "path_corner" "origin" "-232 1168 0" } { "origin" "1448 -128 -56" "classname" "light" } { "wait" "-1" "classname" "func_door" "angle" "270" "model" "*31" } { "wait" "-1" "targetname" "t52" "sounds" "3" "classname" "func_door" "angle" "90" "model" "*32" } { "classname" "light" "origin" "1984 -184 288" "light" "350" } { "classname" "light" "origin" "1760 -312 -184" "light" "200" } { "origin" "1832 -64 -184" "classname" "light" "light" "200" } { "light" "160" "origin" "1808 -296 -24" "classname" "light" } { "classname" "light" "origin" "1224 -8 -184" "light" "160" } { "light" "160" "origin" "1240 -208 -184" "classname" "light" } { "classname" "light" "origin" "1680 -104 -184" "light" "160" } { "light" "160" "origin" "1584 -304 -184" "classname" "light" } { "origin" "1592 -72 288" "classname" "light" } { "classname" "light" "origin" "1328 -232 288" } { "classname" "light" "origin" "1280 -16 327" "light" "250" } { "classname" "light" "origin" "1792 -152 88" "light" "160" } { "light" "160" "origin" "1632 -296 88" "classname" "light" } { "origin" "1696 -280 288" "classname" "light" } { "classname" "light" "origin" "1240 -224 -8" "light" "200" } { "classname" "func_wall" "spawnflags" "3072" "model" "*33" } { "classname" "func_wall" "spawnflags" "3072" "model" "*34" } { "classname" "func_wall" "spawnflags" "3072" "model" "*35" } { "classname" "func_wall" "spawnflags" "3072" "model" "*36" } { "classname" "func_wall" "spawnflags" "3072" "model" "*37" } { "classname" "monster_ogre" "origin" "1992 -192 200" "angle" "180" "spawnflags" "256" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "targetname" "t51" "model" "*38" } { "classname" "trigger_multiple" "target" "t51" "model" "*39" } { "classname" "func_plat" "model" "*40" } { "classname" "light" "origin" "1480 56 -168" "light" "160" } { "origin" "1432 280 -64" "classname" "light" "light" "160" } { "light" "160" "classname" "light" "origin" "1424 280 96" } { "light" "160" "origin" "1472 232 -168" "classname" "light" } { "classname" "monster_zombie" "origin" "1592 -24 160" "angle" "180" } { "classname" "monster_zombie" "origin" "1432 -304 112" "angle" "135" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1304 -288 112" "angle" "90" "spawnflags" "256" } { "classname" "monster_zombie" "origin" "1576 -216 136" "angle" "180" "spawnflags" "768" } { "classname" "monster_zombie" "origin" "1928 -80 200" "spawnflags" "768" "angle" "180" } { "angle" "180" "spawnflags" "768" "origin" "1928 -280 200" "classname" "monster_zombie" } { "classname" "trigger_changelevel" "map" "e2m3" "model" "*41" } { "classname" "light" "origin" "80 1544 40" "light" "160" } { "light" "160" "origin" "-112 1544 40" "classname" "light" } { "classname" "item_shells" "origin" "1056 552 8" } { "classname" "light" "origin" "-112 1672 40" "light" "160" } { "light" "160" "origin" "88 1680 40" "classname" "light" } { "classname" "item_health" "origin" "-168 1688 -8" "spawnflags" "1" } { "classname" "monster_knight" "origin" "-112 1616 16" "angle" "45" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1760 -208 -216" "angle" "180" } { "classname" "trigger_secret" "model" "*42" } { "classname" "trigger_secret" "model" "*43" } { "classname" "trigger_secret" "model" "*44" } { "classname" "trigger_multiple" "target" "t29" "model" "*45" } { "classname" "item_shells" "origin" "-144 -1728 288" "spawnflags" "768" } { "classname" "item_rockets" "origin" "2000 -304 176" "spawnflags" "1793" } { "classname" "item_rockets" "origin" "2000 -112 176" "spawnflags" "1793" } { "origin" "-62 -702 72" "classname" "ambient_swamp1" } { "classname" "ambient_swamp2" "origin" "386 -702 72" } { "origin" "-246 -470 -264" "classname" "ambient_swamp2" } { "classname" "ambient_swamp1" "origin" "554 -454 -264" } { "origin" "162 -430 -264" "classname" "ambient_swamp1" } { "spawnflags" "1" "origin" "-72 576 104" "classname" "item_shells" } { "spawnflags" "2048" "wait" "10" "target" "t16" "classname" "trigger_multiple" "model" "*46" } { "targetname" "t16" "sounds" "4" "wait" "-1" "angle" "-1" "classname" "func_door" "model" "*47" } { "origin" "-184 1480 168" "classname" "light" "light" "160" } { "classname" "light" "origin" "-224 1592 168" "light" "100" } { "target" "t52" "classname" "trigger_once" "model" "*48" } { "mangle" "20 30 0" "origin" "1224 -288 336" "classname" "info_intermission" } { "mangle" "20 180 0" "origin" "-352 760 240" "classname" "info_intermission" } { "mangle" "20 135 0" "origin" "480 -440 208" "classname" "info_intermission" } { "angle" "90" "origin" "-176 -1904 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-128 -1848 264" "angle" "90" } { "angle" "90" "origin" "-192 -1808 264" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "-320 -1824 264" "angle" "90" } { "spawnflags" "1792" "classname" "func_wall" "model" "*49" } { "spawnflags" "1792" "origin" "200 -664 0" "classname" "weapon_lightning" } { "origin" "-184 1512 144" "classname" "item_artifact_super_damage" } { "classname" "item_cells" "origin" "240 -664 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "392 640 0" "spawnflags" "1793" } { "classname" "item_cells" "origin" "-168 456 104" "spawnflags" "1793" } { "classname" "func_door" "angle" "-1" "spawnflags" "1" "wait" "6" "speed" "1000" "sounds" "3" "targetname" "t15" "model" "*50" } { "classname" "weapon_grenadelauncher" "origin" "1312 280 56" "spawnflags" "3584" } { "sounds" "2" "wait" "5" "message" "Shoot the buttons..." "spawnflags" "3584" "classname" "trigger_multiple" "targetname" "t53" "model" "*51" } { "classname" "trigger_relay" "origin" "-72 -320 48" "targetname" "t13" "killtarget" "t53" } �{ "classname" "worldspawn" "wad" "gfx/jr_med.wad" "worldtype" "0" "sounds" "9" "message" "the Crypt of Decay" } { "classname" "light" "origin" "192 -648 128" } { "classname" "info_player_start" "origin" "688 -1600 -312" "angle" "180" } { "classname" "light_flame_large_yellow" "origin" "650 -438 4" } { "origin" "386 -438 4" "classname" "light_flame_large_yellow" } { "origin" "66 -886 4" "classname" "light_flame_large_yellow" } { "origin" "322 -886 4" "classname" "light_flame_large_yellow" } { "light" "250" "origin" "192 -1408 288" "classname" "light" } { "light" "250" "classname" "light" "origin" "192 -1088 288" } { "light" "250" "origin" "112 -1248 272" "classname" "light" } { "light" "250" "classname" "light" "origin" "272 -1248 272" } { "light" "200" "origin" "192 -1056 32" "classname" "light" } { "light" "150" "origin" "192 -1248 24" "classname" "light" } { "origin" "194 -1462 108" "classname" "light_flame_large_yellow" } { "origin" "194 -1030 164" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "192 -1440 32" "classname" "light" } { "sounds" "2" "classname" "func_plat" "spawnflags" "1" "model" "*1" } { "origin" "226 -1670 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "88 -1552 -184" "classname" "light" } { "origin" "-22 -1374 -212" "classname" "light_flame_large_yellow" } { "light" "150" "origin" "328 -1256 -184" "classname" "light" } { "light" "150" "classname" "light" "origin" "56 -1256 -184" } { "classname" "light" "origin" "248 -1480 -184" "light" "150" } { "light" "250" "origin" "552 -1608 -72" "classname" "light" } { "light" "150" "origin" "432 -1656 -224" "classname" "light" } { "light" "150" "origin" "432 -1496 -224" "classname" "light" } { "light" "100" "origin" "192 -1248 -40" "classname" "light" } { "origin" "10 -438 4" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-254 -438 4" } { "light" "150" "origin" "192 -704 -136" "classname" "light" } { "light" "250" "origin" "192 -512 -136" "classname" "light" } { "classname" "light" "origin" "416 -512 -136" "light" "150" } { "light" "150" "origin" "-32 -512 -136" "classname" "light" } { "classname" "light" "origin" "-208 -512 -136" "light" "150" } { "light" "150" "origin" "592 -512 -136" "classname" "light" } { "classname" "light" "origin" "192 -840 -136" "light" "150" } { "light" "150" "origin" "-352 -672 -168" "classname" "light" } { "classname" "light" "origin" "-320 -832 -168" "light" "150" } { "light" "150" "origin" "-320 -512 -168" "classname" "light" } { "classname" "light" "origin" "696 -512 -168" "light" "150" } { "light" "150" "origin" "736 -672 -168" "classname" "light" } { "classname" "light" "origin" "704 -832 -168" "light" "150" } { "light" "150" "origin" "512 -864 -168" "classname" "light" } { "classname" "light" "origin" "-128 -864 -168" "light" "150" } { "light" "200" "origin" "-128 -320 8" "classname" "light" } { "classname" "light" "origin" "512 -320 8" "light" "200" } { "origin" "384 -24 32" "classname" "light" } { "classname" "light" "origin" "0 -24 32" } { "light" "200" "origin" "416 -192 -8" "classname" "light" } { "classname" "light" "origin" "-32 -192 -8" "light" "200" } { "light" "200" "origin" "840 48 72" "classname" "light" } { "light" "150" "origin" "576 -24 -56" "classname" "light" } { "light" "200" "origin" "624 -24 72" "classname" "light" } { "origin" "1002 354 -60" "classname" "light_flame_large_yellow" } { "light" "100" "origin" "1000 352 -128" "classname" "light" } { "classname" "light" "origin" "736 8 72" "light" "200" } { "light" "200" "origin" "936 88 72" "classname" "light" } { "light" "150" "origin" "688 -8 -32" "classname" "light" } { "classname" "light" "origin" "784 24 -104" "light" "150" } { "light" "150" "origin" "888 72 -32" "classname" "light" } { "light" "200" "origin" "872 208 -56" "classname" "light" } { "classname" "light" "origin" "872 400 -56" "light" "200" } { "light" "200" "origin" "872 592 -56" "classname" "light" } { "classname" "light" "origin" "744 568 88" "light" "150" } { "light" "150" "origin" "744 648 88" "classname" "light" } { "classname" "light" "origin" "704 608 -80" "light" "150" } { "origin" "866 730 -60" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "864 728 -128" "light" "100" } { "sounds" "3" "wait" "-1" "targetname" "t8" "spawnflags" "2049" "angle" "0" "classname" "func_door" "model" "*2" } { "spawnflags" "2048" "angle" "90" "target" "t8" "classname" "func_button" "wait" "-1" "model" "*3" } { "origin" "520 608 -64" "classname" "light" } { "light" "400" "origin" "192 608 -24" "classname" "light" } { "sounds" "1" "wait" "-1" "angle" "270" "spawnflags" "2058" "classname" "func_door_secret" "targetname" "t9" "model" "*4" } { "light" "150" "origin" "1064 640 -112" "classname" "light" } { "targetname" "t9" "angle" "180" "origin" "1024 640 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1120 672 -152" "classname" "monster_zombie" } { "targetname" "t9" "angle" "180" "origin" "1088 600 -152" "classname" "monster_zombie" } { "origin" "976 336 -176" "classname" "item_health" } { "light" "150" "origin" "192 608 -104" "classname" "light" } { "origin" "192 288 -64" "classname" "light" } { "classname" "light_flame_large_yellow" "origin" "66 106 4" "light" "200" } { "light" "200" "origin" "-30 106 4" "classname" "light_flame_large_yellow" } { "classname" "light" "origin" "504 120 -248" "light" "200" } { "light" "200" "origin" "704 224 -248" "classname" "light" } { "classname" "light" "origin" "704 472 -248" "light" "200" } { "light" "200" "origin" "304 112 -248" "classname" "light" } { "classname" "light" "origin" "80 112 -248" "light" "200" } { "light" "200" "origin" "720 608 -248" "classname" "light" } { "spawnflags" "2048" "sounds" "1" "wait" "-1" "targetname" "t3" "classname" "func_door" "angle" "90" "model" "*5" } { "spawnflags" "2048" "wait" "-1" "angle" "270" "classname" "func_door" "message" "This door opens nearby..." "model" "*6" } { "spawnflags" "2048" "target" "t3" "wait" "-1" "classname" "func_button" "angle" "180" "model" "*7" } { "light" "250" "origin" "-448 184 0" "classname" "light" } { "light" "150" "origin" "-552 280 -224" "classname" "light" } { "classname" "light" "origin" "-392 280 -224" "light" "150" } { "light" "150" "origin" "-416 -32 104" "classname" "light" } { "classname" "light" "origin" "-320 -32 104" "light" "150" } { "light" "150" "origin" "-224 -32 104" "classname" "light" } { "light" "250" "origin" "-352 -32 -32" "classname" "light" } { "light" "200" "origin" "-1160 88 -248" "classname" "light" } { "light" "200" "origin" "-1048 88 -32" "classname" "light" } { "light" "200" "classname" "light" "origin" "-1160 224 -32" } { "origin" "-742 658 -44" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-926 658 -44" } { "light" "200" "origin" "-736 632 -96" "classname" "light" } { "classname" "light" "origin" "-928 632 -96" "light" "200" } { "light" "150" "origin" "-600 104 -248" "classname" "light" } { "classname" "light" "origin" "-696 424 -248" "light" "150" } { "light" "150" "origin" "-1152 408 -248" "classname" "light" } { "classname" "light" "origin" "-944 432 -248" "light" "150" } { "light" "150" "origin" "-856 64 -248" "classname" "light" } { "classname" "light" "origin" "-160 152 -248" "light" "150" } { "light" "150" "origin" "-160 448 -248" "classname" "light" } { "classname" "light" "origin" "-328 464 -248" "light" "150" } { "light" "150" "origin" "-256 176 -248" "classname" "light" } { "origin" "-574 410 -172" "classname" "light_flame_large_yellow" } { "classname" "light_flame_large_yellow" "origin" "-470 410 -172" } { "target" "t4" "classname" "trigger_teleport" "model" "*8" } { "light" "200" "style" "2" "origin" "-760 576 -216" "classname" "light" } { "targetname" "t4" "angle" "180" "origin" "120 -32 -112" "classname" "info_teleport_destination" } { "light" "200" "origin" "-264 384 112" "classname" "light" } { "classname" "light" "origin" "-264 288 112" "light" "200" } { "light" "200" "origin" "-264 192 112" "classname" "light" } { "classname" "light" "origin" "-264 480 112" "light" "200" } { "light" "250" "origin" "-264 304 -56" "classname" "light" } { "classname" "light" "origin" "-520 424 0" "light" "250" } { "light" "200" "origin" "-1120 608 72" "classname" "light" } { "classname" "light" "origin" "-1024 584 72" "light" "200" } { "light" "200" "origin" "-928 584 72" "classname" "light" } { "classname" "light" "origin" "-832 584 72" "light" "200" } { "light" "200" "origin" "-544 584 72" "classname" "light" } { "classname" "light" "origin" "-640 584 72" "light" "200" } { "light" "150" "origin" "-480 768 56" "classname" "light" } { "classname" "light" "origin" "-384 768 56" "light" "150" } { "classname" "light" "origin" "-712 120 -32" "light" "200" } { "sounds" "3" "wait" "-1" "targetname" "t5" "spawnflags" "2049" "angle" "180" "classname" "func_door" "model" "*9" } { "spawnflags" "2048" "angle" "270" "target" "t5" "wait" "-1" "classname" "func_button" "model" "*10" } { "style" "32" "targetname" "t5" "light" "200" "origin" "-352 552 -56" "classname" "light" } { "light" "150" "origin" "-432 768 -56" "classname" "light" } { "light" "150" "origin" "-520 680 -56" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "targetname" "t5" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*11" } { "sounds" "3" "targetname" "t5" "spawnflags" "2049" "wait" "-1" "angle" "90" "classname" "func_door" "model" "*12" } { "origin" "-72 848 -56" "classname" "light" } { "origin" "-120 600 -8" "classname" "light" } { "classname" "light" "origin" "192 904 -8" } { "light" "200" "origin" "192 888 -248" "classname" "light" } { "classname" "light" "origin" "-104 600 -248" "light" "200" } { "light" "200" "origin" "376 984 -120" "classname" "light" } { "classname" "light" "origin" "504 760 -120" "light" "200" } { "light" "200" "origin" "-32 608 200" "classname" "light" } { "spawnflags" "2048" "sounds" "3" "wait" "-1" "angle" "-2" "classname" "func_door" "model" "*13" } { "origin" "-16 1456 16" "classname" "light" } { "light" "200" "origin" "384 1248 -56" "classname" "light" } { "classname" "light" "origin" "384 1440 -56" "light" "200" } { "light" "150" "origin" "256 1440 -56" "classname" "light" } { "classname" "light" "origin" "192 1248 -56" "light" "200" } { "light" "200" "origin" "384 1344 -88" "classname" "light" } { "classname" "light" "origin" "192 1152 -88" "light" "200" } { "classname" "light" "origin" "8 1456 -120" "light" "200" } { "spawnflags" "2048" "sounds" "1" "classname" "item_key2" "origin" "-16 1456 -152" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*14" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "model" "*15" } { "classname" "func_door" "angle" "-1" "targetname" "t6" "speed" "400" "wait" "-1" "sounds" "4" "spawnflags" "2048" "model" "*16" } { "classname" "trigger_once" "target" "t6" "model" "*17" } { "classname" "light" "origin" "-192 1456 -136" "light" "80" } { "classname" "light" "origin" "-16 1280 -136" "light" "80" } { "spawnflags" "768" "classname" "monster_hell_knight" "origin" "-16 1280 -168" "angle" "90" "targetname" "t6" } { "spawnflags" "256" "angle" "270" "origin" "-16 1632 -168" "classname" "monster_hell_knight" "targetname" "t6" } { "classname" "monster_hell_knight" "origin" "-192 1456 -168" "angle" "0" "targetname" "t6" } { "classname" "light" "origin" "152 1440 -56" "light" "150" } { "classname" "item_shells" "origin" "-104 1512 -192" "spawnflags" "1" } { "wait" "-1" "classname" "func_door" "angle" "0" "spawnflags" "2056" "model" "*18" } { "wait" "-1" "classname" "func_door" "angle" "180" "spawnflags" "2056" "model" "*19" } { "classname" "light" "origin" "-1120 832 -48" "light" "250" } { "classname" "light" "origin" "-1120 976 -24" "light" "250" } { "classname" "light" "origin" "-1240 1296 216" "light" "200" } { "light" "200" "origin" "-1240 1416 216" "classname" "light" } { "classname" "light" "origin" "-1240 1176 216" "light" "200" } { "classname" "light" "origin" "-712 1416 216" "light" "200" } { "light" "200" "origin" "-712 1296 216" "classname" "light" } { "classname" "light" "origin" "-712 1176 216" "light" "200" } { "light" "200" "origin" "-856 1296 216" "classname" "light" } { "classname" "light" "origin" "-1096 1296 216" "light" "200" } { "light" "200" "origin" "-976 1296 216" "classname" "light" } { "classname" "light_flame_small_white" "origin" "-1318 1514 -8" } { "origin" "-1318 1514 64" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-1318 1514 144" } { "origin" "-634 1078 -8" "classname" "light_flame_small_white" } { "classname" "light_flame_small_white" "origin" "-634 1078 64" } { "origin" "-634 1078 144" "classname" "light_flame_small_white" } { "classname" "func_plat" "spawnflags" "1" "model" "*20" } { "classname" "light" "origin" "-320 1536 184" "light" "250" } { "classname" "light" "origin" "-376 1312 120" "light" "250" } { "classname" "light" "origin" "-16 1536 184" "light" "200" } { "light" "200" "origin" "-560 1312 56" "classname" "light" } { "classname" "light" "origin" "24 1120 216" "light" "150" } { "classname" "light" "origin" "192 608 176" "light" "200" } { "wait" "-1" "classname" "func_door" "angle" "90" "spawnflags" "2049" "targetname" "t7" "sounds" "1" "model" "*21" } { "light" "150" "origin" "400 1104 224" "classname" "light" } { "light" "150" "origin" "-200 1056 224" "classname" "light" } { "light" "200" "origin" "192 960 224" "classname" "light" } { "light" "200" "origin" "192 784 224" "classname" "light" } { "classname" "light" "origin" "672 264 184" "light" "200" } { "light" "200" "origin" "384 184 184" "classname" "light" } { "classname" "light" "origin" "512 256 8" "light" "200" } { "classname" "light" "origin" "8 184 200" "light" "200" } { "classname" "light" "origin" "584 744 200" "light" "200" } { "classname" "light" "origin" "0 432 200" "light" "200" } { "classname" "trigger_once" "targetname" "t8" "target" "t9" "delay" "10" "model" "*22" } { "classname" "light" "origin" "192 -72 216" "light" "250" } { "classname" "light" "origin" "360 -168 360" "light" "200" } { "classname" "light" "origin" "296 -168 360" "light" "200" } { "origin" "472 -168 368" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "408 -168 360" } { "classname" "light" "origin" "384 -168 248" "light" "200" } { "classname" "light" "origin" "936 -304 328" "light" "200" } { "light" "200" "origin" "1000 -232 360" "classname" "light" } { "classname" "light" "origin" "864 -232 360" "light" "200" } { "light" "200" "origin" "864 -368 360" "classname" "light" } { "classname" "light" "origin" "1000 -368 360" "light" "200" } { "classname" "light" "origin" "736 -248 280" "light" "200" } { "light" "200" "origin" "552 -216 280" "classname" "light" } { "classname" "light" "origin" "16 144 -32" "light" "200" } { "classname" "light" "origin" "0 432 -136" "light" "200" } { "classname" "light" "origin" "192 384 184" "light" "200" } { "light" "200" "origin" "192 192 184" "classname" "light" } { "spawnflags" "2048" "classname" "func_button" "wait" "-1" "target" "t7" "model" "*23" } { "style" "33" "targetname" "t7" "classname" "light" "origin" "176 1152 200" "target" "t10" } { "classname" "info_null" "origin" "292 1152 180" "targetname" "t10" } { "classname" "light" "origin" "192 1224 200" "light" "150" } { "light" "150" "origin" "192 1376 200" "classname" "light" } { "classname" "light" "origin" "192 1536 200" "light" "200" } { "light" "150" "origin" "192 1080 200" "classname" "light" } { "classname" "weapon_nailgun" "origin" "184 -1520 -272" } { "classname" "func_button" "target" "t11" "angle" "-1" "targetname" "t12" "lip" "4" "wait" "0.1" "speed" "300" "health" "1" "model" "*24" } { "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t11" "wait" "10" "model" "*25" "lip" "7" // svdijk -- added to prevent z-fighting } { "classname" "func_door_secret" "angle" "90" "spawnflags" "8" "targetname" "t11" "model" "*26" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "light" "origin" "1248 -288 312" "light" "150" } { "light" "200" "origin" "1176 -400 352" "classname" "light" } { "classname" "item_health" "origin" "1336 -536 256" "spawnflags" "2" } { "classname" "light" "origin" "1320 -488 352" "light" "200" } { "classname" "trigger_multiple" "target" "t11" "wait" "10" "model" "*27" } { "classname" "item_armor1" "origin" "192 -592 -64" } { "classname" "item_shells" "origin" "176 592 -160" "spawnflags" "1" } { "classname" "weapon_nailgun" "origin" "-80 1456 -192" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "origin" "56 1144 128" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "-736 608 -280" "spawnflags" "1792" } { "classname" "item_spikes" "origin" "1120 -384 256" "spawnflags" "1" } { "classname" "item_rockets" "origin" "840 -432 192" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-456 312 -80" } { "classname" "monster_zombie" "origin" "-1152 96 -88" "spawnflags" "256" "target" "t36" } { "origin" "-1120 32 -88" "classname" "monster_zombie" "spawnflags" "768" "target" "t36" } { "classname" "monster_zombie" "origin" "-1192 168 -88" "target" "t36" } { "classname" "monster_shambler" "origin" "-1120 1104 -56" "angle" "270" } { "classname" "monster_hell_knight" "origin" "-336 1312 8" "angle" "180" } { "classname" "monster_ogre" "origin" "-698 1446 -56" "angle" "270" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "-888 1128 8" "angle" "90" "spawnflags" "256" } { "classname" "item_health" "origin" "-336 784 -128" } { "origin" "-408 608 -128" "classname" "item_health" } { "classname" "path_corner" "origin" "-1096 584 -120" "targetname" "t13" "target" "t14" } { "origin" "-496 584 -120" "classname" "path_corner" "targetname" "t14" "target" "t13" } { "classname" "monster_demon1" "origin" "-712 576 -104" "angle" "180" "target" "t13" } { "classname" "path_corner" "origin" "-528 472 -96" "targetname" "t15" "target" "t16" } { "origin" "-368 -32 -96" "classname" "path_corner" "target" "t15" "targetname" "t16" } { "classname" "monster_ogre" "origin" "-466 262 -56" "target" "t16" "spawnflags" "256" } { "target" "t22" "targetname" "t21" "origin" "56 -184 -120" "classname" "path_corner" } { "target" "t21" "targetname" "t20" "classname" "path_corner" "origin" "-128 -224 -120" } { "target" "t20" "targetname" "t19" "origin" "-128 -504 -56" "classname" "path_corner" } { "target" "t19" "targetname" "t18" "classname" "path_corner" "origin" "512 -504 -56" } { "target" "t18" "targetname" "t17" "origin" "512 -224 -120" "classname" "path_corner" } { "targetname" "t26" "target" "t17" "classname" "path_corner" "origin" "328 -184 -120" } { "target" "t23" "targetname" "t22" "classname" "path_corner" "origin" "-128 -200 -120" } { "target" "t24" "targetname" "t23" "classname" "path_corner" "origin" "-128 -552 -56" } { "target" "t25" "targetname" "t24" "origin" "512 -552 -56" "classname" "path_corner" } { "target" "t26" "targetname" "t25" "classname" "path_corner" "origin" "512 -200 -120" } { "spawnflags" "256" "target" "t21" "origin" "0 -184 -104" "classname" "monster_hell_knight" } { "spawnflags" "1" "origin" "376 -160 -104" "classname" "monster_hell_knight" } { "spawnflags" "768" "angle" "270" "origin" "190 -706 -40" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "90" "origin" "192 -1408 24" "classname" "monster_hell_knight" } { "origin" "40 -1424 0" "classname" "item_shells" "spawnflags" "1" } { "spawnflags" "1" "origin" "304 -1096 0" "classname" "item_health" } { "origin" "328 -1280 -272" "classname" "item_health" } { "origin" "40 -1256 -272" "classname" "item_spikes" } { "target" "t28" "targetname" "t27" "origin" "864 176 -168" "classname" "path_corner" } { "target" "t27" "targetname" "t28" "classname" "path_corner" "origin" "864 616 -168" } { "target" "t27" "origin" "862 446 -152" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "180" "origin" "526 -26 -104" "classname" "monster_ogre" } { "spawnflags" "256" "angle" "0" "origin" "-72 -24 -104" "classname" "monster_hell_knight" } { "spawnflags" "256" "origin" "-274 -34 -104" "classname" "monster_ogre" } { "spawnflags" "768" "angle" "270" "origin" "-512 760 -104" "classname" "monster_hell_knight" } { "spawnflags" "1793" "origin" "336 1104 128" "classname" "item_rockets" } { "angle" "45" "spawnflags" "256" "origin" "104 256 -136" "classname" "monster_zombie" } { "spawnflags" "769" "angle" "90" "origin" "192 488 -136" "classname" "monster_hell_knight" } { "wait" "1" "speed" "250" "lip" "16" "spawnflags" "5" "targetname" "t29" "dmg" "20" "angle" "180" "classname" "func_door" "model" "*28" } { "wait" "1" "targetname" "t29" "speed" "250" "dmg" "20" "lip" "16" "spawnflags" "5" "classname" "func_door" "sounds" "1" "model" "*29" } { "wait" "2" "target" "t29" "classname" "trigger_multiple" "model" "*30" } { "origin" "506 1762 -124" "classname" "light_torch_small_walltorch" "style" "1" } { "classname" "light_torch_small_walltorch" "origin" "274 2010 -124" } { "light" "200" "origin" "152 1824 -40" "classname" "light" } { "classname" "light" "origin" "288 1824 -40" "light" "200" } { "light" "200" "origin" "288 1696 -40" "classname" "light" } { "classname" "light" "origin" "152 1704 -40" "light" "200" } { "light" "220" "origin" "0 1704 -40" "classname" "light" } { "angle" "180" "classname" "func_door_secret" "targetname" "t35" "spawnflags" "16" "sounds" "1" "model" "*31" } { "spawnflags" "2" "origin" "-16 1816 -192" "classname" "item_health" } { "origin" "432 1672 -320" "classname" "light" "light" "220" } { "spawnflags" "1792" "origin" "-16 1752 -192" "classname" "item_rockets" } { "angle" "270" "origin" "-192 1580 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "236 1536 168" "angle" "180" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "0 1580 168" "angle" "270" "targetname" "t32" } { "angle" "90" "origin" "-96 1492 168" "classname" "trap_spikeshooter" "targetname" "t32" } { "classname" "trap_spikeshooter" "origin" "148 1376 168" "angle" "0" "targetname" "t33" } { "angle" "180" "origin" "236 1256 168" "classname" "trap_spikeshooter" "targetname" "t33" } { "spawnflags" "256" "classname" "trap_spikeshooter" "origin" "192 1580 168" "angle" "270" "targetname" "t33" } { "spawnflags" "257" "angle" "90" "origin" "192 -16 153" "classname" "monster_hell_knight" } { "spawnflags" "257" "angle" "180" "origin" "864 -248 217" "classname" "monster_shambler" } { "angle" "180" "origin" "464 -184 185" "classname" "monster_hell_knight" } { "classname" "monster_hell_knight" "origin" "192 -176 153" "angle" "90" "spawnflags" "1" "target" "t31" } { "spawnflags" "769" "angle" "90" "origin" "190 1166 153" "classname" "monster_ogre" } { "spawnflags" "2048" "origin" "48 1456 -192" "classname" "weapon_grenadelauncher" } { "spawnflags" "1" "origin" "-96 1376 -192" "classname" "item_rockets" } { "angle" "270" "spawnflags" "768" "classname" "monster_ogre" "origin" "862 662 -152" } { "light" "120" "origin" "192 -352 -264" "classname" "light" } { "spawnflags" "768" "origin" "326 -1490 -248" "classname" "monster_ogre" "angle" "180" "target" "t42" } { "origin" "192 552 128" "classname" "item_health" } { "spawnflags" "1024" "classname" "item_health" "origin" "176 672 128" } { "spawnflags" "1025" "origin" "184 1312 128" "classname" "item_health" } { "classname" "item_health" "origin" "-16 1480 128" "spawnflags" "1025" } { "origin" "672 -328 176" "classname" "item_health" } { "classname" "item_health" "origin" "776 -192 176" } { "origin" "784 312 -176" "classname" "item_shells" } { "spawnflags" "256" "angle" "315" "origin" "-26 1094 152" "classname" "monster_ogre" } { "classname" "monster_ogre" "origin" "406 1094 152" "angle" "225" "spawnflags" "768" } { "spawnflags" "1" "origin" "-200 1128 128" "classname" "item_rockets" } { "origin" "-550 -478 212" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "-550 -166 212" } { "origin" "-214 -326 252" "classname" "light_torch_small_walltorch" } { "light" "150" "origin" "-120 -176 192" "classname" "light" } { "classname" "light" "origin" "56 -176 192" "light" "150" } { "origin" "-816 1488 -80" "classname" "item_health" } { "classname" "item_health" "origin" "-752 1488 -80" } { "spawnflags" "1536" "origin" "-688 1488 -80" "classname" "item_health" } { "spawnflags" "1" "origin" "176 1264 -160" "classname" "item_shells" } { "classname" "func_door" "angle" "270" "targetname" "t31" "wait" "-1" "spawnflags" "2048" "model" "*32" } { "wait" "-1" "sounds" "1" "speed" "300" "classname" "func_door" "angle" "90" "model" "*33" } { "speed" "300" "classname" "func_door" "angle" "270" "wait" "-1" "model" "*34" } { "classname" "item_health" "origin" "400 1464 -160" "spawnflags" "1024" } { "classname" "item_health" "origin" "-1136 528 -128" } { "classname" "monster_ogre" "origin" "-370 -218 88" "targetname" "t31" "spawnflags" "1" } { "classname" "monster_hell_knight" "origin" "-64 -176 152" "angle" "0" "spawnflags" "768" "targetname" "t31" } { "classname" "item_health" "origin" "-488 -480 64" } { "classname" "light" "origin" "680 -1600 -160" } { "classname" "item_spikes" "origin" "984 -192 192" "spawnflags" "1" } { "classname" "monster_ogre" "origin" "-490 -410 88" "angle" "45" } { "classname" "trigger_multiple" "target" "t32" "wait" "1" "spawnflags" "1024" "targetname" "t44" "model" "*35" } { "classname" "trigger_multiple" "target" "t33" "wait" "1" "spawnflags" "1024" "model" "*36" } { "classname" "item_rockets" "origin" "-96 272 -384" } { "classname" "weapon_supernailgun" "origin" "-1200 208 -112" "spawnflags" "1792" } { "classname" "func_door_secret" "spawnflags" "2051" "targetname" "t36" "angle" "90" "model" "*37" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "light" "origin" "-1288 640 -80" "light" "160" } { "light" "160" "origin" "-1288 128 -80" "classname" "light" } { "classname" "light" "origin" "-1288 264 -80" "light" "160" } { "light" "160" "origin" "-1288 392 -80" "classname" "light" } { "classname" "light" "origin" "-1288 520 -80" "light" "160" } { "classname" "func_door_secret" "targetname" "t36" "angle" "270" "spawnflags" "2049" "model" "*38" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "item_armor2" "origin" "1128 600 -176" "spawnflags" "1024" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "11" "targetname" "t34" "model" "*39" } { "classname" "light_torch_small_walltorch" "origin" "-246 -1310 -204" "light" "250" } { "classname" "trigger_once" "target" "t34" "model" "*40" } { "classname" "item_spikes" "origin" "-240 -1288 -312" "spawnflags" "1" } { "spawnflags" "1" "origin" "-240 -1368 -312" "classname" "item_spikes" } { "sounds" "3" "classname" "func_door" "angle" "-2" "spawnflags" "3585" "wait" "90" "targetname" "t6" "model" "*41" } { "sounds" "3" "classname" "func_door" "targetname" "t6" "spawnflags" "3585" "angle" "-2" "wait" "90" "model" "*42" } { "sounds" "3" "classname" "func_door" "angle" "0" "spawnflags" "1537" "targetname" "t6" "wait" "90" "model" "*43" } { "classname" "light_torch_small_walltorch" "origin" "-42 1642 -108" "light" "200" } { "classname" "item_health" "origin" "-72 560 -384" } { "origin" "-152 560 -384" "classname" "item_health" } { "targetname" "t31" "spawnflags" "513" "angle" "90" "origin" "848 -376 216" "classname" "monster_hell_knight" } { "targetname" "t31" "spawnflags" "768" "origin" "-512 -248 88" "classname" "monster_shambler" } { "spawnflags" "768" "angle" "180" "origin" "382 1246 -136" "classname" "monster_ogre" } { "spawnflags" "768" "classname" "monster_ogre" "origin" "190 1438 -136" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "640 -1664 -312" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-400 -240 88" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-216 1088 152" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "-512 792 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "888 624 -152" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "-96 1456 -168" "angle" "0" } { "spawnflags" "2048" "classname" "func_wall" "model" "*44" } { "spawnflags" "2048" "classname" "func_wall" "model" "*45" } { "classname" "func_plat" "height" "192" "sounds" "2" "model" "*46" } { "classname" "item_health" "origin" "-1184 72 -352" } { "classname" "light" "origin" "-952 1408 80" "light" "200" } { "classname" "light" "origin" "-1120 1176 48" "light" "250" } { "classname" "light" "origin" "-104 288 -344" "light" "200" } { "classname" "light" "origin" "-512 376 -304" "light" "200" } { "classname" "trigger_multiple" "target" "t35" "model" "*47" } { "classname" "light" "origin" "192 -376 -16" "light" "150" } { "classname" "light" "origin" "192 -328 176" "light" "150" } { "classname" "trigger_multiple" "target" "t32" "spawnflags" "768" "wait" "0.5" "model" "*48" } { "classname" "trigger_multiple" "spawnflags" "768" "wait" "0.5" "target" "t33" "targetname" "t44" "model" "*49" } { "origin" "-1176 112 -112" "classname" "item_rockets" } { "origin" "-736 544 -280" "classname" "item_armorInv" } { "classname" "func_button" "angle" "180" "target" "t36" "model" "*50" } { "classname" "info_null" "origin" "-1332 1116 -36" "targetname" "t37" } { "classname" "light" "origin" "-1296 1120 -32" "target" "t37" "angle" "60" } { "spawnflags" "2048" "classname" "func_wall" "model" "*51" } { "spawnflags" "2048" "classname" "func_wall" "model" "*52" } { "classname" "light" "origin" "192 -152 -344" "light" "160" } { "classname" "light" "origin" "192 32 -344" "light" "160" } { "classname" "light" "origin" "32 168 -344" "light" "140" } { "light" "160" "origin" "-16 408 -344" "classname" "light" } { "classname" "light" "origin" "192 368 -344" "light" "140" } { "light" "140" "origin" "448 368 -344" "classname" "light" } { "classname" "light" "origin" "528 592 -344" "light" "200" } { "light" "200" "origin" "408 808 -344" "classname" "light" } { "classname" "light" "origin" "192 832 -344" "light" "200" } { "light" "200" "origin" "-40 728 -344" "classname" "light" } { "classname" "light" "origin" "-168 632 -344" "light" "200" } { "light" "200" "origin" "-160 976 -344" "classname" "light" } { "classname" "light" "origin" "592 456 -344" "light" "200" } { "classname" "light" "origin" "192 352 -232" "light" "200" } { "light" "200" "origin" "32 264 -232" "classname" "light" } { "classname" "light" "origin" "-184 432 -336" "light" "160" } { "light" "160" "origin" "-192 144 -336" "classname" "light" } { "classname" "light" "origin" "-776 280 -312" "light" "200" } { "light" "200" "origin" "-864 432 -312" "classname" "light" } { "classname" "light" "origin" "-1096 432 -312" "light" "200" } { "light" "200" "origin" "-1168 272 -312" "classname" "light" } { "classname" "light" "origin" "-944 64 -312" "light" "200" } { "light" "200" "origin" "-664 136 -312" "classname" "light" } { "light" "200" "origin" "-1112 592 -96" "classname" "light" } { "classname" "light" "origin" "-1040 1256 -32" "light" "150" } { "light" "200" "origin" "-880 1128 80" "classname" "light" } { "classname" "light" "origin" "-712 1400 -16" "light" "200" } { "classname" "light" "origin" "104 -600 -224" "light" "200" } { "light" "200" "origin" "280 -600 -224" "classname" "light" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*53" } { "light" "160" "origin" "-312 808 -72" "classname" "light" } { "light" "200" "origin" "192 1776 -312" "classname" "light" } { "classname" "light" "origin" "-392 568 -56" "light" "160" } { "classname" "light" "origin" "-1224 1504 8" "light" "170" } { "light" "170" "origin" "-1304 1392 8" "classname" "light" } { "classname" "light" "origin" "-640 1168 -8" "light" "170" } { "light" "160" "origin" "352 -1248 56" "classname" "light" } { "classname" "light" "origin" "40 -1248 56" "light" "160" } { "light" "160" "origin" "192 -1216 -176" "classname" "light" } { "classname" "light" "origin" "24 -1376 -232" "light" "160" } { "light" "160" "origin" "224 -1624 -232" "classname" "light" } { "classname" "light" "origin" "368 -1392 -232" "light" "160" } { "classname" "light" "origin" "8 -464 -40" "light" "140" } { "light" "140" "origin" "384 -464 -40" "classname" "light" } { "classname" "light" "origin" "-544 800 -56" "light" "140" } { "classname" "trigger_secret" "model" "*54" } { "classname" "trigger_secret" "model" "*55" } { "light" "200" "origin" "760 1856 -40" "classname" "light" } { "classname" "light" "origin" "760 1664 -40" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "538 1762 -124" "style" "1" "light" "200" } { "style" "1" "classname" "light_torch_small_walltorch" "origin" "850 1930 -124" "light" "200" } { "origin" "850 1618 -124" "classname" "light_torch_small_walltorch" "style" "1" "light" "200" } { "classname" "light" "origin" "912 1856 -40" "light" "200" } { "light" "200" "origin" "912 1664 -40" "classname" "light" } { "light" "200" "origin" "1064 1776 -172" "classname" "light" } { "light" "200" "origin" "1080 1856 -40" "classname" "light" } { "classname" "light" "origin" "1080 1664 -40" "light" "200" } { "classname" "light" "origin" "1176 1776 -172" "light" "200" } { "light" "170" "origin" "672 1768 -296" "classname" "light" } { "target" "t38" "classname" "trigger_teleport" "model" "*56" } { "targetname" "t38" "origin" "1144 1776 -88" "classname" "info_teleport_destination" } { "map" "e2m7" "classname" "trigger_changelevel" "model" "*57" } { "light" "160" "origin" "840 1768 -200" "classname" "light" } { "light" "140" "origin" "408 608 -344" "classname" "light" } { "classname" "item_spikes" "origin" "-16 240 -160" "spawnflags" "2048" } { "classname" "weapon_supernailgun" "origin" "-1256 1448 -80" } { "origin" "432 1160 152" "classname" "item_artifact_super_damage" } { "message" "The portal lies beyond..." "targetname" "t40" "wait" "-1" "speed" "20" "sounds" "4" "angle" "-2" "classname" "func_door" "model" "*58" } { "origin" "432 1672 -368" "classname" "item_armor2" } { "target" "t39" "sounds" "1" "wait" "-1" "classname" "func_button" "model" "*59" } { "message" "The underwater barrier is lowered..." "target" "t40" "targetname" "t39" "spawnflags" "1" "classname" "trigger_once" "model" "*60" } { "classname" "trigger_secret" "model" "*61" } { "light" "200" "origin" "-128 -704 -224" "classname" "light" } { "classname" "light" "origin" "512 -704 -224" "light" "200" } { "light" "200" "origin" "192 -832 -224" "classname" "light" } { "mangle" "20 240 0" "origin" "400 1048 240" "classname" "info_intermission" } { "mangle" "20 145 0" "origin" "-160 144 64" "classname" "info_intermission" } { "mangle" "-20 45 0" "origin" "-320 -824 -144" "classname" "info_intermission" } { "classname" "func_wall" "spawnflags" "1792" "model" "*62" } { "classname" "item_artifact_super_damage" "origin" "928 1768 -240" "spawnflags" "1792" } { "classname" "light" "origin" "8 1800 -120" "light" "220" } { "classname" "weapon_lightning" "origin" "1216 1784 -264" "spawnflags" "1792" } { "classname" "item_cells" "origin" "880 1648 -264" "spawnflags" "1793" } { "spawnflags" "1793" "origin" "880 1864 -264" "classname" "item_cells" } { "spawnflags" "1792" "classname" "func_wall" "model" "*63" } { "spawnflags" "1792" "classname" "func_wall" "model" "*64" } { "spawnflags" "1792" "classname" "func_wall" "model" "*65" } { "classname" "info_player_coop" "origin" "664 -1520 -312" "angle" "180" } { "angle" "180" "origin" "592 -1600 -312" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "680 -1712 -312" "angle" "180" } { "classname" "air_bubbles" "origin" "720 1384 -320" } { "classname" "light" "origin" "680 1376 -312" } { "classname" "func_door_secret" "angle" "180" "spawnflags" "2" "targetname" "t41" "model" "*66" "t_length" "65" // svdijk -- added to prevent z-fighting } { "classname" "trigger_multiple" "target" "t41" "model" "*67" } { "origin" "688 1176 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "480 1408 -312" } { "origin" "448 1552 -312" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 -312" } { "origin" "840 1080 -160" "classname" "light" "light" "200" } { "light" "200" "classname" "light" "origin" "840 1080 0" } { "classname" "light" "origin" "840 1080 232" "light" "200" } { "classname" "item_artifact_envirosuit" "origin" "576 1440 -344" } { "classname" "item_artifact_invulnerability" "origin" "544 1248 -344" } { "light" "200" "origin" "840 936 232" "classname" "light" } { "classname" "light" "origin" "840 760 232" "light" "150" } { "light" "120" "origin" "808 600 232" "classname" "light" } { "classname" "item_health" "origin" "824 960 152" } { "origin" "848 880 152" "classname" "item_health" } { "classname" "item_health" "origin" "808 768 152" } { "classname" "trigger_multiple" "message" "Welcome to the Well of Wishes!" "wait" "5" "sounds" "1" "model" "*68" } { "classname" "trigger_multiple" "sounds" "1" "wait" "3" "message" "The Dopefish Lives!" "model" "*69" } { "classname" "func_wall" "spawnflags" "1792" "model" "*70" } { "classname" "trigger_secret" "model" "*71" } { "spawnflags" "1" "origin" "-1312 1392 -80" "classname" "item_spikes" } { "classname" "func_wall" "spawnflags" "1792" "model" "*72" } { "classname" "light" "origin" "-544 600 -248" "light" "200" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "194 -214 196" "spawnflags" "2048" } { "light" "200" "origin" "352 984 -328" "classname" "light" } { "classname" "light" "origin" "96 976 -328" "light" "200" } { "classname" "light" "origin" "640 776 -336" "light" "200" } { "classname" "func_plat" "spawnflags" "1" "model" "*73" } { "classname" "monster_fish" "origin" "656 352 -336" "spawnflags" "256" } { "spawnflags" "256" "origin" "432 424 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "296 968 -336" "spawnflags" "256" } { "origin" "-48 800 -336" "classname" "monster_fish" } { "classname" "monster_fish" "origin" "-896 248 -312" } { "origin" "-744 328 -312" "classname" "monster_fish" } { "classname" "path_corner" "origin" "272 -1504 -264" "targetname" "t42" "target" "t43" } { "origin" "56 -1352 -264" "classname" "path_corner" "target" "t42" "targetname" "t43" } { "classname" "item_health" "origin" "312 -1336 -272" } { "origin" "544 -1488 -336" "classname" "item_health" } { "classname" "item_health" "origin" "312 1464 -160" } { "origin" "56 1488 -192" "classname" "item_health" } { "classname" "trigger_once" "killtarget" "t44" "spawnflags" "3072" "model" "*74" } { "classname" "item_rockets" "origin" "216 648 120" "spawnflags" "1" } { "classname" "item_shells" "origin" "136 648 120" "spawnflags" "1" } ���{ "wad" "gfx/tim.wad" "classname" "worldspawn" "sounds" "7" "worldtype" "0" "message" "the Underearth" } { "angle" "90" "origin" "1136 -1100 -72" "classname" "info_player_start" } { "origin" "1184 -776 -152" "classname" "light" "light" "150" } { "classname" "light" "origin" "1704 -584 -184" "light" "150" } { "classname" "light" "origin" "1640 -688 -184" "light" "150" } { "classname" "light" "origin" "1696 -888 -192" "light" "150" } { "classname" "light" "origin" "1088 -960 -152" "light" "150" } { "classname" "light" "origin" "1248 -960 -152" "light" "150" } { "classname" "light" "origin" "1016 -768 -152" "light" "100" } { "classname" "light" "origin" "896 -920 -152" "light" "150" } { "light" "100" "origin" "1584 -208 -112" "classname" "light" } { "light" "100" "origin" "1776 -208 -112" "classname" "light" } { "light" "150" "origin" "1584 -88 -112" "classname" "light" } { "origin" "1774 58 -76" "classname" "light_torch_small_walltorch" } { "light" "100" "origin" "1584 -488 -232" "classname" "light" } { "light" "100" "origin" "1768 -480 -232" "classname" "light" } { "light" "150" "origin" "1752 -112 -176" "classname" "light" } { "light" "150" "origin" "1592 -120 -176" "classname" "light" } { "light" "150" "origin" "1592 -248 -192" "classname" "light" } { "light" "200" "origin" "1768 -240 -192" "classname" "light" } { "light" "150" "origin" "1676 -220 -188" "classname" "light" } { "light" "150" "origin" "1672 -40 -136" "classname" "light" } { "light" "150" "origin" "1676 -376 -252" "classname" "light" } { "light" "250" "origin" "1112 952 -92" "classname" "light" } { "light" "200" "origin" "1280 928 -152" "classname" "light" } { "classname" "light" "origin" "704 952 -92" "light" "250" } { "classname" "light" "origin" "824 1112 -92" "light" "200" } { "classname" "light" "origin" "952 760 -92" "light" "200" } { "classname" "light" "origin" "824 760 -92" "light" "200" } { "light" "250" "origin" "952 1112 -92" "classname" "light" } { "classname" "light" "origin" "1128 -848 288" "light" "500" } { "classname" "light" "origin" "1144 -432 288" } { "classname" "light" "origin" "864 -552 272" "light" "200" } { "classname" "light" "origin" "1392 -568 168" "light" "200" } { "classname" "light" "origin" "1416 -592 -24" "light" "150" } { "classname" "light" "origin" "888 -584 -24" "light" "150" } { "classname" "light_torch_small_walltorch" "origin" "1058 -466 -24" "light" "225" } { "origin" "1214 -466 -24" "classname" "light_torch_small_walltorch" "light" "225" } { "classname" "light_torch_small_walltorch" "origin" "1198 -66 40" "light" "300" } { "classname" "light" "origin" "1144 -204 172" "light" "150" } { "classname" "light" "origin" "1144 -292 -32" "light" "150" } { "classname" "item_spikes" "origin" "880 -592 -96" "spawnflags" "1" } { "classname" "item_health" "origin" "1068 -944 -96" } { "classname" "light" "origin" "1128 -1084 96" "light" "300" } { "classname" "light" "origin" "888 -848 248" "light" "150" } { "light" "150" "origin" "1504 -896 248" "classname" "light" } { "classname" "light" "origin" "1304 -1048 24" "light" "225" } { "classname" "light" "origin" "1520 -872 -192" "light" "150" } { "light" "100" "origin" "1384 -776 -184" "classname" "light" } { "classname" "light" "origin" "1368 -912 -184" "light" "100" } { "classname" "light" "origin" "1584 496 148" "light" "200" } { "light" "200" "origin" "1488 496 148" "classname" "light" } { "classname" "light" "origin" "1688 164 148" "light" "200" } { "light" "200" "origin" "1352 496 148" "classname" "light" } { "classname" "light" "origin" "1608 496 -36" "light" "200" } { "light" "200" "origin" "1456 496 -36" "classname" "light" } { "classname" "light" "origin" "1692 600 -12" "light" "200" } { "light" "200" "origin" "1274 618 -64" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1274 378 -64" "light" "200" } { "classname" "light" "origin" "1256 496 -40" "light" "200" } { "spawnflags" "2056" "wait" "-1" "classname" "func_door" "angle" "270" "model" "*1" } { "wait" "-1" "spawnflags" "2056" "sounds" "3" "angle" "90" "classname" "func_door" "model" "*2" } { "light" "200" "origin" "1968 1792 -257" "classname" "light" } { "light" "250" "origin" "1880 1792 -105" "classname" "light" } { "classname" "light" "origin" "1976 1944 56" "light" "150" } { "light" "150" "origin" "1992 1624 64" "classname" "light" } { "classname" "light" "origin" "1944 1736 64" "light" "150" } { "light" "150" "origin" "1944 1832 64" "classname" "light" } { "classname" "light" "origin" "1128 496 -24" "light" "250" } { "light" "150" "origin" "1120 632 -24" "classname" "light" } { "light" "200" "origin" "928 544 -24" "classname" "light" } { "classname" "light" "origin" "640 664 -8" "light" "200" } { "light" "200" "origin" "848 672 -8" "classname" "light" } { "classname" "light" "origin" "1024 544 -188" "light" "175" } { "light" "250" "origin" "64 192 136" "classname" "light" } { "classname" "light" "origin" "528 184 136" "light" "250" } { "light" "200" "origin" "72 408 8" "classname" "light" } { "classname" "light" "origin" "80 -48 8" "light" "200" } { "light" "250" "origin" "400 384 80" "classname" "light" } { "classname" "light" "origin" "392 -16 80" "light" "250" } { "classname" "light" "origin" "312 184 -80" "light" "200" } { "light" "200" "origin" "440 184 -80" "classname" "light" } { "classname" "light" "origin" "504 368 -120" "light" "250" } { "light" "150" "origin" "632 192 -120" "classname" "light" } { "classname" "light" "origin" "504 -16 -120" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "774 446 -172" "light" "250" } { "origin" "774 -70 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "light" "250" "origin" "896 -128 152" "classname" "light" } { "classname" "light" "origin" "896 184 152" "light" "250" } { "light" "150" "origin" "656 184 216" "classname" "light" } { "classname" "light" "origin" "304 368 -152" "light" "200" } { "light" "200" "origin" "0 480 -168" "classname" "light" } { "classname" "light" "origin" "96 376 -168" "light" "200" } { "classname" "light" "origin" "16 1480 -96" "light" "200" } { "light" "200" "origin" "1280 1824 -120" "classname" "light" } { "light" "200" "origin" "504 1816 -120" "classname" "light" } { "classname" "light" "origin" "712 1808 -120" "light" "200" } { "light" "200" "origin" "1064 1808 -120" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "858 1950 -172" "light" "250" } { "origin" "658 1950 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light_torch_small_walltorch" "origin" "666 1682 -172" "light" "250" } { "origin" "858 1682 -172" "classname" "light_torch_small_walltorch" "light" "250" } { "classname" "light" "origin" "1248 1384 -32" "light" "200" } { "classname" "light" "origin" "1688 936 -136" "light" "200" } { "light" "250" "origin" "1856 1444 -52" "classname" "light" } { "light" "150" "origin" "1864 1316 -192" "classname" "light" } { "classname" "light" "origin" "1776 1212 -192" "light" "150" } { "light" "150" "origin" "1696 1076 -192" "classname" "light" } { "classname" "light_torch_small_walltorch" "origin" "1770 730 -64" "light" "250" } { "classname" "light" "origin" "1760 1720 -201" "light" "250" } { "classname" "light" "origin" "1632 1520 -201" "light" "200" } { "classname" "light" "origin" "1984 1504 -201" "light" "200" } { "light" "250" "classname" "light_torch_small_walltorch" "origin" "1874 2104 -300" } { "light" "250" "origin" "1712 2104 -300" "classname" "light_torch_small_walltorch" } { "light" "200" "classname" "light" "origin" "1792 2048 48" } { "classname" "light_flame_large_yellow" "origin" "1362 1778 0" } { "classname" "light" "origin" "1408 1776 -124" "light" "200" } { "classname" "light" "origin" "1520 1880 -84" "light" "175" } { "light" "175" "origin" "1416 1512 -84" "classname" "light" } { "classname" "light" "origin" "1376 1568 -164" "light" "150" } { "light" "150" "origin" "1416 1888 -164" "classname" "light" } { "classname" "light" "origin" "1544 2072 -164" "light" "150" } { "classname" "light" "origin" "1552 1968 36" "light" "250" } { "classname" "light" "origin" "1416 1968 -16" "light" "175" } { "light" "175" "origin" "1416 2176 -16" "classname" "light" } { "classname" "light" "origin" "1240 2176 -16" "light" "175" } { "light" "175" "origin" "1240 2000 -16" "classname" "light" } { "classname" "light" "origin" "1264 1576 -72" "light" "200" } { "light" "200" "origin" "992 1480 -40" "classname" "light" } { "light" "200" "classname" "light_torch_small_walltorch" "origin" "968 1632 -132" } { "light" "200" "origin" "968 1328 -132" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1474 2234 -133" "light" "200" } { "origin" "1182 2234 -133" "classname" "light_torch_small_walltorch" "light" "200" } { "classname" "light" "origin" "1936 1480 48" "light" "150" } { "light" "150" "origin" "1544 1528 64" "classname" "light" } { "classname" "light" "origin" "1472 1488 24" "light" "150" } { "classname" "light" "origin" "1400 1664 96" "light" "150" } { "light" "200" "origin" "1792 2176 -221" "classname" "light" } { "classname" "light" "origin" "1880 2288 -221" "light" "200" } { "light" "200" "origin" "2048 2288 -221" "classname" "light" } { "classname" "light" "origin" "2128 2208 -221" "light" "200" } { "light" "200" "origin" "2160 1992 -205" "classname" "light" } { "origin" "2288 1952 -29" "classname" "light" } { "light" "250" "origin" "2274 1738 -172" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2274 1682 -172" "light" "250" } { "light" "200" "origin" "2376 2184 -152" "classname" "light" } { "light" "200" "origin" "2618 1658 -169" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "2618 1368 -169" "light" "200" } { "light" "200" "origin" "2176 1488 -169" "classname" "light_torch_small_walltorch" } { "light" "200" "origin" "2298 626 24" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "1930 738 24" "light" "200" } { "light" "200" "origin" "2050 394 24" "classname" "light_torch_small_walltorch" } { "classname" "func_plat" "model" "*3" } { "light" "150" "origin" "2152 1784 -312" "classname" "light" } { "classname" "light" "origin" "2232 936 -4" "light" "200" } { "classname" "light" "origin" "2232 1040 48" } { "classname" "light_torch_small_walltorch" "origin" "2034 1034 -164" "light" "200" } { "classname" "light" "origin" "2304 1040 280" "light" "200" } { "light" "200" "origin" "2168 1040 280" "classname" "light" } { "origin" "2130 2452 -112" "classname" "light_flame_large_yellow" "light" "250" } { "classname" "light_flame_large_yellow" "origin" "1858 2452 -112" "light" "250" } { "classname" "light" "origin" "2132 2416 -188" "light" "150" } { "light" "150" "origin" "1860 2416 -188" "classname" "light" } { "light" "200" "origin" "2256 1968 -453" "classname" "light" } { "classname" "light" "origin" "2256 2184 -453" "light" "200" } { "light" "200" "origin" "2216 2384 -453" "classname" "light" } { "classname" "light" "origin" "1792 2400 -453" "light" "200" } { "light" "175" "origin" "1984 2400 -453" "classname" "light" } { "light" "150" "origin" "2168 1608 -9" "classname" "light" } { "classname" "light" "origin" "2368 1600 -9" "light" "150" } { "light" "150" "origin" "2240 1424 44" "classname" "light" } { "classname" "light" "origin" "2400 1424 44" "light" "150" } { "light" "150" "origin" "2560 1424 44" "classname" "light" } { "classname" "light" "origin" "2560 1560 44" "light" "175" } { "light" "150" "origin" "2232 1288 44" "classname" "light" } { "light" "175" "origin" "2384 1424 -160" "classname" "light" } { "classname" "light" "origin" "2232 1288 -160" "light" "175" } { "light" "150" "origin" "2164 932 -172" "classname" "light" } { "classname" "light" "origin" "2308 932 -172" "light" "150" } { "light" "150" "origin" "2232 776 24" "classname" "light" } { "classname" "light" "origin" "2192 664 24" "light" "150" } { "light" "150" "origin" "2016 696 24" "classname" "light" } { "classname" "light" "origin" "1912 496 24" "light" "150" } { "light" "175" "origin" "80 1616 -120" "classname" "light" } { "classname" "light" "origin" "72 1888 -120" "light" "175" } { "light" "175" "origin" "296 1616 -120" "classname" "light" } { "light" "175" "origin" "304 1888 -120" "classname" "light" } { "targetname" "t20" "angle" "90" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t21" "angle" "120" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t22" "angle" "150" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t19" "angle" "60" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t18" "angle" "30" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t17" "angle" "0" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t24" "angle" "210" "classname" "trap_spikeshooter" "origin" "192 1752 -208" "spawnflags" "1" } { "targetname" "t23" "angle" "180" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t25" "angle" "240" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t26" "angle" "270" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t27" "angle" "300" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "targetname" "t28" "angle" "330" "spawnflags" "1" "origin" "192 1752 -208" "classname" "trap_spikeshooter" } { "delay" ".1" "targetname" "t29" "target" "t17" "classname" "trigger_multiple" "model" "*4" } { "delay" ".1" "targetname" "t17" "target" "t18" "classname" "trigger_multiple" "model" "*5" } { "delay" ".1" "targetname" "t18" "target" "t19" "classname" "trigger_multiple" "model" "*6" } { "delay" ".1" "targetname" "t19" "target" "t20" "classname" "trigger_multiple" "model" "*7" } { "delay" ".1" "targetname" "t20" "target" "t21" "classname" "trigger_multiple" "model" "*8" } { "delay" ".1" "targetname" "t21" "target" "t22" "classname" "trigger_multiple" "model" "*9" } { "delay" ".1" "targetname" "t22" "target" "t23" "classname" "trigger_multiple" "model" "*10" } { "delay" ".1" "targetname" "t23" "target" "t24" "classname" "trigger_multiple" "model" "*11" } { "delay" ".1" "targetname" "t24" "target" "t25" "classname" "trigger_multiple" "model" "*12" } { "delay" ".1" "targetname" "t25" "target" "t26" "classname" "trigger_multiple" "model" "*13" } { "delay" ".1" "targetname" "t26" "target" "t27" "classname" "trigger_multiple" "model" "*14" } { "delay" ".1" "targetname" "t27" "target" "t28" "classname" "trigger_multiple" "model" "*15" } { "target" "t29" "wait" "1.3" "classname" "trigger_multiple" "model" "*16" } { "origin" "192 1750 -188" "classname" "light_flame_large_yellow" } { "light" "125" "origin" "214 1752 -166" "classname" "light" } { "classname" "light" "origin" "192 1774 -166" "light" "125" } { "light" "125" "origin" "170 1750 -166" "classname" "light" } { "classname" "light" "origin" "194 1726 -166" "light" "125" } { "target" "t31" "wait" "-1" "angle" "0" "classname" "func_button" "model" "*17" } { "target" "t31" "angle" "180" "wait" "-1" "classname" "func_button" "model" "*18" } { "target" "t31" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*19" } { "target" "t31" "angle" "90" "wait" "-1" "classname" "func_button" "model" "*20" } { "wait" "-1" "targetname" "t30" "sounds" "4" "speed" "50" "angle" "-1" "classname" "func_door" "model" "*21" } { "count" "4" "targetname" "t31" "target" "t30" "classname" "trigger_counter" "model" "*22" } { "light" "150" "origin" "2424 1080 -176" "classname" "light" } { "classname" "light" "origin" "2424 992 -176" "light" "150" } { "targetname" "t40" "angle" "180" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "160" } { "targetname" "t40" "angle" "140" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "120" } { "targetname" "t40" "angle" "200" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t40" "classname" "trap_spikeshooter" "origin" "2434 1036 -192" "spawnflags" "1" "angle" "220" } { "targetname" "t40" "angle" "240" "spawnflags" "1" "origin" "2434 1036 -192" "classname" "trap_spikeshooter" } { "targetname" "t41" "target" "t40" "wait" ".5" "classname" "trigger_multiple" "model" "*23" } { "target" "t41" "classname" "trigger_multiple" "model" "*24" } { "target" "t41" "classname" "trigger_multiple" "model" "*25" } { "target" "t41" "classname" "trigger_multiple" "model" "*26" } { "target" "t41" "classname" "trigger_multiple" "model" "*27" } { "target" "t41" "classname" "trigger_multiple" "model" "*28" } { "target" "t41" "classname" "trigger_multiple" "model" "*29" } { "target" "t41" "classname" "trigger_multiple" "model" "*30" } { "light" "125" "origin" "2196 1200 -204" "classname" "light" } { "classname" "light" "origin" "2284 1200 -204" "light" "125" } { "light" "125" "origin" "2284 1112 -204" "classname" "light" } { "classname" "light" "origin" "2388 1104 -204" "light" "125" } { "light" "125" "origin" "2284 1000 -204" "classname" "light" } { "classname" "light" "origin" "2140 1008 -204" "light" "125" } { "light" "125" "origin" "2132 1096 -204" "classname" "light" } { "classname" "light" "origin" "2132 1160 -204" "light" "125" } { "light" "200" "origin" "528 1816 -392" "classname" "light" } { "classname" "light" "origin" "736 1808 -392" "light" "200" } { "light" "200" "origin" "1040 1808 -392" "classname" "light" } { "classname" "light" "origin" "744 1424 -392" "light" "200" } { "light" "200" "origin" "752 1288 -416" "classname" "light" } { "light" "200" "origin" "760 1064 -376" "classname" "light" } { "classname" "func_train" "spawnflags" "33" "targetname" "t42" "dmg" "1000" "sounds" "1" "target" "t124" "speed" "250" "model" "*31" } { "classname" "func_door" "angle" "-2" "wait" "-1" "targetname" "t43" "speed" "50" "sounds" "3" "model" "*32" } { "classname" "func_button" "angle" "-2" "wait" "-1" "target" "t42" "sounds" "1" "model" "*33" } { "classname" "trigger_once" "target" "t43" "targetname" "t42" "delay" "2" "model" "*34" } { "classname" "light" "origin" "1936 1784 -288" "light" "125" } { "classname" "monster_ogre" "origin" "1976 1784 -328" "angle" "180" } { "classname" "func_door" "angle" "90" "wait" "-1" "sounds" "1" "model" "*35" } { "classname" "func_door" "angle" "270" "targetname" "t44" "wait" "-1" "model" "*36" } { "classname" "light" "origin" "64 264 8" "light" "125" } { "light" "125" "origin" "64 120 8" "classname" "light" } { "classname" "light" "origin" "64 192 8" "light" "100" } { "classname" "trigger_once" "target" "t44" "model" "*37" } { "classname" "light" "origin" "512 184 -8" "light" "175" } { "targetname" "t119" "classname" "func_door" "angle" "90" "wait" "-1" "speed" "40" "model" "*38" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "270" "speed" "40" "sounds" "4" "model" "*39" } { "targetname" "t119" "classname" "func_door" "wait" "-1" "angle" "-1" "speed" "30" "message" "Go for a swim first..." "sounds" "3" "model" "*40" "origin" "-1 0 0" // svdijk -- added to prevent z-fighting } { "classname" "func_button" "angle" "0" "wait" "-1" "target" "t45" "model" "*41" } { "classname" "light" "origin" "36 184 84" "light" "75" } { "classname" "light" "origin" "752 184 -192" "light" "150" } { "classname" "light" "origin" "544 536 -152" "light" "125" } { "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "targetname" "t45" "model" "*42" } { "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "targetname" "t45" "model" "*43" } { "light" "125" "origin" "544 -152 -152" "classname" "light" } { "classname" "item_artifact_envirosuit" "origin" "1024 492 -232" } { "classname" "item_armor1" "origin" "2128 1752 -352" } { "classname" "light_flame_small_yellow" "origin" "1024 368 -4" "light" "250" } { "classname" "light" "origin" "1024 400 -64" "light" "150" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*44" } { "wait" "5" "sounds" "1" "classname" "func_door" "angle" "-2" "spawnflags" "1" "targetname" "t51" "model" "*45" } { "sounds" "3" "classname" "func_button" "angle" "270" "wait" "3" "target" "t51" "model" "*46" } { "classname" "light" "origin" "1224 712 -216" "light" "100" } { "classname" "light" "origin" "1232 768 -192" "light" "100" } { "light" "75" "origin" "1168 760 -232" "classname" "light" } { "light" "100" "origin" "1232 800 -208" "classname" "light" } { "sounds" "1" "origin" "1860 472 -8" "classname" "item_key2" "spawnflags" "2048" } { "classname" "light" "origin" "1112 1568 -40" "light" "200" } { "light" "150" "origin" "-96 1440 -160" "classname" "light" } { "classname" "light" "origin" "384 1440 -160" "light" "150" } { "light" "200" "origin" "248 1408 -96" "classname" "light" } { "light" "150" "origin" "968 1480 -128" "classname" "light" } { "light" "150" "origin" "1008 -592 -32" "classname" "light" } { "classname" "light" "origin" "1264 -592 -32" "light" "150" } { "light" "200" "origin" "880 -840 -56" "classname" "light" } { "classname" "light" "origin" "1376 -856 -56" "light" "200" } { "light" "175" "origin" "264 184 -8" "classname" "light" } { "light" "175" "origin" "1464 -864 -208" "classname" "light" } { "light" "175" "origin" "1816 160 -56" "classname" "light" } { "classname" "light" "origin" "1560 160 -56" "light" "175" } { "light" "175" "origin" "1544 1632 104" "classname" "light" } { "classname" "light" "origin" "1792 1624 128" "light" "175" } { "classname" "item_health" "origin" "1068 -980 -104" } { "spawnflags" "1" "classname" "item_shells" "origin" "1344 -1160 -96" } { "classname" "monster_ogre" "origin" "1008 -128 40" "angle" "315" "targetname" "t59" "spawnflags" "1" } { "classname" "trigger_once" "target" "t59" "model" "*47" } { "classname" "monster_ogre" "origin" "896 96 56" "angle" "270" "target" "t60" "spawnflags" "1" } { "classname" "path_corner" "origin" "896 208 40" "target" "t60" "targetname" "t61" } { "origin" "896 -16 40" "classname" "path_corner" "targetname" "t60" "target" "t61" } { "classname" "path_corner" "origin" "168 392 -56" "target" "t62" "targetname" "t63" } { "origin" "168 -16 -56" "classname" "path_corner" "targetname" "t62" "target" "t63" } { "classname" "path_corner" "origin" "88 344 -56" "targetname" "t64" "target" "t65" "spawnflags" "256" } { "origin" "88 72 -56" "classname" "path_corner" "target" "t64" "targetname" "t65" "spawnflags" "256" } { "classname" "monster_hell_knight" "origin" "88 224 -40" "angle" "90" "target" "t64" "spawnflags" "257" } { "classname" "monster_hell_knight" "origin" "168 232 -40" "angle" "270" "target" "t62" "spawnflags" "1" } { "classname" "monster_zombie" "origin" "544 -120 -208" "angle" "90" "targetname" "t45" } { "classname" "monster_zombie" "origin" "544 496 -208" "angle" "270" "targetname" "t45" } { "classname" "monster_wizard" "origin" "664 428 216" "angle" "225" "spawnflags" "1" } { "angle" "135" "origin" "664 -56 216" "classname" "monster_wizard" "spawnflags" "257" } { "classname" "light" "origin" "1736 88 -40" "light" "100" } { "classname" "monster_ogre" "origin" "904 -120 56" "angle" "0" "targetname" "t66" "spawnflags" "1281" } { "classname" "trigger_once" "target" "t66" "spawnflags" "256" "model" "*48" } { "classname" "item_health" "origin" "1168 -408 -80" "spawnflags" "1" } { "classname" "item_health" "origin" "1168 -104 -16" } { "classname" "item_spikes" "origin" "928 216 32" } { "classname" "item_rockets" "origin" "632 -48 32" } { "classname" "item_health" "origin" "32 392 -64" } { "light" "200" "origin" "1688 288 148" "classname" "light" } { "classname" "light" "origin" "1688 464 148" "light" "200" } { "classname" "light_torch_small_walltorch" "origin" "1814 622 -64" "light" "250" } { "classname" "light" "origin" "1768 344 -72" "light" "200" } { "classname" "trigger_monsterjump" "angle" "0" "model" "*49" } { "targetname" "t100" "classname" "monster_ogre" "origin" "1504 272 24" "angle" "0" "spawnflags" "256" } { "targetname" "t68" "target" "t67" "origin" "1352 576 -120" "classname" "path_corner" "spawnflags" "256" } { "target" "t68" "targetname" "t67" "classname" "path_corner" "origin" "1352 416 -120" "spawnflags" "256" } { "target" "t68" "angle" "90" "origin" "1360 480 -104" "classname" "monster_demon1" "spawnflags" "257" } { "targetname" "t70" "target" "t69" "origin" "1472 496 -120" "classname" "path_corner" } { "target" "t70" "targetname" "t69" "classname" "path_corner" "origin" "1688 496 -120" } { "target" "t69" "angle" "270" "origin" "1648 544 -104" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t71" "targetname" "t72" "origin" "1984 688 -24" "classname" "path_corner" "spawnflags" "256" } { "target" "t72" "targetname" "t71" "classname" "path_corner" "origin" "1984 448 -24" "spawnflags" "256" } { "target" "t71" "angle" "270" "origin" "1992 536 -8" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t74" "targetname" "t73" "origin" "2120 680 -24" "classname" "path_corner" } { "targetname" "t74" "target" "t73" "classname" "path_corner" "origin" "2240 680 -24" } { "target" "t74" "angle" "225" "origin" "2256 736 -8" "classname" "monster_ogre" "spawnflags" "1" } { "targetname" "t78" "angle" "90" "origin" "2232 1312 -8" "classname" "monster_ogre" "spawnflags" "256" } { "target" "t76" "angle" "90" "origin" "2232 1008 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "targetname" "t76" "target" "t75" "origin" "2120 968 -216" "classname" "path_corner" } { "target" "t76" "targetname" "t75" "classname" "path_corner" "origin" "2336 968 -216" } { "classname" "light" "origin" "2384 2400 -152" "light" "200" } { "targetname" "t77" "wait" "-1" "sounds" "1" "speed" "300" "angle" "-2" "classname" "func_door" "model" "*50" } { "targetname" "t77" "angle" "180" "origin" "2512 2312 -200" "classname" "monster_demon1" "spawnflags" "256" } { "targetname" "t77" "classname" "monster_demon1" "origin" "2512 2160 -200" "angle" "180" } { "classname" "item_health" "origin" "2504 2200 -224" } { "light" "150" "origin" "2536 2312 -128" "classname" "light" } { "classname" "light" "origin" "2536 2160 -128" "light" "150" } { "target" "t77" "classname" "trigger_once" "model" "*51" } { "spawnflags" "2" "origin" "2368 2336 -224" "classname" "item_health" } { "spawnflags" "1" "origin" "2504 2240 -224" "classname" "item_spikes" } { "origin" "2408 2072 -224" "classname" "item_shells" } { "target" "t78" "classname" "trigger_once" "model" "*52" } { "targetname" "t78" "angle" "90" "origin" "2240 1272 -200" "classname" "monster_demon1" } { "light" "150" "origin" "1120 1384 -40" "classname" "light" } { "light" "200" "origin" "1200 1248 -104" "classname" "light" } { "light" "250" "origin" "1288 1032 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "1256 1032 -64" "classname" "light" } { "classname" "light_flame_small_yellow" "origin" "568 1032 -4" "light" "250" } { "classname" "light" "origin" "600 1032 -64" "light" "150" } { "light" "250" "origin" "888 1272 -4" "classname" "light_flame_small_yellow" } { "light" "150" "origin" "888 1240 -64" "classname" "light" } { "light" "200" "origin" "608 1232 -108" "classname" "light" } { "target" "t79" "wait" "-1" "angle" "180" "classname" "func_button" "model" "*53" } { "targetname" "t79" "message" "This door is opened near by..." "sounds" "3" "speed" "35" "angle" "-1" "wait" "-1" "classname" "func_door" "model" "*54" } { "light" "100" "origin" "580 1208 -148" "classname" "light" } { "light" "200" "origin" "1224 736 -8" "classname" "light" } { "target" "t80" "classname" "trigger_once" "model" "*55" } { "targetname" "t80" "angle" "315" "origin" "640 1088 128" "classname" "monster_wizard" } { "targetname" "t80" "classname" "monster_wizard" "origin" "616 720 128" "angle" "0" } { "targetname" "t80" "angle" "180" "origin" "1280 680 128" "classname" "monster_wizard" "spawnflags" "256" } { "target" "t82" "origin" "1248 1080 24" "classname" "monster_wizard" "spawnflags" "1" } { "targetname" "t82" "target" "t81" "origin" "1168 1064 8" "classname" "path_corner" } { "target" "t82" "targetname" "t81" "classname" "path_corner" "origin" "720 1064 8" } { "targetname" "t80" "classname" "monster_wizard" "origin" "584 1112 128" "angle" "315" "spawnflags" "256" } { "origin" "1256 944 -184" "classname" "item_health" } { "spawnflags" "1" "origin" "1116 584 -256" "classname" "item_spikes" } { "targetname" "t79" "angle" "315" "origin" "1000 1464 -168" "classname" "monster_ogre" } { "target" "t85" "targetname" "t86" "origin" "1456 1824 -192" "classname" "path_corner" "spawnflags" "256" } { "target" "t86" "targetname" "t85" "classname" "path_corner" "origin" "1456 1560 -192" "spawnflags" "256" } { "target" "t85" "angle" "270" "origin" "1456 1664 -176" "classname" "monster_ogre" "spawnflags" "256" } { "targetname" "t91" "angle" "270" "origin" "1560 1944 -320" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1600 1912 -320" "angle" "270" } { "angle" "225" "origin" "1960 1984 -320" "classname" "monster_zombie" } { "targetname" "t91" "classname" "monster_zombie" "origin" "1904 1928 -320" "angle" "225" } { "targetname" "t91" "angle" "270" "origin" "1624 1768 -320" "classname" "monster_zombie" } { "target" "t87" "classname" "monster_zombie" "origin" "1696 1912 -320" "angle" "0" } { "target" "t89" "angle" "270" "origin" "1704 1800 -320" "classname" "monster_zombie" } { "target" "t87" "targetname" "t88" "origin" "1648 1912 -336" "classname" "path_corner" } { "target" "t88" "targetname" "t87" "classname" "path_corner" "origin" "1848 1904 -336" } { "targetname" "t90" "target" "t89" "origin" "1648 1824 -336" "classname" "path_corner" } { "target" "t90" "targetname" "t89" "classname" "path_corner" "origin" "1768 1752 -336" } { "target" "t91" "classname" "trigger_once" "model" "*56" } { "spawnflags" "1" "origin" "1496 1808 -200" "classname" "item_health" } { "spawnflags" "1" "origin" "1184 2176 -192" "classname" "item_health" } { "classname" "item_health" "origin" "1184 2128 -192" "spawnflags" "1" } { "spawnflags" "1" "origin" "1264 1680 -192" "classname" "item_health" } { "target" "t92" "targetname" "t93" "origin" "2376 2304 -216" "classname" "path_corner" } { "target" "t93" "targetname" "t92" "classname" "path_corner" "origin" "2376 1984 -216" } { "target" "t95" "targetname" "t94" "origin" "2144 1608 -216" "classname" "path_corner" } { "target" "t94" "targetname" "t95" "classname" "path_corner" "origin" "2376 1608 -216" } { "target" "t92" "angle" "270" "origin" "2376 2176 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "135" "origin" "2416 1760 -200" "classname" "monster_hell_knight" "spawnflags" "257" } { "target" "t94" "angle" "180" "origin" "2320 1608 -200" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "90" "origin" "2152 1840 -200" "classname" "monster_ogre" "spawnflags" "257" } { "target" "t97" "targetname" "t96" "origin" "2096 2376 -216" "classname" "path_corner" "spawnflags" "256" } { "target" "t96" "targetname" "t97" "classname" "path_corner" "origin" "2232 2208 -216" "spawnflags" "256" } { "target" "t96" "angle" "135" "origin" "2200 2264 -200" "classname" "monster_wizard" "spawnflags" "257" } { "origin" "2104 1544 -224" "classname" "item_shells" } { "origin" "2176 1240 -32" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2256 1240 -32" } { "origin" "1928 584 -32" "classname" "item_shells" } { "spawnflags" "1" "origin" "2040 1072 -32" "classname" "item_health" } { "classname" "item_health" "origin" "2040 1024 -32" "spawnflags" "1" } { "angle" "225" "origin" "2352 1160 -200" "classname" "monster_hell_knight" "spawnflags" "256" } { "angle" "180" "origin" "1864 1192 -216" "classname" "monster_zombie" } { "classname" "monster_zombie" "origin" "1776 1192 -216" "angle" "180" } { "target" "t99" "targetname" "t98" "origin" "1688 1192 -232" "classname" "path_corner" } { "target" "t98" "targetname" "t99" "classname" "path_corner" "origin" "1688 1048 -232" } { "target" "t98" "angle" "90" "origin" "1688 1112 -216" "classname" "monster_zombie" } { "origin" "536 416 32" "classname" "item_shells" } { "target" "t100" "classname" "trigger_once" "spawnflags" "256" "model" "*57" } { "origin" "1784 408 -128" "classname" "item_health" } { "classname" "item_health" "origin" "1784 448 -128" } { "origin" "1736 728 -128" "classname" "item_rockets" } { "target" "t101" "sounds" "1" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*58" } { "light" "250" "origin" "1418 234 52" "classname" "light_torch_small_walltorch" } { "targetname" "t101" "classname" "trigger_secret" "model" "*59" } { "targetname" "t102" "angle" "180" "classname" "trigger_monsterjump" "model" "*60" } { "target" "t102" "killtarget" "t102" "classname" "trigger_once" "model" "*61" } { "origin" "1568 1968 -352" "classname" "item_health" } { "classname" "item_health" "origin" "1608 1968 -352" } { "origin" "2112 1776 -352" "classname" "item_shells" "spawnflags" "2048" } { "origin" "1920 2232 -352" "classname" "item_spikes" } { "classname" "item_spikes" "origin" "2008 2232 -352" } { "classname" "item_shells" "origin" "2176 1440 -224" } { "target" "t66" "classname" "trigger_once" "model" "*62" } { "origin" "1744 0 -128" "classname" "item_spikes" "spawnflags" "1" } { "target" "t103" "targetname" "t104" "origin" "-56 1432 -224" "classname" "path_corner" "spawnflags" "256" } { "target" "t104" "targetname" "t103" "classname" "path_corner" "origin" "312 1432 -224" "spawnflags" "256" } { "target" "t103" "origin" "40 1440 -208" "classname" "monster_ogre" "spawnflags" "257" } { "angle" "45" "origin" "56 1616 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "target" "t105" "targetname" "t106" "origin" "520 1816 -232" "classname" "path_corner" } { "target" "t106" "targetname" "t105" "classname" "path_corner" "origin" "920 1816 -232" } { "target" "t105" "origin" "624 1800 -216" "classname" "monster_hell_knight" "spawnflags" "1" } { "angle" "0" "origin" "480 1824 64" "classname" "monster_wizard" "spawnflags" "257" } { "targetname" "t107" "classname" "monster_wizard" "origin" "656 1816 64" "angle" "0" "spawnflags" "1" } { "targetname" "t107" "angle" "0" "origin" "840 1816 64" "classname" "monster_wizard" "spawnflags" "257" } { "target" "t107" "classname" "trigger_once" "model" "*63" } { "origin" "1440 2200 -192" "classname" "item_shells" } { "classname" "item_shells" "origin" "1440 2160 -192" } { "spawnflags" "1" "origin" "1184 1976 -192" "classname" "item_spikes" } { "target" "t109" "targetname" "t108" "origin" "1240 2000 -184" "classname" "path_corner" } { "target" "t108" "targetname" "t109" "classname" "path_corner" "origin" "1240 1760 -184" } { "target" "t108" "angle" "90" "origin" "1240 1904 -168" "classname" "monster_ogre" "spawnflags" "1" } { "target" "t110" "classname" "trigger_once" "spawnflags" "256" "model" "*64" } { "targetname" "t110" "angle" "45" "origin" "1216 2088 -168" "classname" "monster_ogre" "spawnflags" "1281" } { "classname" "item_health" "origin" "1496 1768 -200" } { "target" "t111" "classname" "trigger_once" "model" "*65" } { "targetname" "t111" "angle" "315" "origin" "764 -60 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "45" "origin" "764 436 -208" "classname" "monster_zombie" } { "targetname" "t111" "angle" "225" "origin" "284 -56 -208" "classname" "monster_zombie" "spawnflags" "256" } { "targetname" "t119" "wait" "-1" "speed" "40" "angle" "90" "classname" "func_door" "model" "*66" } { "origin" "1280 592 -128" "classname" "item_shells" } { "classname" "item_shells" "origin" "576 416 32" } { "classname" "item_spikes" "origin" "904 504 -128" "spawnflags" "1" } { "origin" "904 464 -128" "classname" "item_spikes" "spawnflags" "1" } { "classname" "item_health" "origin" "1024 912 -152" "spawnflags" "1" } { "classname" "item_spikes" "origin" "720 848 -184" "spawnflags" "1" } { "classname" "path_corner" "origin" "1224 1192 -176" "targetname" "t112" "target" "t113" } { "origin" "1216 896 -176" "classname" "path_corner" "targetname" "t113" "target" "t112" } { "classname" "monster_ogre" "origin" "1224 992 -160" "angle" "90" "target" "t112" } { "classname" "item_shells" "origin" "968 1600 -184" } { "classname" "item_artifact_super_damage" "origin" "1444 308 -104" } { "classname" "light" "origin" "992 -128 72" "light" "100" } { "classname" "item_shells" "origin" "1672 1008 -240" } { "classname" "item_health" "origin" "2264 1320 -224" } { "classname" "monster_wizard" "origin" "2120 1664 -72" "angle" "315" "spawnflags" "1" } { "classname" "item_health" "origin" "2016 392 -32" } { "classname" "monster_hell_knight" "origin" "360 1744 -208" "angle" "90" "targetname" "t114" "spawnflags" "257" } { "classname" "trigger_once" "target" "t114" "spawnflags" "256" "model" "*67" } { "classname" "item_health" "origin" "352 1464 -232" } { "spawnflags" "1" "origin" "352 1392 -232" "classname" "item_health" } { "classname" "item_armorInv" "origin" "744 1424 -448" } { "classname" "item_health" "origin" "-56 320 -232" } { "origin" "-16 320 -232" "classname" "item_health" } { "targetname" "t117" "classname" "trigger_teleport" "target" "t115" "spawnflags" "2" "model" "*68" } { "delay" ".5" "targetname" "t117" "classname" "trigger_teleport" "target" "t116" "spawnflags" "2" "model" "*69" } { "classname" "monster_wizard" "origin" "2928 1816 -152" "angle" "180" "targetname" "t117" } { "angle" "180" "origin" "2928 1768 -152" "classname" "monster_wizard" "targetname" "t117" } { "classname" "info_teleport_destination" "origin" "1824 1920 -184" "angle" "225" "targetname" "t115" } { "classname" "info_teleport_destination" "origin" "1880 1544 -184" "angle" "180" "targetname" "t116" } { "classname" "trigger_once" "target" "t117" "model" "*70" } { "classname" "monster_zombie" "origin" "764 388 -208" "angle" "0" "targetname" "t111" } { "angle" "0" "origin" "764 -12 -208" "classname" "monster_zombie" "targetname" "t111" } { "classname" "monster_zombie" "origin" "408 -56 -208" "angle" "270" "targetname" "t111" } { "classname" "light" "origin" "1200 672 -240" "light" "125" } { "classname" "item_spikes" "origin" "72 392 -64" "spawnflags" "1" } { "classname" "item_spikes" "origin" "2368 920 -224" } { "origin" "2032 976 -224" "classname" "item_spikes" } { "classname" "light" "origin" "1416 2096 -112" "light" "150" } { "light" "150" "origin" "1240 2096 -112" "classname" "light" } { "classname" "item_spikes" "origin" "464 1824 -240" } { "origin" "504 1824 -240" "classname" "item_spikes" } { "classname" "item_shells" "origin" "-96 1472 -232" "spawnflags" "1" } { "classname" "item_health" "origin" "528 -172 -232" } { "classname" "item_health" "origin" "40 -64 -64" } { "light" "225" "classname" "light_torch_small_walltorch" "origin" "122 -86 -8" } { "origin" "134 462 -8" "classname" "light_torch_small_walltorch" "light" "225" } { "light" "250" "origin" "678 446 92" "classname" "light_torch_small_walltorch" } { "classname" "light_torch_small_walltorch" "origin" "678 -70 92" "light" "250" } { "light" "150" "origin" "120 -56 -16" "classname" "light" } { "classname" "light" "origin" "136 424 -16" "light" "150" } { "light" "150" "origin" "600 184 296" "classname" "light" } { "classname" "light" "origin" "152 184 296" "light" "150" } { "light" "150" "origin" "368 376 296" "classname" "light" } { "classname" "light" "origin" "368 0 296" "light" "150" } { "light" "150" "origin" "352 192 232" "classname" "light" } { "light" "200" "origin" "64 408 168" "classname" "light" } { "classname" "light" "origin" "56 -48 168" "light" "200" } { "light" "125" "origin" "1520 1880 24" "classname" "light" } { "origin" "272 272 -232" "classname" "item_rockets" } { "targetname" "t45" "classname" "func_door" "angle" "-2" "sounds" "1" "wait" "-1" "model" "*71" } { "classname" "light" "origin" "416 -152 -152" "light" "125" } { "targetname" "t45" "angle" "90" "origin" "416 -120 -208" "classname" "monster_zombie" "spawnflags" "256" } { "origin" "400 524 -232" "classname" "item_health" } { "light" "125" "origin" "416 536 -152" "classname" "light" } { "targetname" "t45" "wait" "-1" "sounds" "1" "angle" "-2" "classname" "func_door" "model" "*72" } { "targetname" "t45" "angle" "270" "origin" "416 496 -208" "classname" "monster_zombie" "spawnflags" "256" } { "target" "t120" "wait" "-1" "angle" "270" "classname" "func_button" "model" "*73" } { "target" "t120" "wait" "-1" "angle" "90" "classname" "func_button" "model" "*74" } { "targetname" "t120" "target" "t119" "classname" "trigger_counter" "model" "*75" } { "light" "125" "origin" "1792 840 -112" "classname" "light" } { "classname" "light" "origin" "1584 840 -112" "light" "125" } { "light" "125" "origin" "1792 2280 -480" "classname" "light" } { "classname" "light" "origin" "1792 2144 -480" "light" "125" } { "origin" "1776 2232 -508" "classname" "item_health" } { "classname" "item_health" "origin" "1776 2192 -508" } { "light" "150" "origin" "1016 1696 -336" "classname" "light" } { "light" "125" "origin" "1080 1696 -280" "classname" "light" } { "light" "125" "origin" "1152 1696 -200" "classname" "light" } { "light" "150" "origin" "1096 1696 -352" "classname" "light" } { "classname" "trigger_secret" "model" "*76" } { "target" "t116" "classname" "trigger_teleport" "model" "*77" } { "spawnflags" "768" "angle" "315" "origin" "1400 1856 -176" "classname" "monster_ogre" } { "target" "t121" "targetname" "t122" "spawnflags" "768" "origin" "1824 2280 -344" "classname" "path_corner" } { "target" "t122" "targetname" "t121" "spawnflags" "768" "classname" "path_corner" "origin" "2080 2280 -344" } { "target" "t121" "spawnflags" "769" "angle" "180" "origin" "2128 2272 -328" "classname" "monster_demon1" } { "spawnflags" "2816" "origin" "1824 2072 -352" "classname" "item_shells" } { "spawnflags" "769" "angle" "90" "origin" "1792 2176 -144" "classname" "monster_wizard" } { "spawnflags" "769" "angle" "180" "origin" "2600 1640 -200" "classname" "monster_hell_knight" } { "spawnflags" "2816" "origin" "2096 1136 -32" "classname" "item_shells" } { "classname" "monster_wizard" "origin" "1872 1432 -72" "angle" "90" "spawnflags" "769" } { "classname" "monster_hell_knight" "origin" "2400 1032 -8" "angle" "180" "spawnflags" "769" } { "classname" "monster_demon1" "origin" "1000 496 -104" "angle" "0" "spawnflags" "768" } { "classname" "monster_demon1" "origin" "1240 2088 -168" "angle" "45" "spawnflags" "769" "targetname" "t110" } { "classname" "monster_hell_knight" "origin" "360 1936 -208" "angle" "270" "spawnflags" "769" "targetname" "t114" } { "classname" "item_shells" "origin" "16 1768 -232" "spawnflags" "2560" } { "spawnflags" "2560" "origin" "344 1576 -232" "classname" "item_shells" } { "classname" "trigger_changelevel" "map" "e2m4" "model" "*78" } { "classname" "light" "origin" "1136 -1144 264" "light" "250" } { "light" "250" "origin" "1264 -552 44" "classname" "light_flame_small_yellow" } { "classname" "light_flame_small_yellow" "origin" "1008 -552 44" "light" "250" } { "classname" "monster_demon1" "origin" "888 -120 56" "angle" "0" "spawnflags" "769" "targetname" "t66" } { "classname" "monster_hell_knight" "origin" "616 184 56" "angle" "0" "spawnflags" "768" } { "classname" "item_spikes" "origin" "880 216 32" "spawnflags" "2816" } { "classname" "monster_hell_knight" "origin" "1944 728 -8" "angle" "0" "spawnflags" "769" } { "classname" "light" "origin" "-48 1184 -156" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 1024 -120" "classname" "light" } { "classname" "light" "origin" "48 800 -120" "light" "200" "style" "10" } { "style" "10" "light" "200" "origin" "0 600 -120" "classname" "light" } { "classname" "monster_hell_knight" "origin" "128 1088 -208" "angle" "225" "spawnflags" "1" } { "angle" "315" "origin" "-104 760 -208" "classname" "monster_hell_knight" "spawnflags" "1" } { "classname" "item_spikes" "origin" "-56 592 -232" } { "classname" "item_health" "origin" "-56 920 -232" } { "classname" "monster_demon1" "origin" "0 544 -208" "angle" "90" "target" "t123" "spawnflags" "769" } { "mangle" "20 315 0" "origin" "1568 2040 -88" "classname" "info_intermission" } { "classname" "item_shells" "origin" "1528 1968 -352" "spawnflags" "3584" } { "origin" "-88 1376 -232" "classname" "item_health" "spawnflags" "3585" } { "classname" "item_artifact_envirosuit" "origin" "1216 1696 -168" "spawnflags" "3584" } { "classname" "monster_demon1" "origin" "144 372 -208" "angle" "180" "spawnflags" "768" "targetname" "t123" } { "classname" "monster_demon1" "origin" "0 528 -208" "angle" "90" "spawnflags" "1025" } { "classname" "info_player_deathmatch" "origin" "1128 -840 -80" "angle" "90" } { "classname" "info_player_deathmatch" "origin" "656 184 -208" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "-24 1440 -208" "angle" "0" } { "classname" "info_player_deathmatch" "origin" "1240 1816 -168" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1032 1568 -168" "angle" "0" } { "classname" "func_wall" "spawnflags" "1792" "model" "*79" } { "classname" "weapon_rocketlauncher" "origin" "184 1440 -232" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1424 608 -104" "angle" "270" } { "classname" "info_player_deathmatch" "origin" "2272 680 -8" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "2240 1472 -200" "angle" "270" } { "classname" "weapon_supernailgun" "origin" "2236 1184 -32" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "2424 1824 -200" "angle" "180" } { "classname" "info_player_deathmatch" "origin" "1792 2072 -328" "angle" "270" } { "classname" "weapon_supershotgun" "origin" "1488 1584 -200" "spawnflags" "1792" } { "classname" "weapon_grenadelauncher" "origin" "88 184 -64" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "1224 840 -168" "angle" "90" } { "classname" "weapon_nailgun" "origin" "1024 872 -152" "spawnflags" "1792" } { "classname" "weapon_supershotgun" "origin" "136 1760 -232" "spawnflags" "1792" } { "classname" "weapon_nailgun" "origin" "1976 2288 -352" "spawnflags" "1792" } { "classname" "weapon_lightning" "origin" "2128 1792 -352" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1608 728 -128" "spawnflags" "1792" } { "spawnflags" "1792" "classname" "item_cells" "origin" "72 1032 -232" } { "classname" "item_cells" "origin" "1008 1328 -192" "spawnflags" "1792" } { "classname" "item_cells" "origin" "1304 -1160 -96" "spawnflags" "1792" } { "origin" "2272 1440 -224" "classname" "item_shells" "spawnflags" "2816" } { "origin" "224 1736 -232" "classname" "item_health" } { "classname" "info_intermission" "origin" "1328 -1168 192" "mangle" "20 120 0" } { "classname" "info_intermission" "origin" "1248 680 8" "mangle" "20 130 0" } { "classname" "info_intermission" "origin" "1280 1824 -104" "mangle" "10 180 0" } { "classname" "light" "origin" "-304 888 -80" "light" "150" } { "light" "150" "origin" "-304 712 -80" "classname" "light" } { "classname" "light" "origin" "-224 872 -8" "light" "125" } { "classname" "light" "origin" "-224 728 -8" "light" "125" } { "light" "150" "origin" "-178 706 -156" "classname" "light_torch_small_walltorch" } { "classname" "light" "origin" "-320 838 -138" "light" "100" } { "light" "100" "origin" "-320 774 -138" "classname" "light" } { "classname" "info_player_coop" "origin" "1192 -1088 -72" "angle" "90" } { "angle" "90" "origin" "1080 -1088 -72" "classname" "info_player_coop" } { "classname" "info_player_coop" "origin" "1008 -1112 -72" "angle" "90" } { "angle" "90" "origin" "1264 -1112 -72" "classname" "info_player_coop" } { "classname" "item_armor1" "origin" "784 1816 -232" } { "classname" "item_spikes" "origin" "-56 1472 -232" "spawnflags" "1" } { "classname" "item_rockets" "origin" "400 -172 -232" } { "classname" "func_wall" "spawnflags" "1792" "model" "*80" } { "spawnflags" "1792" "classname" "func_wall" "model" "*81" } { "classname" "path_corner" "origin" "1954 1770 -96" "targetname" "t124" "target" "t125" } { "classname" "path_corner" "origin" "1954 1770 -320" "targetname" "t125" "target" "t124" } { "classname" "weapon_grenadelauncher" "origin" "1688 720 -128" "spawnflags" "2048" } { "wait" "-1" "angle" "-2" "classname" "func_door" "targetname" "t126" "lip" "-8" "model" "*82" } { "classname" "func_door_secret" "angle" "90" "spawnflags" "2" "model" "*83" } { "classname" "light" "origin" "-224 832 -152" "light" "125" } { "classname" "trigger_counter" "target" "t126" "targetname" "t127" "spawnflags" "1" "count" "7" "model" "*84" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*85" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*86" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*87" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*88" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*89" } { "health" "1" "classname" "trigger_once" "target" "t127" "model" "*90" } { "classname" "trigger_once" "health" "1" "target" "t127" "model" "*91" } { "classname" "ambient_swamp1" "origin" "1338 -854 -104" } { "classname" "ambient_swamp2" "origin" "938 -854 -104" } { "classname" "ambient_drip" "origin" "1138 -854 -176" } { "classname" "ambient_drip" "origin" "1650 -862 -192" } { "classname" "ambient_drip" "origin" "1674 -438 -192" } { "classname" "ambient_drip" "origin" "1682 2 -48" } { "classname" "ambient_swamp1" "origin" "1674 1986 -280" } { "classname" "ambient_swamp2" "origin" "1826 2378 -280" } { "classname" "ambient_swamp1" "origin" "2258 2058 -280" } { "classname" "ambient_drip" "origin" "746 1370 -376" } { "classname" "ambient_drip" "origin" "762 906 -240" } { "origin" "1034 722 -240" "classname" "ambient_drip" } { "classname" "ambient_drip" "origin" "554 1818 -352" } { "origin" "1002 1810 -352" "classname" "ambient_drip" } { "speed" "35" "classname" "func_door" "wait" "-1" "angle" "-2" "sounds" "1" "targetname" "t101" "model" "*92" } { "classname" "light" "origin" "1440 296 -80" "light" "125" } { "origin" "1792 792 -240" "classname" "item_spikes" } ���// // load keybindings // // commands with a leading + will also be called for key up events with // the + changed to a - unbindall // // character controls // bind ALT +strafe bind , +moveleft bind a +moveleft bind . +moveright bind d +moveright bind DEL +lookdown bind PGDN +lookup bind END centerview bind e +moveup bind c +movedown bind SHIFT +speed bind CTRL +attack bind UPARROW +forward bind w +forward bind DOWNARROW +back bind s +back bind LEFTARROW +left bind RIGHTARROW +right bind SPACE +jump //bind ENTER +jump bind TAB +showscores bind 1 "impulse 1" bind 2 "impulse 2" bind 3 "impulse 3" bind 4 "impulse 4" bind 5 "impulse 5" bind 6 "impulse 6" bind 7 "impulse 7" bind 8 "impulse 8" bind 0 "impulse 0" bind / "impulse 10" // change weapon bind MWHEELDOWN "impulse 10" bind MWHEELUP "impulse 12" // zoom alias zoom_in "sensitivity 2;fov 90;wait;fov 70;wait;fov 50;wait;fov 30;wait;fov 10;wait;fov 5;bind F11 zoom_out" alias zoom_out "sensitivity 4;fov 5;wait;fov 10;wait;fov 30;wait;fov 50;wait;fov 70;wait;fov 90;bind F11 zoom_in; sensitivity 3" bind F11 zoom_in // Function keys bind F1 "help" bind F2 "menu_save" bind F3 "menu_load" bind F4 "menu_options" bind F5 "menu_multiplayer" bind F6 "echo Quicksaving...; wait; save quick" bind F9 "echo Quickloading...; wait; load quick" bind F10 "quit" bind F12 "screenshot" // mouse options bind \ +mlook // // client environment commands // bind PAUSE "pause" bind ESCAPE "togglemenu" bind ~ "toggleconsole" bind ` "toggleconsole" bind t "messagemode" bind + "sizeup" bind = "sizeup" bind - "sizedown" bind INS +klook // // mouse buttons // bind MOUSE1 +attack //bind MOUSE2 +forward bind MOUSE2 +jump //bind MOUSE3 +mlook // // game controller // bind LSHOULDER "impulse 12" bind RSHOULDER "impulse 10" bind LTRIGGER +jump bind RTRIGGER +attack // // default cvars // gamma 1.0 volume 0.7 sensitivity 3 //viewsize 100 viewsize 110 scr_conscale 1.6 scr_menuscale 1.6 scr_sbarscale 1.6 // default to mouse-look enabled +mlook ���gfx/conback.lmp����������������������������������������� �����maps/e1m1.ent���������������������������������������������Þf��maps/e1m2.ent�������������������������������������������ôf�G¡��maps/e1m4.ent�������������������������������������������<�ת��maps/e2m2.ent�������������������������������������������³�+j��maps/e2m3.ent�������������������������������������������@�=˜��maps/e2m7.ent�������������������������������������������€µ�Å��default.cfg���������������������������������������������{�ñ����������quakespasm-0.93.0/Quake/sv_user.c�������������������������������������������������������������������0000644�0000000�0000000�00000032513�12733153600�015357� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_user.c -- server code for moving users #include "quakedef.h" edict_t *sv_player; extern cvar_t sv_friction; cvar_t sv_edgefriction = {"edgefriction", "2", CVAR_NONE}; extern cvar_t sv_stopspeed; static vec3_t forward, right, up; // world float *angles; float *origin; float *velocity; qboolean onground; usercmd_t cmd; cvar_t sv_idealpitchscale = {"sv_idealpitchscale","0.8",CVAR_NONE}; cvar_t sv_altnoclip = {"sv_altnoclip","1",CVAR_ARCHIVE}; //johnfitz /* =============== SV_SetIdealPitch =============== */ #define MAX_FORWARD 6 void SV_SetIdealPitch (void) { float angleval, sinval, cosval; trace_t tr; vec3_t top, bottom; float z[MAX_FORWARD]; int i, j; int step, dir, steps; if (!((int)sv_player->v.flags & FL_ONGROUND)) return; angleval = sv_player->v.angles[YAW] * M_PI*2 / 360; sinval = sin(angleval); cosval = cos(angleval); for (i=0 ; i<MAX_FORWARD ; i++) { top[0] = sv_player->v.origin[0] + cosval*(i+3)*12; top[1] = sv_player->v.origin[1] + sinval*(i+3)*12; top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2]; bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2] - 160; tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player); if (tr.allsolid) return; // looking at a wall, leave ideal the way is was if (tr.fraction == 1) return; // near a dropoff z[i] = top[2] + tr.fraction*(bottom[2]-top[2]); } dir = 0; steps = 0; for (j=1 ; j<i ; j++) { step = z[j] - z[j-1]; if (step > -ON_EPSILON && step < ON_EPSILON) continue; if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) ) return; // mixed changes steps++; dir = step; } if (!dir) { sv_player->v.idealpitch = 0; return; } if (steps < 2) return; sv_player->v.idealpitch = -dir * sv_idealpitchscale.value; } /* ================== SV_UserFriction ================== */ void SV_UserFriction (void) { float *vel; float speed, newspeed, control; vec3_t start, stop; float friction; trace_t trace; vel = velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (!speed) return; // if the leading edge is over a dropoff, increase friction start[0] = stop[0] = origin[0] + vel[0]/speed*16; start[1] = stop[1] = origin[1] + vel[1]/speed*16; start[2] = origin[2] + sv_player->v.mins[2]; stop[2] = start[2] - 34; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player); if (trace.fraction == 1.0) friction = sv_friction.value*sv_edgefriction.value; else friction = sv_friction.value; // apply friction control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== SV_Accelerate ============== */ cvar_t sv_maxspeed = {"sv_maxspeed", "320", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_accelerate = {"sv_accelerate", "10", CVAR_NONE}; void SV_Accelerate (float wishspeed, const vec3_t wishdir) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct (velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = sv_accelerate.value*host_frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed*wishdir[i]; } void SV_AirAccelerate (float wishspeed, vec3_t wishveloc) { int i; float addspeed, wishspd, accelspeed, currentspeed; wishspd = VectorNormalize (wishveloc); if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (velocity, wishveloc); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; // accelspeed = sv_accelerate.value * host_frametime; accelspeed = sv_accelerate.value*wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed*wishveloc[i]; } void DropPunchAngle (void) { float len; len = VectorNormalize (sv_player->v.punchangle); len -= 10*host_frametime; if (len < 0) len = 0; VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle); } /* =================== SV_WaterMove =================== */ void SV_WaterMove (void) { int i; vec3_t wishvel; float speed, newspeed, wishspeed, addspeed, accelspeed; // // user intentions // AngleVectors (sv_player->v.v_angle, forward, right, up); for (i=0 ; i<3 ; i++) wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += cmd.upmove; wishspeed = VectorLength(wishvel); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } wishspeed *= 0.7; // // water friction // speed = VectorLength (velocity); if (speed) { newspeed = speed - host_frametime * speed * sv_friction.value; if (newspeed < 0) newspeed = 0; VectorScale (velocity, newspeed/speed, velocity); } else newspeed = 0; // // water acceleration // if (!wishspeed) return; addspeed = wishspeed - newspeed; if (addspeed <= 0) return; VectorNormalize (wishvel); accelspeed = sv_accelerate.value * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed * wishvel[i]; } void SV_WaterJump (void) { if (sv.time > sv_player->v.teleport_time || !sv_player->v.waterlevel) { sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP; sv_player->v.teleport_time = 0; } sv_player->v.velocity[0] = sv_player->v.movedir[0]; sv_player->v.velocity[1] = sv_player->v.movedir[1]; } /* =================== SV_NoclipMove -- johnfitz new, alternate noclip. old noclip is still handled in SV_AirMove =================== */ void SV_NoclipMove (void) { AngleVectors (sv_player->v.v_angle, forward, right, up); velocity[0] = forward[0]*cmd.forwardmove + right[0]*cmd.sidemove; velocity[1] = forward[1]*cmd.forwardmove + right[1]*cmd.sidemove; velocity[2] = forward[2]*cmd.forwardmove + right[2]*cmd.sidemove; velocity[2] += cmd.upmove*2; //doubled to match running speed if (VectorLength (velocity) > sv_maxspeed.value) { VectorNormalize (velocity); VectorScale (velocity, sv_maxspeed.value, velocity); } } /* =================== SV_AirMove =================== */ void SV_AirMove (void) { int i; vec3_t wishvel, wishdir; float wishspeed; float fmove, smove; AngleVectors (sv_player->v.angles, forward, right, up); fmove = cmd.forwardmove; smove = cmd.sidemove; // hack to not let you back into teleporter if (sv.time < sv_player->v.teleport_time && fmove < 0) fmove = 0; for (i=0 ; i<3 ; i++) wishvel[i] = forward[i]*fmove + right[i]*smove; if ( (int)sv_player->v.movetype != MOVETYPE_WALK) wishvel[2] = cmd.upmove; else wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } if ( sv_player->v.movetype == MOVETYPE_NOCLIP) { // noclip VectorCopy (wishvel, velocity); } else if ( onground ) { SV_UserFriction (); SV_Accelerate (wishspeed, wishdir); } else { // not on ground, so little effect on velocity SV_AirAccelerate (wishspeed, wishvel); } } /* =================== SV_ClientThink the move fields specify an intended velocity in pix/sec the angle fields specify an exact angular motion in degrees =================== */ void SV_ClientThink (void) { vec3_t v_angle; if (sv_player->v.movetype == MOVETYPE_NONE) return; onground = (int)sv_player->v.flags & FL_ONGROUND; origin = sv_player->v.origin; velocity = sv_player->v.velocity; DropPunchAngle (); // // if dead, behave differently // if (sv_player->v.health <= 0) return; // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client->cmd; angles = sv_player->v.angles; VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle); angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; if (!sv_player->v.fixangle) { angles[PITCH] = -v_angle[PITCH]/3; angles[YAW] = v_angle[YAW]; } if ( (int)sv_player->v.flags & FL_WATERJUMP ) { SV_WaterJump (); return; } // // walk // //johnfitz -- alternate noclip if (sv_player->v.movetype == MOVETYPE_NOCLIP && sv_altnoclip.value) SV_NoclipMove (); else if (sv_player->v.waterlevel >= 2 && sv_player->v.movetype != MOVETYPE_NOCLIP) SV_WaterMove (); else SV_AirMove (); //johnfitz } /* =================== SV_ReadClientMove =================== */ void SV_ReadClientMove (usercmd_t *move) { int i; vec3_t angle; int bits; // read ping time host_client->ping_times[host_client->num_pings%NUM_PING_TIMES] = sv.time - MSG_ReadFloat (); host_client->num_pings++; // read current angles for (i=0 ; i<3 ; i++) //johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE if (sv.protocol == PROTOCOL_NETQUAKE) angle[i] = MSG_ReadAngle (sv.protocolflags); else angle[i] = MSG_ReadAngle16 (sv.protocolflags); //johnfitz VectorCopy (angle, host_client->edict->v.v_angle); // read movement move->forwardmove = MSG_ReadShort (); move->sidemove = MSG_ReadShort (); move->upmove = MSG_ReadShort (); // read buttons bits = MSG_ReadByte (); host_client->edict->v.button0 = bits & 1; host_client->edict->v.button2 = (bits & 2)>>1; i = MSG_ReadByte (); if (i) host_client->edict->v.impulse = i; } /* =================== SV_ReadClientMessage Returns false if the client should be killed =================== */ qboolean SV_ReadClientMessage (void) { int ret; int ccmd; const char *s; do { nextmsg: ret = NET_GetMessage (host_client->netconnection); if (ret == -1) { Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n"); return false; } if (!ret) return true; MSG_BeginReading (); while (1) { if (!host_client->active) return false; // a command caused an error if (msg_badread) { Sys_Printf ("SV_ReadClientMessage: badread\n"); return false; } ccmd = MSG_ReadChar (); switch (ccmd) { case -1: goto nextmsg; // end of message default: Sys_Printf ("SV_ReadClientMessage: unknown command char\n"); return false; case clc_nop: // Sys_Printf ("clc_nop\n"); break; case clc_stringcmd: s = MSG_ReadString (); ret = 0; if (q_strncasecmp(s, "status", 6) == 0) ret = 1; else if (q_strncasecmp(s, "god", 3) == 0) ret = 1; else if (q_strncasecmp(s, "notarget", 8) == 0) ret = 1; else if (q_strncasecmp(s, "fly", 3) == 0) ret = 1; else if (q_strncasecmp(s, "name", 4) == 0) ret = 1; else if (q_strncasecmp(s, "noclip", 6) == 0) ret = 1; else if (q_strncasecmp(s, "setpos", 6) == 0) ret = 1; else if (q_strncasecmp(s, "say", 3) == 0) ret = 1; else if (q_strncasecmp(s, "say_team", 8) == 0) ret = 1; else if (q_strncasecmp(s, "tell", 4) == 0) ret = 1; else if (q_strncasecmp(s, "color", 5) == 0) ret = 1; else if (q_strncasecmp(s, "kill", 4) == 0) ret = 1; else if (q_strncasecmp(s, "pause", 5) == 0) ret = 1; else if (q_strncasecmp(s, "spawn", 5) == 0) ret = 1; else if (q_strncasecmp(s, "begin", 5) == 0) ret = 1; else if (q_strncasecmp(s, "prespawn", 8) == 0) ret = 1; else if (q_strncasecmp(s, "kick", 4) == 0) ret = 1; else if (q_strncasecmp(s, "ping", 4) == 0) ret = 1; else if (q_strncasecmp(s, "give", 4) == 0) ret = 1; else if (q_strncasecmp(s, "ban", 3) == 0) ret = 1; if (ret == 1) Cmd_ExecuteString (s, src_client); else Con_DPrintf("%s tried to %s\n", host_client->name, s); break; case clc_disconnect: // Sys_Printf ("SV_ReadClientMessage: client disconnected\n"); return false; case clc_move: SV_ReadClientMove (&host_client->cmd); break; } } } while (ret == 1); return true; } /* ================== SV_RunClients ================== */ void SV_RunClients (void) { int i; for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (!host_client->active) continue; sv_player = host_client->edict; if (!SV_ReadClientMessage ()) { SV_DropClient (false); // client misbehaved... continue; } if (!host_client->spawned) { // clear client movement until a new packet is received memset (&host_client->cmd, 0, sizeof(host_client->cmd)); continue; } // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) SV_ClientThink (); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/world.h���������������������������������������������������������������������0000644�0000000�0000000�00000005442�12407762022�015030� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_WORLD_H #define _QUAKE_WORLD_H typedef struct { vec3_t normal; float dist; } plane_t; typedef struct { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area qboolean inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position plane_t plane; // surface normal at impact edict_t *ent; // entity the surface is on } trace_t; #define MOVE_NORMAL 0 #define MOVE_NOMONSTERS 1 #define MOVE_MISSILE 2 void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities void SV_UnlinkEdict (edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself // flags ent->v.modified void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid // flags ent->v.modified // sets ent->v.absmin and ent->v.absmax // if touchtriggers, calls prog functions for the intersected triggers int SV_PointContents (vec3_t p); int SV_TruePointContents (vec3_t p); // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all // the non-true version remaps the water current contents to content_water edict_t *SV_TestEntityPosition (edict_t *ent); trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); // mins and maxs are reletive // if the entire move stays in a solid volume, trace.allsolid will be set // if the starting point is in a solid, it will be allowed to move out // to an open area // nomonsters is used for line of sight or edge testing, where mosnters // shouldn't be considered solid objects // passedict is explicitly excluded from clipping checks (normally NULL) qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace); #endif /* _QUAKE_WORLD_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/Makefile.w64����������������������������������������������������������������0000644�0000000�0000000�00000013700�13142575645�015615� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# GNU Makefile for compiling Win64 quakespasm.exe using MinGW-w64. # Usage: "make -f Makefile.w64" # To cross-compile on Linux hosts, see the build_cross_win32*.sh scripts. # "make DEBUG=1" to build a debug client. # "make SDL_CONFIG=/path/to/sdl-config" to override the locally included SDL versions. # "make WINSOCK2=0" to use the old WinSock 1.1 api (NOT RECOMMENDED) ### Enable/disable SDL2 USE_SDL2=0 ### Enable/disable codecs for streaming music support USE_CODEC_WAVE=1 USE_CODEC_FLAC=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 USE_CODEC_OPUS=1 # either mikmod or xmp USE_CODEC_MIKMOD=1 USE_CODEC_XMP=0 USE_CODEC_UMX=1 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # --------------------------- # Helper functions # --------------------------- check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) # --------------------------- DEBUG ?= 0 WINSOCK2?= 1 # --------------------------- # build variables # --------------------------- CC = gcc LINKER = $(CC) WINDRES = windres STRIP = strip CPUFLAGS= LDFLAGS = -m64 -mwindows DFLAGS ?= CFLAGS ?= -m64 -Wall -Wno-trigraphs CFLAGS += $(CPUFLAGS) ifneq ($(DEBUG),0) DFLAGS += -DDEBUG CFLAGS += -g do_strip= else DFLAGS += -DNDEBUG CFLAGS += -O2 CFLAGS += $(call check_gcc,-fweb,) CFLAGS += $(call check_gcc,-frename-registers,) cmd_strip=$(STRIP) $(1) define do_strip $(call cmd_strip,$(1)); endef endif ifeq ($(USE_SDL2),1) CFLAGS += -DUSE_SDL2 endif # default to our local SDL[2] for build ifeq ($(USE_SDL2),1) SDL_CONFIG ?=../Windows/SDL2/bin/sdl2-config --prefix=../Windows/SDL2 --lib-suffix=64 else SDL_CONFIG ?=../Windows/SDL/bin/sdl-config --prefix=../Windows/SDL --lib-suffix=64 endif SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags) SDL_LIBS := $(shell $(SDL_CONFIG) --libs) ifeq ($(WINSOCK2),1) DEFWINSOCK :=-D_USE_WINSOCK2 LIBWINSOCK := -lws2_32 else DEFWINSOCK := LIBWINSOCK := -lwsock32 endif CFLAGS += $(DEFWINSOCK) NET_LIBS := $(LIBWINSOCK) ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3.o lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123.o lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif CODECLIBS := ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_FLAC),1) CFLAGS+= -DUSE_CODEC_FLAC CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= -lFLAC endif ifeq ($(USE_CODEC_OPUS),1) CFLAGS+= -DUSE_CODEC_OPUS CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= -lopusfile -lopus -logg endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),1) CFLAGS+= -DUSE_CODEC_MP3 CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),1) CFLAGS+= -DUSE_CODEC_MIKMOD CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),1) CFLAGS+= -DUSE_CODEC_XMP CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 CODECLIBS+= -lxmp endif ifeq ($(USE_CODEC_UMX),1) CFLAGS+= -DUSE_CODEC_UMX endif CFLAGS+= $(CODEC_INC) COMMON_LIBS:= -lm -lopengl32 -lwinmm LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS) # --------------------------- # targets # --------------------------- .PHONY: clean debug release DEFAULT_TARGET := quakespasm.exe # --------------------------- # rules # --------------------------- %.o: %.c $(CC) $(DFLAGS) -c $(CFLAGS) $(SDL_CFLAGS) -o $@ $< %.res: ../Windows/%.rc $(WINDRES) -I../Windows --output-format=coff --target=pe-x86-64 -o $@ $< # ---------------------------------------------------------------------------- # objects # ---------------------------------------------------------------------------- MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj) \ snd_mikmod.o \ snd_xmp.o \ snd_umx.o COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o SYSOBJ_CDA := cd_sdl.o SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := pl_win.o sys_sdl_win.o SYSOBJ_MAIN:= main_sdl.o SYSOBJ_RES := QuakeSpasm.res GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_fog.o \ gl_rmisc.o \ r_part.o \ r_world.o \ gl_screen.o \ gl_sky.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ image.o \ gl_texmgr.o \ gl_mesh.o \ r_sprite.o \ r_alias.o \ r_brush.o \ gl_model.o OBJS := strlcat.o \ strlcpy.o \ $(GLOBJS) \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_tent.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ common.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ world.o \ zone.o \ $(SYSOBJ_SYS) $(SYSOBJ_MAIN) $(SYSOBJ_RES) # ------------------------ # MinGW-w64 build rules # ------------------------ quakespasm.exe: $(OBJS) $(LINKER) $(OBJS) $(LDFLAGS) $(LIBS) $(SDL_LIBS) -o $@ $(call do_strip,$@) image.o: lodepng.c lodepng.h stb_image_write.h release: quakespasm.exe debug: $(error Use "make DEBUG=1") clean: rm -f $(shell find . \( -name '*~' -o -name '#*#' -o -name '*.o' -o -name '*.res' -o -name $(DEFAULT_TARGET) \) -print) ����������������������������������������������������������������quakespasm-0.93.0/Quake/glquake.h�������������������������������������������������������������������0000644�0000000�0000000�00000033460�13176243376�015345� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __GLQUAKE_H #define __GLQUAKE_H void GL_BeginRendering (int *x, int *y, int *width, int *height); void GL_EndRendering (void); void GL_Set2D (void); extern int glx, gly, glwidth, glheight; #define GL_UNUSED_TEXTURE (~(GLuint)0) // r_local.h -- private refresh defs #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) // normalizing factor so player model works out to about // 1 pixel per triangle #define MAX_LBM_HEIGHT 480 #define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf #define SKYSHIFT 7 #define SKYSIZE (1 << SKYSHIFT) #define SKYMASK (SKYSIZE - 1) #define BACKFACE_EPSILON 0.01 void R_TimeRefresh_f (void); void R_ReadPointFile_f (void); texture_t *R_TextureAnimation (texture_t *base, int frame); typedef struct surfcache_s { struct surfcache_s *next; struct surfcache_s **owner; // NULL is an empty chunk of memory int lightadj[MAXLIGHTMAPS]; // checked for strobe flush int dlight; int size; // including header unsigned width; unsigned height; // DEBUG only needed for debug float mipscale; struct texture_s *texture; // checked for animating textures byte data[4]; // width*height elements } surfcache_t; typedef struct { pixel_t *surfdat; // destination for generated surface int rowbytes; // destination logical width in bytes msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting texture_t *texture; // corrected for animating textures int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; typedef enum { pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 } ptype_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct particle_s { // driver-usable fields vec3_t org; float color; // drivers never touch the following fields struct particle_s *next; vec3_t vel; float ramp; float die; ptype_t type; } particle_t; //==================================================== extern qboolean r_cache_thrash; // compatability extern vec3_t modelorg, r_entorigin; extern entity_t *currententity; extern int r_visframecount; // ??? what difs? extern int r_framecount; extern mplane_t frustum[4]; // // view origin // extern vec3_t vup; extern vec3_t vpn; extern vec3_t vright; extern vec3_t r_origin; // // screen size info // extern refdef_t r_refdef; extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern int d_lightstylevalue[256]; // 8.8 fraction of base light value extern cvar_t r_norefresh; extern cvar_t r_drawentities; extern cvar_t r_drawworld; extern cvar_t r_drawviewmodel; extern cvar_t r_speeds; extern cvar_t r_pos; extern cvar_t r_waterwarp; extern cvar_t r_fullbright; extern cvar_t r_lightmap; extern cvar_t r_shadows; extern cvar_t r_wateralpha; extern cvar_t r_lavaalpha; extern cvar_t r_telealpha; extern cvar_t r_slimealpha; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_scale; extern cvar_t gl_clear; extern cvar_t gl_cull; extern cvar_t gl_smoothmodels; extern cvar_t gl_affinemodels; extern cvar_t gl_polyblend; extern cvar_t gl_flashblend; extern cvar_t gl_nocolors; extern cvar_t gl_playermip; extern cvar_t gl_subdivide_size; extern float load_subdivide_size; //johnfitz -- remember what subdivide_size value was when this map was loaded extern int gl_stencilbits; // Multitexture extern qboolean mtexenabled; extern qboolean gl_mtexable; extern PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc; extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc; extern PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc; extern GLint gl_max_texture_units; //ericw //johnfitz -- anisotropic filtering #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF extern float gl_max_anisotropy; extern qboolean gl_anisotropy_able; //ericw -- VBO extern PFNGLBINDBUFFERARBPROC GL_BindBufferFunc; extern PFNGLBUFFERDATAARBPROC GL_BufferDataFunc; extern PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc; extern PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc; extern PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc; extern qboolean gl_vbo_able; //ericw //ericw -- GLSL // SDL 1.2 has a bug where it doesn't provide these typedefs on OS X! typedef GLuint (APIENTRYP QS_PFNGLCREATESHADERPROC) (GLenum type); typedef void (APIENTRYP QS_PFNGLDELETESHADERPROC) (GLuint shader); typedef void (APIENTRYP QS_PFNGLDELETEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP QS_PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); typedef void (APIENTRYP QS_PFNGLCOMPILESHADERPROC) (GLuint shader); typedef void (APIENTRYP QS_PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); typedef void (APIENTRYP QS_PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (APIENTRYP QS_PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); typedef void (APIENTRYP QS_PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef GLuint (APIENTRYP QS_PFNGLCREATEPROGRAMPROC) (void); typedef void (APIENTRYP QS_PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); typedef void (APIENTRYP QS_PFNGLLINKPROGRAMPROC) (GLuint program); typedef void (APIENTRYP QS_PFNGLBINDATTRIBLOCATIONFUNC) (GLuint program, GLuint index, const GLchar *name); typedef void (APIENTRYP QS_PFNGLUSEPROGRAMPROC) (GLuint program); typedef GLint (APIENTRYP QS_PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP QS_PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); typedef void (APIENTRYP QS_PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); typedef void (APIENTRYP QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); typedef GLint (APIENTRYP QS_PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP QS_PFNGLUNIFORM1IPROC) (GLint location, GLint v0); typedef void (APIENTRYP QS_PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); typedef void (APIENTRYP QS_PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (APIENTRYP QS_PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); extern QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc; extern QS_PFNGLDELETESHADERPROC GL_DeleteShaderFunc; extern QS_PFNGLDELETEPROGRAMPROC GL_DeleteProgramFunc; extern QS_PFNGLSHADERSOURCEPROC GL_ShaderSourceFunc; extern QS_PFNGLCOMPILESHADERPROC GL_CompileShaderFunc; extern QS_PFNGLGETSHADERIVPROC GL_GetShaderivFunc; extern QS_PFNGLGETSHADERINFOLOGPROC GL_GetShaderInfoLogFunc; extern QS_PFNGLGETPROGRAMIVPROC GL_GetProgramivFunc; extern QS_PFNGLGETPROGRAMINFOLOGPROC GL_GetProgramInfoLogFunc; extern QS_PFNGLCREATEPROGRAMPROC GL_CreateProgramFunc; extern QS_PFNGLATTACHSHADERPROC GL_AttachShaderFunc; extern QS_PFNGLLINKPROGRAMPROC GL_LinkProgramFunc; extern QS_PFNGLBINDATTRIBLOCATIONFUNC GL_BindAttribLocationFunc; extern QS_PFNGLUSEPROGRAMPROC GL_UseProgramFunc; extern QS_PFNGLGETATTRIBLOCATIONPROC GL_GetAttribLocationFunc; extern QS_PFNGLVERTEXATTRIBPOINTERPROC GL_VertexAttribPointerFunc; extern QS_PFNGLENABLEVERTEXATTRIBARRAYPROC GL_EnableVertexAttribArrayFunc; extern QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC GL_DisableVertexAttribArrayFunc; extern QS_PFNGLGETUNIFORMLOCATIONPROC GL_GetUniformLocationFunc; extern QS_PFNGLUNIFORM1IPROC GL_Uniform1iFunc; extern QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc; extern QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc; extern QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc; extern qboolean gl_glsl_able; extern qboolean gl_glsl_gamma_able; extern qboolean gl_glsl_alias_able; // ericw -- //ericw -- NPOT texture support extern qboolean gl_texture_NPOT; //johnfitz -- polygon offset #define OFFSET_BMODEL 1 #define OFFSET_NONE 0 #define OFFSET_DECAL -1 #define OFFSET_FOG -2 #define OFFSET_SHOWTRIS -3 void GL_PolygonOffset (int); //johnfitz -- GL_EXT_texture_env_combine //the values for GL_ARB_ are identical #define GL_COMBINE_EXT 0x8570 #define GL_COMBINE_RGB_EXT 0x8571 #define GL_COMBINE_ALPHA_EXT 0x8572 #define GL_RGB_SCALE_EXT 0x8573 #define GL_CONSTANT_EXT 0x8576 #define GL_PRIMARY_COLOR_EXT 0x8577 #define GL_PREVIOUS_EXT 0x8578 #define GL_SOURCE0_RGB_EXT 0x8580 #define GL_SOURCE1_RGB_EXT 0x8581 #define GL_SOURCE0_ALPHA_EXT 0x8588 #define GL_SOURCE1_ALPHA_EXT 0x8589 extern qboolean gl_texture_env_combine; extern qboolean gl_texture_env_add; // for GL_EXT_texture_env_add //johnfitz -- rendering statistics extern int rs_brushpolys, rs_aliaspolys, rs_skypolys, rs_particles, rs_fogpolys; extern int rs_dynamiclightmaps, rs_brushpasses, rs_aliaspasses, rs_skypasses; extern float rs_megatexels; //johnfitz -- track developer statistics that vary every frame extern cvar_t devstats; typedef struct { int packetsize; int edicts; int visedicts; int efrags; int tempents; int beams; int dlights; } devstats_t; extern devstats_t dev_stats, dev_peakstats; //ohnfitz -- reduce overflow warning spam typedef struct { double packetsize; double efrags; double beams; double varstring; } overflowtimes_t; extern overflowtimes_t dev_overflows; //this stores the last time overflow messages were displayed, not the last time overflows occured #define CONSOLE_RESPAM_TIME 3 // seconds between repeated warning messages //johnfitz -- moved here from r_brush.c extern int gl_lightmap_format, lightmap_bytes; #define MAX_LIGHTMAPS 512 //johnfitz -- was 64 extern gltexture_t *lightmap_textures[MAX_LIGHTMAPS]; //johnfitz -- changed to an array extern int gl_warpimagesize; //johnfitz -- for water warp extern qboolean r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz typedef struct glsl_attrib_binding_s { const char *name; GLuint attrib; } glsl_attrib_binding_t; extern float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; //ericw //johnfitz -- fog functions called from outside gl_fog.c void Fog_ParseServerMessage (void); float *Fog_GetColor (void); float Fog_GetDensity (void); void Fog_EnableGFog (void); void Fog_DisableGFog (void); void Fog_StartAdditive (void); void Fog_StopAdditive (void); void Fog_SetupFrame (void); void Fog_NewMap (void); void Fog_Init (void); void Fog_SetupState (void); void R_NewGame (void); void R_AnimateLight (void); void R_MarkSurfaces (void); void R_CullSurfaces (void); qboolean R_CullBox (vec3_t emins, vec3_t emaxs); void R_StoreEfrags (efrag_t **ppefrag); qboolean R_CullModelForEntity (entity_t *e); void R_RotateForEntity (vec3_t origin, vec3_t angles); void R_MarkLights (dlight_t *light, int num, mnode_t *node); void R_InitParticles (void); void R_DrawParticles (void); void CL_RunParticles (void); void R_ClearParticles (void); void R_TranslatePlayerSkin (int playernum); void R_TranslateNewPlayerSkin (int playernum); //johnfitz -- this handles cases when the actual texture changes void R_UpdateWarpTextures (void); void R_DrawWorld (void); void R_DrawAliasModel (entity_t *e); void R_DrawBrushModel (entity_t *e); void R_DrawSpriteModel (entity_t *e); void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain); void R_RenderDlights (void); void GL_BuildLightmaps (void); void GL_DeleteBModelVertexBuffer (void); void GL_BuildBModelVertexBuffer (void); void GLMesh_LoadVertexBuffers (void); void GLMesh_DeleteVertexBuffers (void); void R_RebuildAllLightmaps (void); int R_LightPoint (vec3_t p); void GL_SubdivideSurface (msurface_t *fa); void R_BuildLightMap (msurface_t *surf, byte *dest, int stride); void R_RenderDynamicLightmaps (msurface_t *fa); void R_UploadLightmaps (void); void R_DrawWorld_ShowTris (void); void R_DrawBrushModel_ShowTris (entity_t *e); void R_DrawAliasModel_ShowTris (entity_t *e); void R_DrawParticles_ShowTris (void); GLint GL_GetUniformLocation (GLuint *programPtr, const char *name); GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings); void R_DeleteShaders (void); void GLWorld_CreateShaders (void); void GLAlias_CreateShaders (void); void GL_DrawAliasShadow (entity_t *e); void DrawGLTriangleFan (glpoly_t *p); void DrawGLPoly (glpoly_t *p); void DrawWaterPoly (glpoly_t *p); void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr); void Sky_Init (void); void Sky_DrawSky (void); void Sky_NewMap (void); void Sky_LoadTexture (texture_t *mt); void Sky_LoadSkyBox (const char *name); void TexMgr_RecalcWarpImageSize (void); void R_ClearTextureChains (qmodel_t *mod, texchain_t chain); void R_ChainSurface (msurface_t *surf, texchain_t chain); void R_DrawTextureChains (qmodel_t *model, entity_t *ent, texchain_t chain); void R_DrawWorld_Water (void); void GL_BindBuffer (GLenum target, GLuint buffer); void GL_ClearBufferBindings (); void GLSLGamma_DeleteTexture (void); void GLSLGamma_GammaCorrect (void); void R_ScaleView_DeleteTexture (void); float GL_WaterAlphaForSurface (msurface_t *fa); #endif /* __GLQUAKE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/keys.c����������������������������������������������������������������������0000644�0000000�0000000�00000055346�12665410352�014661� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "arch_def.h" /* key up events are sent even if in console mode */ #define HISTORY_FILE_NAME "history.txt" #define CMDLINES 32 char key_lines[CMDLINES][MAXCMDLINE]; int key_linepos; int key_insert; //johnfitz -- insert key toggle (for editing) double key_blinktime; //johnfitz -- fudge cursor blinking to make it easier to spot in certain cases int edit_line = 0; int history_line = 0; keydest_t key_dest; char *keybindings[MAX_KEYS]; qboolean consolekeys[MAX_KEYS]; // if true, can't be rebound while in console qboolean menubound[MAX_KEYS]; // if true, can't be rebound while in menu qboolean keydown[MAX_KEYS]; typedef struct { const char *name; int keynum; } keyname_t; keyname_t keynames[] = { {"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"ALT", K_ALT}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, // {"KP_NUMLOCK", K_KP_NUMLOCK}, {"KP_SLASH", K_KP_SLASH}, {"KP_STAR", K_KP_STAR}, {"KP_MINUS", K_KP_MINUS}, {"KP_HOME", K_KP_HOME}, {"KP_UPARROW", K_KP_UPARROW}, {"KP_PGUP", K_KP_PGUP}, {"KP_PLUS", K_KP_PLUS}, {"KP_LEFTARROW", K_KP_LEFTARROW}, {"KP_5", K_KP_5}, {"KP_RIGHTARROW", K_KP_RIGHTARROW}, {"KP_END", K_KP_END}, {"KP_DOWNARROW", K_KP_DOWNARROW}, {"KP_PGDN", K_KP_PGDN}, {"KP_ENTER", K_KP_ENTER}, {"KP_INS", K_KP_INS}, {"KP_DEL", K_KP_DEL}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, {"COMMAND", K_COMMAND}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"AUX1", K_AUX1}, {"AUX2", K_AUX2}, {"AUX3", K_AUX3}, {"AUX4", K_AUX4}, {"AUX5", K_AUX5}, {"AUX6", K_AUX6}, {"AUX7", K_AUX7}, {"AUX8", K_AUX8}, {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"AUX17", K_AUX17}, {"AUX18", K_AUX18}, {"AUX19", K_AUX19}, {"AUX20", K_AUX20}, {"AUX21", K_AUX21}, {"AUX22", K_AUX22}, {"AUX23", K_AUX23}, {"AUX24", K_AUX24}, {"AUX25", K_AUX25}, {"AUX26", K_AUX26}, {"AUX27", K_AUX27}, {"AUX28", K_AUX28}, {"AUX29", K_AUX29}, {"AUX30", K_AUX30}, {"AUX31", K_AUX31}, {"AUX32", K_AUX32}, {"PAUSE", K_PAUSE}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands {"BACKQUOTE", '`'}, // because a raw backquote may toggle the console {"TILDE", '~'}, // because a raw tilde may toggle the console {"LTHUMB", K_LTHUMB}, {"RTHUMB", K_RTHUMB}, {"LSHOULDER", K_LSHOULDER}, {"RSHOULDER", K_RSHOULDER}, {"ABUTTON", K_ABUTTON}, {"BBUTTON", K_BBUTTON}, {"XBUTTON", K_XBUTTON}, {"YBUTTON", K_YBUTTON}, {"LTRIGGER", K_LTRIGGER}, {"RTRIGGER", K_RTRIGGER}, {NULL, 0} }; /* ============================================================================== LINE TYPING INTO THE CONSOLE ============================================================================== */ static void PasteToConsole (void) { char *cbd, *p, *workline; int mvlen, inslen; if (key_linepos == MAXCMDLINE - 1) return; if ((cbd = PL_GetClipboardData()) == NULL) return; p = cbd; while (*p) { if (*p == '\n' || *p == '\r' || *p == '\b') { *p = 0; break; } p++; } inslen = (int) (p - cbd); if (inslen + key_linepos > MAXCMDLINE - 1) inslen = MAXCMDLINE - 1 - key_linepos; if (inslen <= 0) goto done; workline = key_lines[edit_line]; workline += key_linepos; mvlen = (int) strlen(workline); if (mvlen + inslen + key_linepos > MAXCMDLINE - 1) { mvlen = MAXCMDLINE - 1 - key_linepos - inslen; if (mvlen < 0) mvlen = 0; } // insert the string if (mvlen != 0) memmove (workline + inslen, workline, mvlen); memcpy (workline, cbd, inslen); key_linepos += inslen; workline[mvlen + inslen] = '\0'; done: Z_Free(cbd); } /* ==================== Key_Console -- johnfitz -- heavy revision Interactive line editing and console scrollback ==================== */ extern char *con_text, key_tabpartial[MAXCMDLINE]; extern int con_current, con_linewidth, con_vislines; void Key_Console (int key) { static char current[MAXCMDLINE] = ""; int history_line_last; size_t len; char *workline = key_lines[edit_line]; switch (key) { case K_ENTER: case K_KP_ENTER: key_tabpartial[0] = 0; Cbuf_AddText (workline + 1); // skip the prompt Cbuf_AddText ("\n"); Con_Printf ("%s\n", workline); // If the last two lines are identical, skip storing this line in history // by not incrementing edit_line if (strcmp(workline, key_lines[(edit_line-1)&31])) edit_line = (edit_line + 1) & 31; history_line = edit_line; key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = 0; //johnfitz -- otherwise old history items show up in the new edit line key_linepos = 1; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command may take some time return; case K_TAB: Con_TabComplete (); return; case K_BACKSPACE: key_tabpartial[0] = 0; if (key_linepos > 1) { workline += key_linepos - 1; if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; key_linepos--; } return; case K_DEL: key_tabpartial[0] = 0; workline += key_linepos; if (*workline) { if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; } return; case K_HOME: if (keydown[K_CTRL]) { //skip initial empty lines int i, x; char *line; for (i = con_current - con_totallines + 1; i <= con_current; i++) { line = con_text + (i % con_totallines) * con_linewidth; for (x = 0; x < con_linewidth; x++) { if (line[x] != ' ') break; } if (x != con_linewidth) break; } con_backscroll = CLAMP(0, con_current-i%con_totallines-2, con_totallines-(glheight>>3)-1); } else key_linepos = 1; return; case K_END: if (keydown[K_CTRL]) con_backscroll = 0; else key_linepos = strlen(workline); return; case K_PGUP: case K_MWHEELUP: con_backscroll += keydown[K_CTRL] ? ((con_vislines>>3) - 4) : 2; if (con_backscroll > con_totallines - (vid.height>>3) - 1) con_backscroll = con_totallines - (vid.height>>3) - 1; return; case K_PGDN: case K_MWHEELDOWN: con_backscroll -= keydown[K_CTRL] ? ((con_vislines>>3) - 4) : 2; if (con_backscroll < 0) con_backscroll = 0; return; case K_LEFTARROW: if (key_linepos > 1) { key_linepos--; key_blinktime = realtime; } return; case K_RIGHTARROW: len = strlen(workline); if ((int)len == key_linepos) { len = strlen(key_lines[(edit_line + 31) & 31]); if ((int)len <= key_linepos) return; // no character to get workline += key_linepos; *workline = key_lines[(edit_line + 31) & 31][key_linepos]; workline[1] = 0; key_linepos++; } else { key_linepos++; key_blinktime = realtime; } return; case K_UPARROW: if (history_line == edit_line) Q_strcpy(current, workline); history_line_last = history_line; do { history_line = (history_line - 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { history_line = history_line_last; return; } key_tabpartial[0] = 0; Q_strcpy(workline, key_lines[history_line]); key_linepos = Q_strlen(workline); return; case K_DOWNARROW: if (history_line == edit_line) return; key_tabpartial[0] = 0; do { history_line = (history_line + 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) Q_strcpy(workline, current); else Q_strcpy(workline, key_lines[history_line]); key_linepos = Q_strlen(workline); return; case K_INS: if (keydown[K_SHIFT]) /* Shift-Ins paste */ PasteToConsole(); else key_insert ^= 1; return; case 'v': case 'V': #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) if (keydown[K_COMMAND]) { /* Cmd+v paste (Mac-only) */ PasteToConsole(); return; } #endif if (keydown[K_CTRL]) { /* Ctrl+v paste */ PasteToConsole(); return; } break; case 'c': case 'C': if (keydown[K_CTRL]) { /* Ctrl+C: abort the line -- S.A */ Con_Printf ("%s\n", workline); workline[0] = ']'; workline[1] = 0; key_linepos = 1; history_line= edit_line; return; } break; } } void Char_Console (int key) { size_t len; char *workline = key_lines[edit_line]; if (key_linepos < MAXCMDLINE-1) { qboolean endpos = !workline[key_linepos]; key_tabpartial[0] = 0; //johnfitz // if inserting, move the text to the right if (key_insert && !endpos) { workline[MAXCMDLINE - 2] = 0; workline += key_linepos; len = strlen(workline) + 1; memmove (workline + 1, workline, len); *workline = key; } else { workline += key_linepos; *workline = key; // null terminate if at the end if (endpos) workline[1] = 0; } key_linepos++; } } //============================================================================ qboolean chat_team = false; static char chat_buffer[MAXCMDLINE]; static int chat_bufferlen = 0; const char *Key_GetChatBuffer (void) { return chat_buffer; } int Key_GetChatMsgLen (void) { return chat_bufferlen; } void Key_EndChat (void) { key_dest = key_game; chat_bufferlen = 0; chat_buffer[0] = 0; } void Key_Message (int key) { switch (key) { case K_ENTER: case K_KP_ENTER: if (chat_team) Cbuf_AddText ("say_team \""); else Cbuf_AddText ("say \""); Cbuf_AddText(chat_buffer); Cbuf_AddText("\"\n"); Key_EndChat (); return; case K_ESCAPE: Key_EndChat (); return; case K_BACKSPACE: if (chat_bufferlen) chat_buffer[--chat_bufferlen] = 0; return; } } void Char_Message (int key) { if (chat_bufferlen == sizeof(chat_buffer) - 1) return; // all full chat_buffer[chat_bufferlen++] = key; chat_buffer[chat_bufferlen] = 0; } //============================================================================ /* =================== Key_StringToKeynum Returns a key number to be used to index keybindings[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. =================== */ int Key_StringToKeynum (const char *str) { keyname_t *kn; if (!str || !str[0]) return -1; if (!str[1]) return str[0]; for (kn=keynames ; kn->name ; kn++) { if (!q_strcasecmp(str,kn->name)) return kn->keynum; } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, or a K_* name) for the given keynum. FIXME: handle quote special (general escape sequence?) =================== */ const char *Key_KeynumToString (int keynum) { static char tinystr[2]; keyname_t *kn; if (keynum == -1) return "<KEY NOT FOUND>"; if (keynum > 32 && keynum < 127) { // printable ascii tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) return kn->name; } return "<UNKNOWN KEYNUM>"; } /* =================== Key_SetBinding =================== */ void Key_SetBinding (int keynum, const char *binding) { if (keynum == -1) return; // free old bindings if (keybindings[keynum]) { Z_Free (keybindings[keynum]); keybindings[keynum] = NULL; } // allocate memory for new binding if (binding) keybindings[keynum] = Z_Strdup(binding); } /* =================== Key_Unbind_f =================== */ void Key_Unbind_f (void) { int b; if (Cmd_Argc() != 2) { Con_Printf ("unbind <key> : remove commands from a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding (b, NULL); } void Key_Unbindall_f (void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (keybindings[i]) Key_SetBinding (i, NULL); } } /* ============ Key_Bindlist_f -- johnfitz ============ */ void Key_Bindlist_f (void) { int i, count; count = 0; for (i = 0; i < MAX_KEYS; i++) { if (keybindings[i] && *keybindings[i]) { Con_SafePrintf (" %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]); count++; } } Con_SafePrintf ("%i bindings\n", count); } /* =================== Key_Bind_f =================== */ void Key_Bind_f (void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c != 2 && c != 3) { Con_Printf ("bind <key> [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keybindings[b]) Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; } // copy the rest of the command line cmd[0] = 0; for (i = 2; i < c; i++) { q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd)); if (i != (c-1)) q_strlcat (cmd, " ", sizeof(cmd)); } Key_SetBinding (b, cmd); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings (FILE *f) { int i; // unbindall before loading stored bindings: if (cfg_unbindall.value) fprintf (f, "unbindall\n"); for (i = 0; i < MAX_KEYS; i++) { if (keybindings[i] && *keybindings[i]) fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } } void History_Init (void) { int i, c; FILE *hf; for (i = 0; i < CMDLINES; i++) { key_lines[i][0] = ']'; key_lines[i][1] = 0; } key_linepos = 1; hf = fopen(va("%s/%s", host_parms->userdir, HISTORY_FILE_NAME), "rt"); if (hf != NULL) { do { i = 1; do { c = fgetc(hf); key_lines[edit_line][i++] = c; } while (c != '\r' && c != '\n' && c != EOF && i < MAXCMDLINE); key_lines[edit_line][i - 1] = 0; edit_line = (edit_line + 1) & (CMDLINES - 1); /* for people using a windows-generated history file on unix: */ if (c == '\r' || c == '\n') { do c = fgetc(hf); while (c == '\r' || c == '\n'); if (c != EOF) ungetc(c, hf); else c = 0; /* loop once more, otherwise last line is lost */ } } while (c != EOF && edit_line < CMDLINES); fclose(hf); history_line = edit_line = (edit_line - 1) & (CMDLINES - 1); key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = 0; } } void History_Shutdown (void) { int i; FILE *hf; hf = fopen(va("%s/%s", host_parms->userdir, HISTORY_FILE_NAME), "wt"); if (hf != NULL) { i = edit_line; do { i = (i + 1) & (CMDLINES - 1); } while (i != edit_line && !key_lines[i][1]); while (i != edit_line && key_lines[i][1]) { fprintf(hf, "%s\n", key_lines[i] + 1); i = (i + 1) & (CMDLINES - 1); } fclose(hf); } } /* =================== Key_Init =================== */ void Key_Init (void) { int i; History_Init (); key_blinktime = realtime; //johnfitz // // initialize consolekeys[] // for (i = 32; i < 127; i++) // ascii characters consolekeys[i] = true; consolekeys['`'] = false; consolekeys['~'] = false; consolekeys[K_TAB] = true; consolekeys[K_ENTER] = true; consolekeys[K_ESCAPE] = true; consolekeys[K_BACKSPACE] = true; consolekeys[K_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_LEFTARROW] = true; consolekeys[K_RIGHTARROW] = true; consolekeys[K_CTRL] = true; consolekeys[K_SHIFT] = true; consolekeys[K_INS] = true; consolekeys[K_DEL] = true; consolekeys[K_PGDN] = true; consolekeys[K_PGUP] = true; consolekeys[K_HOME] = true; consolekeys[K_END] = true; consolekeys[K_KP_NUMLOCK] = true; consolekeys[K_KP_SLASH] = true; consolekeys[K_KP_STAR] = true; consolekeys[K_KP_MINUS] = true; consolekeys[K_KP_HOME] = true; consolekeys[K_KP_UPARROW] = true; consolekeys[K_KP_PGUP] = true; consolekeys[K_KP_PLUS] = true; consolekeys[K_KP_LEFTARROW] = true; consolekeys[K_KP_5] = true; consolekeys[K_KP_RIGHTARROW] = true; consolekeys[K_KP_END] = true; consolekeys[K_KP_DOWNARROW] = true; consolekeys[K_KP_PGDN] = true; consolekeys[K_KP_ENTER] = true; consolekeys[K_KP_INS] = true; consolekeys[K_KP_DEL] = true; #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) consolekeys[K_COMMAND] = true; #endif consolekeys[K_MWHEELUP] = true; consolekeys[K_MWHEELDOWN] = true; // // initialize menubound[] // menubound[K_ESCAPE] = true; for (i = 0; i < 12; i++) menubound[K_F1+i] = true; // // register our functions // Cmd_AddCommand ("bindlist",Key_Bindlist_f); //johnfitz Cmd_AddCommand ("bind",Key_Bind_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); } static struct { qboolean active; int lastkey; int lastchar; } key_inputgrab = { false, -1, -1 }; /* =================== Key_BeginInputGrab =================== */ void Key_BeginInputGrab (void) { Key_ClearStates (); key_inputgrab.active = true; key_inputgrab.lastkey = -1; key_inputgrab.lastchar = -1; IN_UpdateInputMode (); } /* =================== Key_EndInputGrab =================== */ void Key_EndInputGrab (void) { Key_ClearStates (); key_inputgrab.active = false; IN_UpdateInputMode (); } /* =================== Key_GetGrabbedInput =================== */ void Key_GetGrabbedInput (int *lastkey, int *lastchar) { if (lastkey) *lastkey = key_inputgrab.lastkey; if (lastchar) *lastchar = key_inputgrab.lastchar; } /* =================== Key_Event Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ void Key_Event (int key, qboolean down) { char *kb; char cmd[1024]; if (key < 0 || key >= MAX_KEYS) return; // handle fullscreen toggle if (down && (key == K_ENTER || key == K_KP_ENTER) && keydown[K_ALT]) { VID_Toggle(); return; } // handle autorepeats and stray key up events if (down) { if (keydown[key]) { if (key_dest == key_game && !con_forcedup) return; // ignore autorepeats in game mode } else if (key >= 200 && !keybindings[key]) Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key)); } else if (!keydown[key]) return; // ignore stray key up events keydown[key] = down; if (key_inputgrab.active) { if (down) key_inputgrab.lastkey = key; return; } // handle escape specialy, so the user can never unbind it if (key == K_ESCAPE) { if (!down) return; if (keydown[K_SHIFT]) { Con_ToggleConsole_f(); return; } switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_game: case key_console: M_ToggleMenu_f (); break; default: Sys_Error ("Bad key_dest"); } return; } // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the kenum as a parameter, so multiple // downs can be matched with ups if (!down) { kb = keybindings[key]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } return; } // during demo playback, most keys bring up the main menu if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game && key != K_TAB) { M_ToggleMenu_f (); return; } // if not a consolekey, send to the interpreter no matter what mode is if ((key_dest == key_menu && menubound[key]) || (key_dest == key_console && !consolekeys[key]) || (key_dest == key_game && (!con_forcedup || !consolekeys[key]))) { kb = keybindings[key]; if (kb) { if (kb[0] == '+') { // button commands add keynum as a parm sprintf (cmd, "%s %i\n", kb, key); Cbuf_AddText (cmd); } else { Cbuf_AddText (kb); Cbuf_AddText ("\n"); } } return; } if (!down) return; // other systems only care about key down events switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_game: case key_console: Key_Console (key); break; default: Sys_Error ("Bad key_dest"); } } /* =================== Char_Event Called by the backend when the user has input a character. =================== */ void Char_Event (int key) { if (key < 32 || key > 126) return; #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) if (keydown[K_COMMAND]) return; #endif if (keydown[K_CTRL]) return; if (key_inputgrab.active) { key_inputgrab.lastchar = key; return; } switch (key_dest) { case key_message: Char_Message (key); break; case key_menu: M_Charinput (key); break; case key_game: if (!con_forcedup) break; /* fallthrough */ case key_console: Char_Console (key); break; default: break; } } /* =================== Key_TextEntry =================== */ qboolean Key_TextEntry (void) { if (key_inputgrab.active) return true; switch (key_dest) { case key_message: return true; case key_menu: return M_TextEntry(); case key_game: if (!con_forcedup) return false; /* fallthrough */ case key_console: return true; default: return false; } } /* =================== Key_ClearStates =================== */ void Key_ClearStates (void) { int i; for (i = 0; i < MAX_KEYS; i++) { if (keydown[i]) Key_Event (i, false); } } /* =================== Key_UpdateForDest =================== */ void Key_UpdateForDest (void) { static qboolean forced = false; if (cls.state == ca_dedicated) return; switch (key_dest) { case key_console: if (forced && cls.state == ca_connected) { forced = false; IN_Activate(); key_dest = key_game; } break; case key_game: if (cls.state != ca_connected) { forced = true; IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_console; break; } /* fallthrough */ default: forced = false; break; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/view.h����������������������������������������������������������������������0000644�0000000�0000000�00000002226�12657474270�014664� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_VIEW_H #define _QUAKE_VIEW_H extern cvar_t vid_gamma; extern cvar_t vid_contrast; extern float v_blend[4]; void V_Init (void); void V_RenderView (void); void V_CalcBlend (void); void V_UpdateBlend (void); float V_CalcRoll (vec3_t angles, vec3_t velocity); //void V_UpdatePalette (void); //johnfitz #endif /* _QUAKE_VIEW_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/crc.c�����������������������������������������������������������������������0000644�0000000�0000000�00000007165�13070122360�014437� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* crc.c */ #include "quakedef.h" #include "crc.h" // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the // CCITT standard CRC used by XMODEM #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 static unsigned short crctable[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } void CRC_ProcessByte(unsigned short *crcvalue, byte data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } //johnfitz -- texture crc unsigned short CRC_Block (const byte *start, int count) { unsigned short crc; CRC_Init (&crc); while (count--) crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; return crc; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/build_cross_win32.sh��������������������������������������������������������0000755�0000000�0000000�00000000755�12475156650�017434� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Change this script to meet your needs and/or environment. #TARGET=i686-w64-mingw32 TARGET=i686-pc-mingw32 #PREFIX=/opt/cross_win32 PREFIX=/usr/local/cross-win32 PATH="$PREFIX/bin:$PATH" export PATH MAKE_CMD=make CC="$TARGET-gcc" AS="$TARGET-as" RANLIB="$TARGET-ranlib" AR="$TARGET-ar" WINDRES="$TARGET-windres" STRIP="$TARGET-strip" export PATH CC AS AR RANLIB WINDRES STRIP exec $MAKE_CMD CC=$CC AS=$AS RANLIB=$RANLIB AR=$AR WINDRES=$WINDRES STRIP=$STRIP -f Makefile.w32 $* �������������������quakespasm-0.93.0/Quake/q_sound.h�������������������������������������������������������������������0000644�0000000�0000000�00000012520�12407762022�015344� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sound.h -- client sound i/o functions #ifndef __QUAKE_SOUND__ #define __QUAKE_SOUND__ /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { int left; int right; } portable_samplepair_t; typedef struct sfx_s { char name[MAX_QPATH]; cache_user_t cache; } sfx_t; /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { int length; int loopstart; int speed; int width; int stereo; byte data[1]; /* variable sized */ } sfxcache_t; typedef struct { int channels; int samples; /* mono samples in buffer */ int submission_chunk; /* don't mix less than this # */ int samplepos; /* in mono samples */ int samplebits; int signed8; /* device opened for S8 format? (e.g. Amiga AHI) */ int speed; unsigned char *buffer; } dma_t; /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { sfx_t *sfx; /* sfx number */ int leftvol; /* 0-255 volume */ int rightvol; /* 0-255 volume */ int end; /* end time in global paintsamples */ int pos; /* sample position in sfx */ int looping; /* where to loop, -1 = no looping */ int entnum; /* to allow overriding a specific sound */ int entchannel; vec3_t origin; /* origin of sound effect */ vec_t dist_mult; /* distance multiplier (attenuation/clipK) */ int master_vol; /* 0-255 master volume */ } channel_t; #define WAV_FORMAT_PCM 1 typedef struct { int rate; int width; int channels; int loopstart; int samples; int dataofs; /* chunk starts this many bytes from file start */ } wavinfo_t; void S_Init (void); void S_Startup (void); void S_Shutdown (void); void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation); void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); void S_StopSound (int entnum, int entchannel); void S_StopAllSounds(qboolean clear); void S_ClearBuffer (void); void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up); void S_ExtraUpdate (void); void S_BlockSound (void); void S_UnblockSound (void); sfx_t *S_PrecacheSound (const char *sample); void S_TouchSound (const char *sample); void S_ClearPrecache (void); void S_BeginPrecaching (void); void S_EndPrecaching (void); void S_PaintChannels (int endtime); void S_InitPaintChannels (void); /* picks a channel based on priorities, empty slots, number of channels */ channel_t *SND_PickChannel (int entnum, int entchannel); /* spatializes a channel */ void SND_Spatialize (channel_t *ch); /* music stream support */ void S_RawSamples(int samples, int rate, int width, int channels, byte * data, float volume); /* Expects data in signed 16 bit, or unsigned 8 bit format. */ /* initializes cycling through a DMA buffer and returns information on it */ qboolean SNDDMA_Init(dma_t *dma); /* gets the current DMA position */ int SNDDMA_GetDMAPos(void); /* shutdown the DMA xfer. */ void SNDDMA_Shutdown(void); /* validates & locks the dma buffer */ void SNDDMA_LockBuffer(void); /* unlocks the dma buffer / sends sound to the device */ void SNDDMA_Submit(void); /* blocks sound output upon window focus loss */ void SNDDMA_BlockSound(void); /* unblocks the output upon window focus gain */ void SNDDMA_UnblockSound(void); /* ==================================================================== * User-setable variables * ==================================================================== */ #define MAX_CHANNELS 1024 // ericw -- was 512 /* johnfitz -- was 128 */ #define MAX_DYNAMIC_CHANNELS 128 /* johnfitz -- was 8 */ extern channel_t snd_channels[MAX_CHANNELS]; /* 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds * MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc * MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds */ extern volatile dma_t *shm; extern int total_channels; extern int soundtime; extern int paintedtime; extern int s_rawend; extern vec3_t listener_origin; extern vec3_t listener_forward; extern vec3_t listener_right; extern vec3_t listener_up; extern cvar_t sndspeed; extern cvar_t snd_mixspeed; extern cvar_t snd_filterquality; extern cvar_t sfxvolume; extern cvar_t loadas8bit; #define MAX_RAW_SAMPLES 8192 extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; extern cvar_t bgmvolume; void S_LocalSound (const char *name); sfxcache_t *S_LoadSound (sfx_t *s); wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength); void SND_InitScaletable (void); #endif /* __QUAKE_SOUND__ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/input.h���������������������������������������������������������������������0000644�0000000�0000000�00000003152�12665410352�015036� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_INPUT_H #define _QUAKE_INPUT_H // input.h -- external (non-keyboard) input devices void IN_Init (void); void IN_Shutdown (void); void IN_Commands (void); // oportunity for devices to stick commands on the script buffer // mouse moved by dx and dy pixels void IN_MouseMotion(int dx, int dy); void IN_SendKeyEvents (void); // used as a callback for Sys_SendKeyEvents() by some drivers void IN_UpdateInputMode (void); // do stuff if input mode (text/non-text) changes matter to the keyboard driver void IN_Move (usercmd_t *cmd); // add additional movement on top of the keyboard move cmd void IN_ClearStates (void); // restores all button and position states to defaults // called when the app becomes active void IN_Activate (); // called when the app becomes inactive void IN_Deactivate (qboolean free_cursor); #endif /* _QUAKE_INPUT_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/stb_image_write.h�����������������������������������������������������������0000644�0000000�0000000�00000073726�13142664700�017060� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* QuakeSpasm: kept only the jpg writer, the only thing we use, and removed all others. */ /* stb_image_write - v1.07 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can define STBIW_MEMMOVE() to replace memmove() USAGE: There are four functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); There are also four equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: PNG/BMP/TGA Sean Barrett HDR Baldur Karlsson TGA monochrome: Jean-Sebastien Guay misc enhancements: Tim Kelsey TGA RLE Alan Hickman initial file IO callback implementation Emmanuel Julien JPEG Jon Olick (original jo_jpeg.cpp code) Daniel Gibson bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #ifdef __cplusplus extern "C" { #endif #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #define STBIWDEF extern #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif typedef void stbi_write_func(void *context, void *data, int size); #if 0 /* not used in QuakeSpasm */ STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); #endif #ifdef __cplusplus } #endif #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include <stdio.h> #endif // STBI_WRITE_NO_STDIO #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <math.h> #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include <assert.h> #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) typedef struct { stbi_write_func *func; void *context; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<<bits[1])-1); } static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { unsigned short EOB[2]; unsigned short M16zeroes[2]; int dataOff, i, diff, end0pos; int DU[64]; EOB[0] = HTAC[0x00][0]; EOB[1] = HTAC[0x00][1]; M16zeroes[0] = HTAC[0xF0][0]; M16zeroes[1] = HTAC[0xF0][1]; // DCT rows for(dataOff=0; dataOff<64; dataOff+=8) { stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]); } // DCT columns for(dataOff=0; dataOff<8; ++dataOff) { stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]); } // Quantize/descale/zigzag the coefficients for(i=0; i<64; ++i) { float v = CDU[i]*fdtbl[i]; // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); } // Encode DC diff = DU[0] - DC; if (diff == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); } else { unsigned short bits[2]; stbiw__jpg_calcBits(diff, bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } // Encode ACs end0pos = 63; for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; unsigned char head1[] = { 0xFF,0xC0,0,0x11,8, /*[5]*/0,/*[6]*/0,/*[7]*/0,/*[8]*/0, 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; head1[5] = (unsigned char)(height>>8); head1[6] = STBIW_UCHAR(height); head1[7] = (unsigned char)(width>>8); head1[8] = STBIW_UCHAR(width); s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; const unsigned char *imageData = (const unsigned char *)data; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; int x, y, pos; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { for(col = x; col < x+8; ++col, ++pos) { int p = row*width*comp + col*comp; float r, g, b; if(row >= height) { p -= width*comp*(row+1 - height); } if(col >= width) { p -= comp*(col+1 - width); } r = imageData[p+0]; g = imageData[p+ofsG]; b = imageData[p+ofsB]; YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } #if 0 /* not used in QuakeSpasm */ STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ������������������������������������������quakespasm-0.93.0/Quake/cmd.h�����������������������������������������������������������������������0000644�0000000�0000000�00000010734�12407762022�014444� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_CMD_H #define _QUAKE_CMD_H // cmd.h -- Command buffer and command execution //=========================================================================== /* Any number of commands can be added in a frame, from several different sources. Most commands come from either keybindings or console line input, but remote servers can also send across commands and entire text files can be execed. The + command line options are also added to the command buffer. The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); */ void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed void Cbuf_AddText (const char *text); // as new commands are generated from the console or keybindings, // the text is added to the end of the command buffer. void Cbuf_InsertText (const char *text); // when a command wants to issue other commands immediately, the text is // inserted at the beginning of the buffer, before any remaining unexecuted // commands. void Cbuf_Execute (void); // Pulls off \n terminated lines of text from the command buffer and sends // them through Cmd_ExecuteString. Stops when the buffer is empty. // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function! //=========================================================================== /* Command execution takes a null terminated string, breaks it into tokens, then searches for a command or variable that matches the first token. Commands can come from three sources, but the handler functions may choose to dissallow the action or forward it to a remote server if the source is not apropriate. */ typedef void (*xcommand_t) (void); typedef enum { src_client, // came in over a net connection as a clc_stringcmd // host_client will be valid during this state. src_command // from the command buffer } cmd_source_t; extern cmd_source_t cmd_source; void Cmd_Init (void); void Cmd_AddCommand (const char *cmd_name, xcommand_t function); // called by the init functions of other parts of the program to // register commands and functions to call for them. // The cmd_name is referenced later, so it should not be in temp memory qboolean Cmd_Exists (const char *cmd_name); // used by the cvar code to check for cvar / command name overlap const char *Cmd_CompleteCommand (const char *partial); // attempts to match a partial command for automatic command line completion // returns NULL if nothing fits int Cmd_Argc (void); const char *Cmd_Argv (int arg); const char *Cmd_Args (void); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are allways safe. int Cmd_CheckParm (const char *parm); // Returns the position (1 to argc-1) in the command's argument list // where the given parameter apears, or 0 if not present void Cmd_TokenizeString (const char *text); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString (const char *text, cmd_source_t src); // Parses a single line of text into arguments and tries to execute it. // The text can come from the command buffer, a remote client, or stdin. void Cmd_ForwardToServer (void); // adds the current command line as a clc_stringcmd to the client message. // things like godmode, noclip, etc, are commands directed to the server, // so when they are typed in at the console, they will need to be forwarded. void Cmd_Print (const char *text); // used by command functions to send output to either the graphics console or // passed as a print message to the client #endif /* _QUAKE_CMD_H */ ������������������������������������quakespasm-0.93.0/Quake/snd_mix.c�������������������������������������������������������������������0000644�0000000�0000000�00000031132�13136721573�015336� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2011 O. Sezer <sezero@users.sourceforge.net> Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_mix.c -- portable code to mix sounds for snd_dma.c #include "quakedef.h" #define PAINTBUFFER_SIZE 2048 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; int snd_scaletable[32][256]; int *snd_p, snd_linear_count; short *snd_out; static int snd_vol; static void Snd_WriteLinearBlastStereo16 (void) { int i; int val; for (i = 0; i < snd_linear_count; i += 2) { val = snd_p[i] / 256; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < (short)0x8000) snd_out[i] = (short)0x8000; else snd_out[i] = val; val = snd_p[i+1] / 256; if (val > 0x7fff) snd_out[i+1] = 0x7fff; else if (val < (short)0x8000) snd_out[i+1] = (short)0x8000; else snd_out[i+1] = val; } } static void S_TransferStereo16 (int endtime) { int lpos; int lpaintedtime; snd_p = (int *) paintbuffer; lpaintedtime = paintedtime; while (lpaintedtime < endtime) { // handle recirculating buffer issues lpos = lpaintedtime & ((shm->samples >> 1) - 1); snd_out = (short *)shm->buffer + (lpos << 1); snd_linear_count = (shm->samples >> 1) - lpos; if (lpaintedtime + snd_linear_count > endtime) snd_linear_count = endtime - lpaintedtime; snd_linear_count <<= 1; // write a linear blast of samples Snd_WriteLinearBlastStereo16 (); snd_p += snd_linear_count; lpaintedtime += (snd_linear_count >> 1); } } static void S_TransferPaintBuffer (int endtime) { int out_idx, out_mask; int count, step, val; int *p; if (shm->samplebits == 16 && shm->channels == 2) { S_TransferStereo16 (endtime); return; } p = (int *) paintbuffer; count = (endtime - paintedtime) * shm->channels; out_mask = shm->samples - 1; out_idx = paintedtime * shm->channels & out_mask; step = 3 - shm->channels; if (shm->samplebits == 16) { short *out = (short *)shm->buffer; while (count--) { val = *p / 256; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (shm->samplebits == 8 && !shm->signed8) { unsigned char *out = shm->buffer; while (count--) { val = *p / 256; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val / 256) + 128; out_idx = (out_idx + 1) & out_mask; } } else if (shm->samplebits == 8) /* S8 format, e.g. with Amiga AHI */ { signed char *out = (signed char *) shm->buffer; while (count--) { val = *p / 256; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val / 256); out_idx = (out_idx + 1) & out_mask; } } } /* ============== S_MakeBlackmanWindowKernel Makes a lowpass filter kernel, from equation 16-4 in "The Scientist and Engineer's Guide to Digital Signal Processing" M is the kernel size (not counting the center point), must be even kernel has room for M+1 floats f_c is the filter cutoff frequency, as a fraction of the samplerate ============== */ static void S_MakeBlackmanWindowKernel(float *kernel, int M, float f_c) { int i; for (i = 0; i <= M; i++) { if (i == M/2) { kernel[i] = 2 * M_PI * f_c; } else { kernel[i] = ( sin(2 * M_PI * f_c * (i - M/2.0)) / (i - (M/2.0)) ) * (0.42 - 0.5*cos(2 * M_PI * i / (double)M) + 0.08*cos(4 * M_PI * i / (double)M) ); } } // normalize the kernel so all of the values sum to 1 { float sum = 0; for (i = 0; i <= M; i++) { sum += kernel[i]; } for (i = 0; i <= M; i++) { kernel[i] /= sum; } } } typedef struct { float *memory; // kernelsize floats float *kernel; // kernelsize floats int kernelsize; // M+1, rounded up to be a multiple of 16 int M; // M value used to make kernel, even int parity; // 0-3 float f_c; // cutoff frequency, [0..1], fraction of sample rate } filter_t; static void S_UpdateFilter(filter_t *filter, int M, float f_c) { if (filter->f_c != f_c || filter->M != M) { if (filter->memory != NULL) free(filter->memory); if (filter->kernel != NULL) free(filter->kernel); filter->M = M; filter->f_c = f_c; filter->parity = 0; // M + 1 rounded up to the next multiple of 16 filter->kernelsize = (M + 1) + 16 - ((M + 1) % 16); filter->memory = (float *) calloc(filter->kernelsize, sizeof(float)); filter->kernel = (float *) calloc(filter->kernelsize, sizeof(float)); S_MakeBlackmanWindowKernel(filter->kernel, M, f_c); } } /* ============== S_ApplyFilter Lowpass-filter the given buffer containing 44100Hz audio. As an optimization, it decimates the audio to 11025Hz (setting every sample position that's not a multiple of 4 to 0), then convoluting with the filter kernel is 4x faster, because we can skip 3/4 of the input samples that are known to be 0 and skip 3/4 of the filter kernel. ============== */ static void S_ApplyFilter(filter_t *filter, int *data, int stride, int count) { int i, j; float *input; const int kernelsize = filter->kernelsize; const float *kernel = filter->kernel; int parity; input = (float *) malloc(sizeof(float) * (filter->kernelsize + count)); // set up the input buffer // memory holds the previous filter->kernelsize samples of input. memcpy(input, filter->memory, filter->kernelsize * sizeof(float)); for (i=0; i<count; i++) { input[filter->kernelsize+i] = data[i * stride] / (32768.0 * 256.0); } // copy out the last filter->kernelsize samples to 'memory' for next time memcpy(filter->memory, input + count, filter->kernelsize * sizeof(float)); // apply the filter parity = filter->parity; for (i=0; i<count; i++) { const float *input_plus_i = input + i; float val[4] = {0, 0, 0, 0}; for (j = (4 - parity) % 4; j < kernelsize; j+=16) { val[0] += kernel[j] * input_plus_i[j]; val[1] += kernel[j+4] * input_plus_i[j+4]; val[2] += kernel[j+8] * input_plus_i[j+8]; val[3] += kernel[j+12] * input_plus_i[j+12]; } // 4.0 factor is to increase volume by 12 dB; this is to make up the // volume drop caused by the zero-filling this filter does. data[i * stride] = (val[0] + val[1] + val[2] + val[3]) * (32768.0 * 256.0 * 4.0); parity = (parity + 1) % 4; } filter->parity = parity; free(input); } /* ============== S_LowpassFilter lowpass filters 24-bit integer samples in 'data' (stored in 32-bit ints). assumes 44100Hz sample rate, and lowpasses at around 5kHz memory should be a zero-filled filter_t struct ============== */ static void S_LowpassFilter(int *data, int stride, int count, filter_t *memory) { int M; float bw, f_c; switch ((int)snd_filterquality.value) { case 1: M = 126; bw = 0.900; break; case 2: M = 150; bw = 0.915; break; case 3: M = 174; bw = 0.930; break; case 4: M = 198; bw = 0.945; break; case 5: default: M = 222; bw = 0.960; break; } f_c = (bw * 11025 / 2.0) / 44100.0; S_UpdateFilter(memory, M, f_c); S_ApplyFilter(memory, data, stride, count); } /* =============================================================================== CHANNEL MIXING =============================================================================== */ static void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime, int paintbufferstart); static void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime, int paintbufferstart); void S_PaintChannels (int endtime) { int i; int end, ltime, count; channel_t *ch; sfxcache_t *sc; snd_vol = sfxvolume.value * 256; while (paintedtime < endtime) { // if paintbuffer is smaller than DMA buffer end = endtime; if (endtime - paintedtime > PAINTBUFFER_SIZE) end = paintedtime + PAINTBUFFER_SIZE; // clear the paint buffer memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); // paint in the channels. ch = snd_channels; for (i = 0; i < total_channels; i++, ch++) { if (!ch->sfx) continue; if (!ch->leftvol && !ch->rightvol) continue; sc = S_LoadSound (ch->sfx); if (!sc) continue; ltime = paintedtime; while (ltime < end) { // paint up to end if (ch->end < end) count = ch->end - ltime; else count = end - ltime; if (count > 0) { // the last param to SND_PaintChannelFrom is the index // to start painting to in the paintbuffer, usually 0. if (sc->width == 1) SND_PaintChannelFrom8(ch, sc, count, ltime - paintedtime); else SND_PaintChannelFrom16(ch, sc, count, ltime - paintedtime); ltime += count; } // if at end of loop, restart if (ltime >= ch->end) { if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { // channel just stopped ch->sfx = NULL; break; } } } } // clip each sample to 0dB, then reduce by 6dB (to leave some headroom for // the lowpass filter and the music). the lowpass will smooth out the // clipping for (i=0; i<end-paintedtime; i++) { paintbuffer[i].left = CLAMP(-32768 * 256, paintbuffer[i].left, 32767 * 256) / 2; paintbuffer[i].right = CLAMP(-32768 * 256, paintbuffer[i].right, 32767 * 256) / 2; } // apply a lowpass filter if (sndspeed.value == 11025 && shm->speed == 44100) { static filter_t memory_l, memory_r; S_LowpassFilter((int *)paintbuffer, 2, end - paintedtime, &memory_l); S_LowpassFilter(((int *)paintbuffer) + 1, 2, end - paintedtime, &memory_r); } // paint in the music if (s_rawend >= paintedtime) { // copy from the streaming sound source int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for (i = paintedtime; i < stop; i++) { s = i & (MAX_RAW_SAMPLES - 1); // lower music by 6db to match sfx paintbuffer[i - paintedtime].left += s_rawsamples[s].left / 2; paintbuffer[i - paintedtime].right += s_rawsamples[s].right / 2; } // if (i != end) // Con_Printf ("partial stream\n"); // else // Con_Printf ("full stream\n"); } // transfer out according to DMA format S_TransferPaintBuffer(end); paintedtime = end; } } void SND_InitScaletable (void) { int i, j; int scale; for (i = 0; i < 32; i++) { scale = i * 8 * 256 * sfxvolume.value; for (j = 0; j < 256; j++) { /* When compiling with gcc-4.1.0 at optimisations O1 and higher, the tricky signed char type conversion is not guaranteed. Therefore we explicity calculate the signed value from the index as required. From Kevin Shanahan. See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26719 */ // snd_scaletable[i][j] = ((signed char)j) * scale; snd_scaletable[i][j] = ((j < 128) ? j : j - 256) * scale; } } } static void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int paintbufferstart) { int data; int *lscale, *rscale; unsigned char *sfx; int i; if (ch->leftvol > 255) ch->leftvol = 255; if (ch->rightvol > 255) ch->rightvol = 255; lscale = snd_scaletable[ch->leftvol >> 3]; rscale = snd_scaletable[ch->rightvol >> 3]; sfx = (unsigned char *)sc->data + ch->pos; for (i = 0; i < count; i++) { data = sfx[i]; paintbuffer[paintbufferstart + i].left += lscale[data]; paintbuffer[paintbufferstart + i].right += rscale[data]; } ch->pos += count; } static void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int paintbufferstart) { int data; int left, right; int leftvol, rightvol; signed short *sfx; int i; leftvol = ch->leftvol * snd_vol; rightvol = ch->rightvol * snd_vol; leftvol /= 256; rightvol /= 256; sfx = (signed short *)sc->data + ch->pos; for (i = 0; i < count; i++) { data = sfx[i]; // this was causing integer overflow as observed in quakespasm // with the warpspasm mod moved >>8 to left/right volume above. // left = (data * leftvol) >> 8; // right = (data * rightvol) >> 8; left = data * leftvol; right = data * rightvol; paintbuffer[paintbufferstart + i].left += left; paintbuffer[paintbufferstart + i].right += right; } ch->pos += count; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/lodepng.h�������������������������������������������������������������������0000644�0000000�0000000�00000240621�13157453245�015340� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* LodePNG version 20170917 Copyright (c) 2005-2017 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef LODEPNG_H #define LODEPNG_H #include <string.h> /*for size_t*/ extern const char* LODEPNG_VERSION_STRING; /* The following #defines are used to create code sections. They can be disabled to disable code sections, which can give faster compile time and smaller binary. The "NO_COMPILE" defines are designed to be used to pass as defines to the compiler command to disable them without modifying this header, e.g. -DLODEPNG_NO_COMPILE_ZLIB for gcc. In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to allow implementing a custom lodepng_crc32. */ /*deflate & zlib. If disabled, you must specify alternative zlib functions in the custom_zlib field of the compress and decompress settings*/ #ifndef LODEPNG_NO_COMPILE_ZLIB #define LODEPNG_COMPILE_ZLIB #endif /*png encoder and png decoder*/ #ifndef LODEPNG_NO_COMPILE_PNG #define LODEPNG_COMPILE_PNG #endif /*deflate&zlib decoder and png decoder*/ #ifndef LODEPNG_NO_COMPILE_DECODER #define LODEPNG_COMPILE_DECODER #endif /*deflate&zlib encoder and png encoder*/ #ifndef LODEPNG_NO_COMPILE_ENCODER #define LODEPNG_COMPILE_ENCODER #endif /*the optional built in harddisk file loading and saving functions*/ #ifndef LODEPNG_NO_COMPILE_DISK #define LODEPNG_COMPILE_DISK #endif /*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ #ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS #define LODEPNG_COMPILE_ANCILLARY_CHUNKS #endif /*ability to convert error numerical codes to English text string*/ #ifndef LODEPNG_NO_COMPILE_ERROR_TEXT #define LODEPNG_COMPILE_ERROR_TEXT #endif /*Compile the default allocators (C's free, malloc and realloc). If you disable this, you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your source files with custom allocators.*/ #ifndef LODEPNG_NO_COMPILE_ALLOCATORS #define LODEPNG_COMPILE_ALLOCATORS #endif /*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ #ifdef __cplusplus #ifndef LODEPNG_NO_COMPILE_CPP #define LODEPNG_COMPILE_CPP #endif #endif #ifdef LODEPNG_COMPILE_CPP #include <vector> #include <string> #endif /*LODEPNG_COMPILE_CPP*/ #ifdef LODEPNG_COMPILE_PNG /*The PNG color types (also used for raw).*/ typedef enum LodePNGColorType { LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ LCT_RGB = 2, /*RGB: 8,16 bit*/ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ } LodePNGColorType; #ifdef LODEPNG_COMPILE_DECODER /* Converts PNG data in memory to raw pixel data. out: Output parameter. Pointer to buffer that will contain the raw pixel data. After decoding, its size is w * h * (bytes per pixel) bytes larger than initially. Bytes per pixel depends on colortype and bitdepth. Must be freed after usage with free(*out). Note: for 16-bit per channel colors, uses big endian format like PNG does. w: Output parameter. Pointer to width of pixel data. h: Output parameter. Pointer to height of pixel data. in: Memory buffer with the PNG file. insize: size of the in buffer. colortype: the desired color type for the raw output image. See explanation on PNG color types. bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. Return value: LodePNG error code (0 means no error). */ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize); /*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize); #ifdef LODEPNG_COMPILE_DISK /* Load PNG from disk, from file with given name. Same as the other decode functions, but instead takes a filename as input. */ unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename); /*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename); #endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Converts raw pixel data into a PNG image in memory. The colortype and bitdepth of the output PNG image cannot be chosen, they are automatically determined by the colortype, bitdepth and content of the input pixel data. Note: for 16-bit per channel colors, needs big endian format like PNG does. out: Output parameter. Pointer to buffer that will contain the PNG image data. Must be freed after usage with free(*out). outsize: Output parameter. Pointer to the size in bytes of the out buffer. image: The raw pixel data to encode. The size of this buffer should be w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. w: width of the raw pixel data in pixels. h: height of the raw pixel data in pixels. colortype: the color type of the raw input image. See explanation on PNG color types. bitdepth: the bit depth of the raw input image. See explanation on PNG color types. Return value: LodePNG error code (0 means no error). */ unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h); /*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DISK /* Converts raw pixel data into a PNG file on disk. Same as the other encode functions, but instead takes a filename as output. NOTE: This overwrites existing files without warning! */ unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h); /*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h); #endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_CPP namespace lodepng { #ifdef LODEPNG_COMPILE_DECODER /*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::vector<unsigned char>& in, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #ifdef LODEPNG_COMPILE_DISK /* Converts PNG file from disk to raw pixel data in memory. Same as the other decode functions, but instead takes a filename as input. */ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER /*Same as lodepng_encode_memory, but encodes to an std::vector. colortype is that of the raw input data. The output PNG color type will be auto chosen.*/ unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned encode(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #ifdef LODEPNG_COMPILE_DISK /* Converts 32-bit RGBA raw pixel data into a PNG file on disk. Same as the other encode functions, but instead takes a filename as output. NOTE: This overwrites existing files without warning! */ unsigned encode(const std::string& filename, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned encode(const std::string& filename, const std::vector<unsigned char>& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_ENCODER */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ERROR_TEXT /*Returns an English description of the numerical error code.*/ const char* lodepng_error_text(unsigned code); #endif /*LODEPNG_COMPILE_ERROR_TEXT*/ #ifdef LODEPNG_COMPILE_DECODER /*Settings for zlib decompression*/ typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; struct LodePNGDecompressSettings { unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ /*use custom zlib decoder instead of built in one (default: null)*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is used, custom_deflate is ignored since only the built in zlib function will call custom_deflate*/ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); const void* custom_context; /*optional custom settings for custom functions*/ }; extern const LodePNGDecompressSettings lodepng_default_decompress_settings; void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Settings for zlib compression. Tweaking these settings tweaks the balance between speed and compression ratio. */ typedef struct LodePNGCompressSettings LodePNGCompressSettings; struct LodePNGCompressSettings /*deflate = compress*/ { /*LZ77 related settings*/ unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ /*use custom zlib encoder instead of built in one (default: null)*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGCompressSettings*); /*use custom deflate encoder instead of built in one (default: null) if custom_zlib is used, custom_deflate is ignored since only the built in zlib function will call custom_deflate*/ unsigned (*custom_deflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGCompressSettings*); const void* custom_context; /*optional custom settings for custom functions*/ }; extern const LodePNGCompressSettings lodepng_default_compress_settings; void lodepng_compress_settings_init(LodePNGCompressSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_PNG /* Color mode of an image. Contains all information required to decode the pixel bits to RGBA colors. This information is the same as used in the PNG file format, and is used both for PNG and raw image data in LodePNG. */ typedef struct LodePNGColorMode { /*header (IHDR)*/ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ /* palette (PLTE and tRNS) Dynamically allocated with the colors of the palette, including alpha. When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use lodepng_palette_clear, then for each color use lodepng_palette_add. If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. When decoding, by default you can ignore this palette, since LodePNG already fills the palette colors in the pixels of the raw RGBA output. The palette is only supported for color type 3. */ unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ /* transparent color key (tRNS) This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. For greyscale PNGs, r, g and b will all 3 be set to the same. When decoding, by default you can ignore this information, since LodePNG sets pixels with this key to transparent already in the raw RGBA output. The color key is only supported for color types 0 and 2. */ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ unsigned key_r; /*red/greyscale component of color key*/ unsigned key_g; /*green component of color key*/ unsigned key_b; /*blue component of color key*/ } LodePNGColorMode; /*init, cleanup and copy functions to use with this struct*/ void lodepng_color_mode_init(LodePNGColorMode* info); void lodepng_color_mode_cleanup(LodePNGColorMode* info); /*return value is error code (0 means no error)*/ unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); void lodepng_palette_clear(LodePNGColorMode* info); /*add 1 color to the palette*/ unsigned lodepng_palette_add(LodePNGColorMode* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a); /*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ unsigned lodepng_get_bpp(const LodePNGColorMode* info); /*get the amount of color channels used, based on colortype in the struct. If a palette is used, it counts as 1 channel.*/ unsigned lodepng_get_channels(const LodePNGColorMode* info); /*is it a greyscale type? (only colortype 0 or 4)*/ unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); /*has it got an alpha channel? (only colortype 2 or 6)*/ unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); /*has it got a palette? (only colortype 3)*/ unsigned lodepng_is_palette_type(const LodePNGColorMode* info); /*only returns true if there is a palette and there is a value in the palette with alpha < 255. Loops through the palette to check this.*/ unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); /* Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). Returns false if the image can only have opaque pixels. In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, or if "key_defined" is true. */ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); /*Returns the byte size of a raw image buffer with given width, height and color mode*/ size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*The information of a Time chunk in PNG.*/ typedef struct LodePNGTime { unsigned year; /*2 bytes used (0-65535)*/ unsigned month; /*1-12*/ unsigned day; /*1-31*/ unsigned hour; /*0-23*/ unsigned minute; /*0-59*/ unsigned second; /*0-60 (to allow for leap seconds)*/ } LodePNGTime; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*Information about the PNG image, except pixels, width and height.*/ typedef struct LodePNGInfo { /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ unsigned compression_method;/*compression method of the original file. Always 0.*/ unsigned filter_method; /*filter method of the original file*/ unsigned interlace_method; /*interlace method of the original file*/ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /* suggested background color chunk (bKGD) This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding the encoder writes the red one. For palette PNGs: When decoding, the RGB value will be stored, not a palette index. But when encoding, specify the index of the palette in background_r, the other two are then ignored. The decoder does not use this background color to edit the color of pixels. */ unsigned background_defined; /*is a suggested background color given?*/ unsigned background_r; /*red component of suggested background color*/ unsigned background_g; /*green component of suggested background color*/ unsigned background_b; /*blue component of suggested background color*/ /* non-international text chunks (tEXt and zTXt) The char** arrays each contain num strings. The actual messages are in text_strings, while text_keys are keywords that give a short description what the actual text represents, e.g. Title, Author, Description, or anything else. A keyword is minimum 1 character and maximum 79 characters long. It's discouraged to use a single line length longer than 79 characters for texts. Don't allocate these text buffers yourself. Use the init/cleanup functions correctly and use lodepng_add_text and lodepng_clear_text. */ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ char** text_strings; /*the actual text*/ /* international text chunks (iTXt) Similar to the non-international text chunks, but with additional strings "langtags" and "transkeys". */ size_t itext_num; /*the amount of international texts in this PNG*/ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ char** itext_strings; /*the actual international text - UTF-8 string*/ /*time chunk (tIME)*/ unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ LodePNGTime time; /*phys chunk (pHYs)*/ unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ unsigned phys_x; /*pixels per unit in x direction*/ unsigned phys_y; /*pixels per unit in y direction*/ unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ /* unknown chunks There are 3 buffers, one for each position in the PNG where unknown chunks can appear each buffer contains all unknown chunks for that position consecutively The 3 buffers are the unknown chunks between certain critical chunks: 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND Do not allocate or traverse this data yourself. Use the chunk traversing functions declared later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. */ unsigned char* unknown_chunks_data[3]; size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGInfo; /*init, cleanup and copy functions to use with this struct*/ void lodepng_info_init(LodePNGInfo* info); void lodepng_info_cleanup(LodePNGInfo* info); /*return value is error code (0 means no error)*/ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /* Converts raw buffer from one color type to another color type, based on LodePNGColorMode structs to describe the input and output color type. See the reference manual at the end of this header file to see which color conversions are supported. return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel of the output color type (lodepng_get_bpp). For < 8 bpp images, there should not be padding bits at the end of scanlines. For 16-bit per channel colors, uses big endian format like PNG does. Return value is LodePNG error code */ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DECODER /* Settings for the decoder. This contains settings for the PNG and the Zlib decoder, but not the Info settings from the Info structs. */ typedef struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ unsigned ignore_crc; /*ignore CRC checksums*/ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ unsigned remember_unknown_chunks; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGDecoderSettings; void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ typedef enum LodePNGFilterStrategy { /*every filter at zero*/ LFS_ZERO, /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ LFS_MINSUM, /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending on the image, this is better or worse than minsum.*/ LFS_ENTROPY, /* Brute-force-search PNG filters by compressing each filter for each scanline. Experimental, very slow, and only rarely gives better compression than MINSUM. */ LFS_BRUTE_FORCE, /*use predefined_filters buffer: you specify the filter type for each scanline*/ LFS_PREDEFINED } LodePNGFilterStrategy; /*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ typedef struct LodePNGColorProfile { unsigned colored; /*not greyscale*/ unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ unsigned short key_g; unsigned short key_b; unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ } LodePNGColorProfile; void lodepng_color_profile_init(LodePNGColorProfile* profile); /*Get a LodePNGColorProfile of the image.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); /*The function LodePNG uses internally to decide the PNG color with auto_convert. Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); /*Settings for the encoder.*/ typedef struct LodePNGEncoderSettings { LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to completely follow the official PNG heuristic, filter_palette_zero must be true and filter_strategy must be LFS_MINSUM*/ unsigned filter_palette_zero; /*Which filter strategy to use when not using zeroes due to filter_palette_zero. Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ LodePNGFilterStrategy filter_strategy; /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with the same length as the amount of scanlines in the image, and each value must <= 5. You have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ const unsigned char* predefined_filters; /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). If colortype is 3, PLTE is _always_ created.*/ unsigned force_palette; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*add LodePNG identifier and version as a text chunk, for debugging*/ unsigned add_id; /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ unsigned text_compression; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGEncoderSettings; void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) /*The settings, state and information for extended encoding and decoding.*/ typedef struct LodePNGState { #ifdef LODEPNG_COMPILE_DECODER LodePNGDecoderSettings decoder; /*the decoding settings*/ #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER LodePNGEncoderSettings encoder; /*the encoding settings*/ #endif /*LODEPNG_COMPILE_ENCODER*/ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ unsigned error; #ifdef LODEPNG_COMPILE_CPP /* For the lodepng::State subclass. */ virtual ~LodePNGState(){} #endif } LodePNGState; /*init, cleanup and copy functions to use with this struct*/ void lodepng_state_init(LodePNGState* state); void lodepng_state_cleanup(LodePNGState* state); void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); #endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ #ifdef LODEPNG_COMPILE_DECODER /* Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and getting much more information about the PNG image and color mode. */ unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); /* Read the PNG header, but not the actual data. This returns only the information that is in the header chunk of the PNG, such as width, height and color type. The information is placed in the info_png field of the LodePNGState. */ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state); #endif /*LODEPNG_COMPILE_ENCODER*/ /* The lodepng_chunk functions are normally not needed, except to traverse the unknown chunks stored in the LodePNGInfo struct, or add new ones to it. It also allows traversing the chunks of an encoded PNG file yourself. PNG standard chunk naming conventions: First byte: uppercase = critical, lowercase = ancillary Second byte: uppercase = public, lowercase = private Third byte: must be uppercase Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ /* Gets the length of the data of the chunk. Total chunk length has 12 bytes more. There must be at least 4 bytes to read from. If the result value is too large, it may be corrupt data. */ unsigned lodepng_chunk_length(const unsigned char* chunk); /*puts the 4-byte type in null terminated string*/ void lodepng_chunk_type(char type[5], const unsigned char* chunk); /*check if the type is the given type*/ unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); /*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); /*0: public, 1: private (see PNG standard)*/ unsigned char lodepng_chunk_private(const unsigned char* chunk); /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); /*get pointer to the data of the chunk, where the input points to the header of the chunk*/ unsigned char* lodepng_chunk_data(unsigned char* chunk); const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); /*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ unsigned lodepng_chunk_check_crc(const unsigned char* chunk); /*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ void lodepng_chunk_generate_crc(unsigned char* chunk); /*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ unsigned char* lodepng_chunk_next(unsigned char* chunk); const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); /* Appends chunk to the data in out. The given chunk should already have its chunk header. The out variable and outlength are updated to reflect the new reallocated buffer. Returns error code (0 if it went ok) */ unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); /* Appends new chunk to out. The chunk to append is given by giving its length, type and data separately. The type is a 4-letter string. The out variable and outlength are updated to reflect the new reallocated buffer. Returne error code (0 if it went ok) */ unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data); /*Calculate CRC32 of buffer*/ unsigned lodepng_crc32(const unsigned char* buf, size_t len); #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB /* This zlib part can be used independently to zlib compress and decompress a buffer. It cannot be used to create gzip files however, and it only supports the part of zlib that is required for PNG, it does not support dictionaries. */ #ifdef LODEPNG_COMPILE_DECODER /*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings); /* Decompresses Zlib data. Reallocates the out buffer and appends the data. The data must be according to the zlib specification. Either, *out must be NULL and *outsize must be 0, or, *out must be a valid buffer and *outsize its size in bytes. out must be freed by user after usage. */ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Compresses data with Zlib. Reallocates the out buffer and appends the data. Zlib adds a small header and trailer around the deflate data. The data is output in the format of the zlib specification. Either, *out must be NULL and *outsize must be 0, or, *out must be a valid buffer and *outsize its size in bytes. out must be freed by user after usage. */ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings); /* Find length-limited Huffman code for given frequencies. This function is in the public interface only for tests, it's used internally by lodepng_deflate. */ unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen); /*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DISK /* Load a file from disk into buffer. The function allocates the out buffer, and after usage you should free it. out: output parameter, contains pointer to loaded buffer. outsize: output parameter, size of the allocated out buffer filename: the path to the file to load return value: error code (0 means ok) */ unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); /* Save a file from buffer to disk. Warning, if it exists, this function overwrites the file without warning! buffer: the buffer to write buffersize: size of the buffer to write filename: the path to the file to save to return value: error code (0 means ok) */ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); #endif /*LODEPNG_COMPILE_DISK*/ #ifdef LODEPNG_COMPILE_CPP /* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ namespace lodepng { #ifdef LODEPNG_COMPILE_PNG class State : public LodePNGState { public: State(); State(const State& other); virtual ~State(); State& operator=(const State& other); }; #ifdef LODEPNG_COMPILE_DECODER /* Same as other lodepng::decode, but using a State for more settings and information. */ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize); unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, State& state, const std::vector<unsigned char>& in); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Same as other lodepng::encode, but using a State for more settings and information. */ unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h, State& state); unsigned encode(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, unsigned w, unsigned h, State& state); #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DISK /* Load a file from disk into an std::vector. return value: error code (0 means ok) */ unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename); /* Save the binary data in an std::vector to a file on disk. The file is overwritten without warning. */ unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_PNG */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER /* Zlib-decompress an unsigned char buffer */ unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); /* Zlib-decompress an std::vector */ unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER /* Zlib-compress an unsigned char buffer */ unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); /* Zlib-compress an std::vector */ unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_ZLIB */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ /* TODO: [.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often [.] check compatibility with various compilers - done but needs to be redone for every newer version [X] converting color to 16-bit per channel types [ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) [ ] make sure encoder generates no chunks with size > (2^31)-1 [ ] partial decoding (stream processing) [X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [ ] don't stop decoding on errors like 69, 57, 58 (make warnings) [ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes [ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... [ ] allow user to give data (void*) to custom allocator */ #endif /*LODEPNG_H inclusion guard*/ /* LodePNG Documentation --------------------- 0. table of contents -------------------- 1. about 1.1. supported features 1.2. features not supported 2. C and C++ version 3. security 4. decoding 5. encoding 6. color conversions 6.1. PNG color types 6.2. color conversions 6.3. padding bits 6.4. A note about 16-bits per channel and endianness 7. error values 8. chunks and PNG editing 9. compiler support 10. examples 10.1. decoder C++ example 10.2. decoder C example 11. state settings reference 12. changes 13. contact information 1. about -------- PNG is a file format to store raster images losslessly with good compression, supporting different color types and alpha channel. LodePNG is a PNG codec according to the Portable Network Graphics (PNG) Specification (Second Edition) - W3C Recommendation 10 November 2003. The specifications used are: *) Portable Network Graphics (PNG) Specification (Second Edition): http://www.w3.org/TR/2003/REC-PNG-20031110 *) RFC 1950 ZLIB Compressed Data Format version 3.3: http://www.gzip.org/zlib/rfc-zlib.html *) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: http://www.gzip.org/zlib/rfc-deflate.html The most recent version of LodePNG can currently be found at http://lodev.org/lodepng/ LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds extra functionality. LodePNG exists out of two files: -lodepng.h: the header file for both C and C++ -lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage If you want to start using LodePNG right away without reading this doc, get the examples from the LodePNG website to see how to use it in code, or check the smaller examples in chapter 13 here. LodePNG is simple but only supports the basic requirements. To achieve simplicity, the following design choices were made: There are no dependencies on any external library. There are functions to decode and encode a PNG with a single function call, and extended versions of these functions taking a LodePNGState struct allowing to specify or get more information. By default the colors of the raw image are always RGB or RGBA, no matter what color type the PNG file uses. To read and write files, there are simple functions to convert the files to/from buffers in memory. This all makes LodePNG suitable for loading textures in games, demos and small programs, ... It's less suitable for full fledged image editors, loading PNGs over network (it requires all the image data to be available before decoding can begin), life-critical systems, ... 1.1. supported features ----------------------- The following features are supported by the decoder: *) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, or the same color type as the PNG *) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image *) Adam7 interlace and deinterlace for any color type *) loading the image from harddisk or decoding it from a buffer from other sources than harddisk *) support for alpha channels, including RGBA color model, translucent palettes and color keying *) zlib decompression (inflate) *) zlib compression (deflate) *) CRC32 and ADLER32 checksums *) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. *) the following chunks are supported (generated/interpreted) by both encoder and decoder: IHDR: header information PLTE: color palette IDAT: pixel data IEND: the final chunk tRNS: transparency for palettized images tEXt: textual information zTXt: compressed textual information iTXt: international textual information bKGD: suggested background color pHYs: physical dimensions tIME: modification time 1.2. features not supported --------------------------- The following features are _not_ supported: *) some features needed to make a conformant PNG-Editor might be still missing. *) partial loading/stream processing. All data must be available and is processed in one call. *) The following public chunks are not supported but treated as unknown chunks by LodePNG cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT Some of these are not supported on purpose: LodePNG wants to provide the RGB values stored in the pixels, not values modified by system dependent gamma or color models. 2. C and C++ version -------------------- The C version uses buffers allocated with alloc that you need to free() yourself. You need to use init and cleanup functions for each struct whenever using a struct from the C version to avoid exploits and memory leaks. The C++ version has extra functions with std::vectors in the interface and the lodepng::State class which is a LodePNGState with constructor and destructor. These files work without modification for both C and C++ compilers because all the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers ignore it, and the C code is made to compile both with strict ISO C90 and C++. To use the C++ version, you need to rename the source file to lodepng.cpp (instead of lodepng.c), and compile it with a C++ compiler. To use the C version, you need to rename the source file to lodepng.c (instead of lodepng.cpp), and compile it with a C compiler. 3. Security ----------- Even if carefully designed, it's always possible that LodePNG contains possible exploits. If you discover one, please let me know, and it will be fixed. When using LodePNG, care has to be taken with the C version of LodePNG, as well as the C-style structs when working with C++. The following conventions are used for all C-style structs: -if a struct has a corresponding init function, always call the init function when making a new one -if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks -if a struct has a corresponding copy function, use the copy function instead of "=". The destination must also be inited already. 4. Decoding ----------- Decoding converts a PNG compressed image to a raw pixel buffer. Most documentation on using the decoder is at its declarations in the header above. For C, simple decoding can be done with functions such as lodepng_decode32, and more advanced decoding can be done with the struct LodePNGState and lodepng_decode. For C++, all decoding can be done with the various lodepng::decode functions, and lodepng::State can be used for advanced features. When using the LodePNGState, it uses the following fields for decoding: *) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here *) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get *) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use LodePNGInfo info_png -------------------- After decoding, this contains extra information of the PNG image, except the actual pixels, width and height because these are already gotten directly from the decoder functions. It contains for example the original color type of the PNG image, text comments, suggested background color, etc... More details about the LodePNGInfo struct are at its declaration documentation. LodePNGColorMode info_raw ------------------------- When decoding, here you can specify which color type you want the resulting raw image to be. If this is different from the colortype of the PNG, then the decoder will automatically convert the result. This conversion always works, except if you want it to convert a color PNG to greyscale or to a palette with missing colors. By default, 32-bit color is used for the result. LodePNGDecoderSettings decoder ------------------------------ The settings can be used to ignore the errors created by invalid CRC and Adler32 chunks, and to disable the decoding of tEXt chunks. There's also a setting color_convert, true by default. If false, no conversion is done, the resulting data will be as it was in the PNG (after decompression) and you'll have to puzzle the colors of the pixels together yourself using the color type information in the LodePNGInfo. 5. Encoding ----------- Encoding converts a raw pixel buffer to a PNG compressed image. Most documentation on using the encoder is at its declarations in the header above. For C, simple encoding can be done with functions such as lodepng_encode32, and more advanced decoding can be done with the struct LodePNGState and lodepng_encode. For C++, all encoding can be done with the various lodepng::encode functions, and lodepng::State can be used for advanced features. Like the decoder, the encoder can also give errors. However it gives less errors since the encoder input is trusted, the decoder input (a PNG image that could be forged by anyone) is not trusted. When using the LodePNGState, it uses the following fields for encoding: *) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. *) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has *) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use LodePNGInfo info_png -------------------- When encoding, you use this the opposite way as when decoding: for encoding, you fill in the values you want the PNG to have before encoding. By default it's not needed to specify a color type for the PNG since it's automatically chosen, but it's possible to choose it yourself given the right settings. The encoder will not always exactly match the LodePNGInfo struct you give, it tries as close as possible. Some things are ignored by the encoder. The encoder uses, for example, the following settings from it when applicable: colortype and bitdepth, text chunks, time chunk, the color key, the palette, the background color, the interlace method, unknown chunks, ... When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. If the palette contains any colors for which the alpha channel is not 255 (so there are translucent colors in the palette), it'll add a tRNS chunk. LodePNGColorMode info_raw ------------------------- You specify the color type of the raw image that you give to the input here, including a possible transparent color key and palette you happen to be using in your raw image data. By default, 32-bit color is assumed, meaning your input has to be in RGBA format with 4 bytes (unsigned chars) per pixel. LodePNGEncoderSettings encoder ------------------------------ The following settings are supported (some are in sub-structs): *) auto_convert: when this option is enabled, the encoder will automatically choose the smallest possible color mode (including color key) that can encode the colors of all pixels without information loss. *) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, 2 = dynamic huffman tree (best compression). Should be 2 for proper compression. *) use_lz77: whether or not to use LZ77 for compressed block types. Should be true for proper compression. *) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value 2048 by default, but can be set to 32768 for better, but slow, compression. *) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE chunk if force_palette is true. This can used as suggested palette to convert to by viewers that don't support more than 256 colors (if those still exist) *) add_id: add text chunk "Encoder: LodePNG <version>" to the image. *) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. zTXt chunks use zlib compression on the text. This gives a smaller result on large texts but a larger result on small texts (such as a single program name). It's all tEXt or all zTXt though, there's no separate setting per text yet. 6. color conversions -------------------- An important thing to note about LodePNG, is that the color type of the PNG, and the color type of the raw image, are completely independent. By default, when you decode a PNG, you get the result as a raw image in the color type you want, no matter whether the PNG was encoded with a palette, greyscale or RGBA color. And if you encode an image, by default LodePNG will automatically choose the PNG color type that gives good compression based on the values of colors and amount of colors in the image. It can be configured to let you control it instead as well, though. To be able to do this, LodePNG does conversions from one color mode to another. It can convert from almost any color type to any other color type, except the following conversions: RGB to greyscale is not supported, and converting to a palette when the palette doesn't have a required color is not supported. This is not supported on purpose: this is information loss which requires a color reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey is easy, but there are multiple ways if you want to give some channels more weight). By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB color, no matter what color type the PNG has. And by default when encoding, LodePNG automatically picks the best color model for the output PNG, and expects the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control the color format of the images yourself, you can skip this chapter. 6.1. PNG color types -------------------- A PNG image can have many color types, ranging from 1-bit color to 64-bit color, as well as palettized color modes. After the zlib decompression and unfiltering in the PNG image is done, the raw pixel data will have that color type and thus a certain amount of bits per pixel. If you want the output raw image after decoding to have another color type, a conversion is done by LodePNG. The PNG specification gives the following color types: 0: greyscale, bit depths 1, 2, 4, 8, 16 2: RGB, bit depths 8 and 16 3: palette, bit depths 1, 2, 4 and 8 4: greyscale with alpha, bit depths 8 and 16 6: RGBA, bit depths 8 and 16 Bit depth is the amount of bits per pixel per color channel. So the total amount of bits per pixel is: amount of channels * bitdepth. 6.2. color conversions ---------------------- As explained in the sections about the encoder and decoder, you can specify color types and bit depths in info_png and info_raw to change the default behaviour. If, when decoding, you want the raw image to be something else than the default, you need to set the color type and bit depth you want in the LodePNGColorMode, or the parameters colortype and bitdepth of the simple decoding function. If, when encoding, you use another color type than the default in the raw input image, you need to specify its color type and bit depth in the LodePNGColorMode of the raw image, or use the parameters colortype and bitdepth of the simple encoding function. If, when encoding, you don't want LodePNG to choose the output PNG color type but control it yourself, you need to set auto_convert in the encoder settings to false, and specify the color type you want in the LodePNGInfo of the encoder (including palette: it can generate a palette if auto_convert is true, otherwise not). If the input and output color type differ (whether user chosen or auto chosen), LodePNG will do a color conversion, which follows the rules below, and may sometimes result in an error. To avoid some confusion: -the decoder converts from PNG to raw image -the encoder converts from raw image to PNG -the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image -the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG -when encoding, the color type in LodePNGInfo is ignored if auto_convert is enabled, it is automatically generated instead -when decoding, the color type in LodePNGInfo is set by the decoder to that of the original PNG image, but it can be ignored since the raw image has the color type you requested instead -if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion between the color types is done if the color types are supported. If it is not supported, an error is returned. If the types are the same, no conversion is done. -even though some conversions aren't supported, LodePNG supports loading PNGs from any colortype and saving PNGs to any colortype, sometimes it just requires preparing the raw image correctly before encoding. -both encoder and decoder use the same color converter. Non supported color conversions: -color to greyscale: no error is thrown, but the result will look ugly because only the red channel is taken -anything to palette when that palette does not have that color in it: in this case an error is thrown Supported color conversions: -anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA -any grey or grey+alpha, to grey or grey+alpha -anything to a palette, as long as the palette has the requested colors in it -removing alpha channel -higher to smaller bitdepth, and vice versa If you want no color conversion to be done (e.g. for speed or control): -In the encoder, you can make it save a PNG with any color type by giving the raw color mode and LodePNGInfo the same color mode, and setting auto_convert to false. -In the decoder, you can make it store the pixel data in the same color type as the PNG has, by setting the color_convert setting to false. Settings in info_raw are then ignored. The function lodepng_convert does the color conversion. It is available in the interface but normally isn't needed since the encoder and decoder already call it. 6.3. padding bits ----------------- In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines have a bit amount that isn't a multiple of 8, then padding bits are used so that each scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. The raw input image you give to the encoder, and the raw output image you get from the decoder will NOT have these padding bits, e.g. in the case of a 1-bit image with a width of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, not the first bit of a new byte. 6.4. A note about 16-bits per channel and endianness ---------------------------------------------------- LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like for any other color format. The 16-bit values are stored in big endian (most significant byte first) in these arrays. This is the opposite order of the little endian used by x86 CPU's. LodePNG always uses big endian because the PNG file format does so internally. Conversions to other formats than PNG uses internally are not supported by LodePNG on purpose, there are myriads of formats, including endianness of 16-bit colors, the order in which you store R, G, B and A, and so on. Supporting and converting to/from all that is outside the scope of LodePNG. This may mean that, depending on your use case, you may want to convert the big endian output of LodePNG to little endian with a for loop. This is certainly not always needed, many applications and libraries support big endian 16-bit colors anyway, but it means you cannot simply cast the unsigned char* buffer to an unsigned short* buffer on x86 CPUs. 7. error values --------------- All functions in LodePNG that return an error code, return 0 if everything went OK, or a non-zero code if there was an error. The meaning of the LodePNG error values can be retrieved with the function lodepng_error_text: given the numerical error code, it returns a description of the error in English as a string. Check the implementation of lodepng_error_text to see the meaning of each code. 8. chunks and PNG editing ------------------------- If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG editor that should follow the rules about handling of unknown chunks, or if your program is able to read other types of chunks than the ones handled by LodePNG, then that's possible with the chunk functions of LodePNG. A PNG chunk has the following layout: 4 bytes length 4 bytes type name length bytes data 4 bytes CRC 8.1. iterating through chunks ----------------------------- If you have a buffer containing the PNG image data, then the first chunk (the IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the signature of the PNG and are not part of a chunk. But if you start at byte 8 then you have a chunk, and can check the following things of it. NOTE: none of these functions check for memory buffer boundaries. To avoid exploits, always make sure the buffer contains all the data of the chunks. When using lodepng_chunk_next, make sure the returned value is within the allocated memory. unsigned lodepng_chunk_length(const unsigned char* chunk): Get the length of the chunk's data. The total chunk length is this length + 12. void lodepng_chunk_type(char type[5], const unsigned char* chunk): unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): Get the type of the chunk or compare if it's a certain type unsigned char lodepng_chunk_critical(const unsigned char* chunk): unsigned char lodepng_chunk_private(const unsigned char* chunk): unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). Check if the chunk is private (public chunks are part of the standard, private ones not). Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your program doesn't handle that type of unknown chunk. unsigned char* lodepng_chunk_data(unsigned char* chunk): const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): Get a pointer to the start of the data of the chunk. unsigned lodepng_chunk_check_crc(const unsigned char* chunk): void lodepng_chunk_generate_crc(unsigned char* chunk): Check if the crc is correct or generate a correct one. unsigned char* lodepng_chunk_next(unsigned char* chunk): const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these functions do no boundary checking of the allocated data whatsoever, so make sure there is enough data available in the buffer to be able to go to the next chunk. unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data): These functions are used to create new chunks that are appended to the data in *out that has length *outlength. The append function appends an existing chunk to the new data. The create function creates a new chunk with the given parameters and appends it. Type is the 4-letter name of the chunk. 8.2. chunks in info_png ----------------------- The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 buffers (each with size) to contain 3 types of unknown chunks: the ones that come before the PLTE chunk, the ones that come between the PLTE and the IDAT chunks, and the ones that come after the IDAT chunks. It's necessary to make the distionction between these 3 cases because the PNG standard forces to keep the ordering of unknown chunks compared to the critical chunks, but does not force any other ordering rules. info_png.unknown_chunks_data[0] is the chunks before PLTE info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT info_png.unknown_chunks_data[2] is the chunks after IDAT The chunks in these 3 buffers can be iterated through and read by using the same way described in the previous subchapter. When using the decoder to decode a PNG, you can make it store all unknown chunks if you set the option settings.remember_unknown_chunks to 1. By default, this option is off (0). The encoder will always encode unknown chunks that are stored in the info_png. If you need it to add a particular chunk that isn't known by LodePNG, you can use lodepng_chunk_append or lodepng_chunk_create to the chunk data in info_png.unknown_chunks_data[x]. Chunks that are known by LodePNG should not be added in that way. E.g. to make LodePNG add a bKGD chunk, set background_defined to true and add the correct parameters there instead. 9. compiler support ------------------- No libraries other than the current standard C library are needed to compile LodePNG. For the C++ version, only the standard C++ library is needed on top. Add the files lodepng.c(pp) and lodepng.h to your project, include lodepng.h where needed, and your program can read/write PNG files. It is compatible with C90 and up, and C++03 and up. If performance is important, use optimization when compiling! For both the encoder and decoder, this makes a large difference. Make sure that LodePNG is compiled with the same compiler of the same version and with the same settings as the rest of the program, or the interfaces with std::vectors and std::strings in C++ can be incompatible. CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. *) gcc and g++ LodePNG is developed in gcc so this compiler is natively supported. It gives no warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ version 4.7.1 on Linux, 32-bit and 64-bit. *) Clang Fully supported and warning-free. *) Mingw The Mingw compiler (a port of gcc for Windows) should be fully supported by LodePNG. *) Visual Studio and Visual C++ Express Edition LodePNG should be warning-free with warning level W4. Two warnings were disabled with pragmas though: warning 4244 about implicit conversions, and warning 4996 where it wants to use a non-standard function fopen_s instead of the standard C fopen. Visual Studio may want "stdafx.h" files to be included in each source file and give an error "unexpected end of file while looking for precompiled header". This is not standard C++ and will not be added to the stock LodePNG. You can disable it for lodepng.cpp only by right clicking it, Properties, C/C++, Precompiled Headers, and set it to Not Using Precompiled Headers there. NOTE: Modern versions of VS should be fully supported, but old versions, e.g. VS6, are not guaranteed to work. *) Compilers on Macintosh LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for C and C++. *) Other Compilers If you encounter problems on any compilers, feel free to let me know and I may try to fix it if the compiler is modern and standards complient. 10. examples ------------ This decoder example shows the most basic usage of LodePNG. More complex examples can be found on the LodePNG website. 10.1. decoder C++ example ------------------------- #include "lodepng.h" #include <iostream> int main(int argc, char *argv[]) { const char* filename = argc > 1 ? argv[1] : "test.png"; //load and decode std::vector<unsigned char> image; unsigned width, height; unsigned error = lodepng::decode(image, width, height, filename); //if there's an error, display it if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... } 10.2. decoder C example ----------------------- #include "lodepng.h" int main(int argc, char *argv[]) { unsigned error; unsigned char* image; size_t width, height; const char* filename = argc > 1 ? argv[1] : "test.png"; error = lodepng_decode32_file(&image, &width, &height, filename); if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); / * use image here * / free(image); return 0; } 11. state settings reference ---------------------------- A quick reference of some settings to set on the LodePNGState For decoding: state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums state.decoder.zlibsettings.custom_...: use custom inflate function state.decoder.ignore_crc: ignore CRC checksums state.decoder.color_convert: convert internal PNG color to chosen one state.decoder.read_text_chunks: whether to read in text metadata chunks state.decoder.remember_unknown_chunks: whether to read in unknown chunks state.info_raw.colortype: desired color type for decoded image state.info_raw.bitdepth: desired bit depth for decoded image state.info_raw....: more color settings, see struct LodePNGColorMode state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo For encoding: state.encoder.zlibsettings.btype: disable compression by setting it to 0 state.encoder.zlibsettings.use_lz77: use LZ77 in compression state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching state.encoder.zlibsettings.lazymatching: try one more LZ77 matching state.encoder.zlibsettings.custom_...: use custom deflate function state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png state.encoder.filter_palette_zero: PNG filter strategy for palette state.encoder.filter_strategy: PNG filter strategy to encode with state.encoder.force_palette: add palette even if not encoding to one state.encoder.add_id: add LodePNG identifier and version as a text chunk state.encoder.text_compression: use compressed text chunks for metadata state.info_raw.colortype: color type of raw input image you provide state.info_raw.bitdepth: bit depth of raw input image you provide state.info_raw: more color settings, see struct LodePNGColorMode state.info_png.color.colortype: desired color type if auto_convert is false state.info_png.color.bitdepth: desired bit depth if auto_convert is false state.info_png.color....: more color settings, see struct LodePNGColorMode state.info_png....: more PNG related settings, see struct LodePNGInfo 12. changes ----------- The version number of LodePNG is the date of the change given in the format yyyymmdd. Some changes aren't backwards compatible. Those are indicated with a (!) symbol. *) 17 sep 2017: fix memory leak for some encoder input error cases *) 27 nov 2016: grey+alpha auto color model detection bugfix *) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). *) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within the limits of pure C90). *) 08 dec 2015: Made load_file function return error if file can't be opened. *) 24 okt 2015: Bugfix with decoding to palette output. *) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. *) 23 aug 2014: Reduced needless memory usage of decoder. *) 28 jun 2014: Removed fix_png setting, always support palette OOB for simplicity. Made ColorProfile public. *) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. *) 22 dec 2013: Power of two windowsize required for optimization. *) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. *) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). *) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" prefix for the custom allocators and made it possible with a new #define to use custom ones in your project without needing to change lodepng's code. *) 28 jan 2013: Bugfix with color key. *) 27 okt 2012: Tweaks in text chunk keyword length error handling. *) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. (no palette). Better deflate tree encoding. New compression tweak settings. Faster color conversions while decoding. Some internal cleanups. *) 23 sep 2012: Reduced warnings in Visual Studio a little bit. *) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions and made it work with function pointers instead. *) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc and free functions and toggle #defines from compiler flags. Small fixes. *) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. *) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed redundant C++ codec classes. Reduced amount of structs. Everything changed, but it is cleaner now imho and functionality remains the same. Also fixed several bugs and shrunk the implementation code. Made new samples. *) 6 nov 2011 (!): By default, the encoder now automatically chooses the best PNG color model and bit depth, based on the amount and type of colors of the raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. *) 9 okt 2011: simpler hash chain implementation for the encoder. *) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. *) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. A bug with the PNG filtertype heuristic was fixed, so that it chooses much better ones (it's quite significant). A setting to do an experimental, slow, brute force search for PNG filter types is added. *) 17 aug 2011 (!): changed some C zlib related function names. *) 16 aug 2011: made the code less wide (max 120 characters per line). *) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. *) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. *) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman to optimize long sequences of zeros. *) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and LodePNG_InfoColor_canHaveAlpha functions for convenience. *) 7 nov 2010: added LodePNG_error_text function to get error code description. *) 30 okt 2010: made decoding slightly faster *) 26 okt 2010: (!) changed some C function and struct names (more consistent). Reorganized the documentation and the declaration order in the header. *) 08 aug 2010: only changed some comments and external samples. *) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. *) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. *) 02 sep 2008: fixed bug where it could create empty tree that linux apps could read by ignoring the problem but windows apps couldn't. *) 06 jun 2008: added more error checks for out of memory cases. *) 26 apr 2008: added a few more checks here and there to ensure more safety. *) 06 mar 2008: crash with encoding of strings fixed *) 02 feb 2008: support for international text chunks added (iTXt) *) 23 jan 2008: small cleanups, and #defines to divide code in sections *) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. *) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. *) 17 jan 2008: ability to encode and decode compressed zTXt chunks added Also various fixes, such as in the deflate and the padding bits code. *) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved filtering code of encoder. *) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A C++ wrapper around this provides an interface almost identical to before. Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code are together in these files but it works both for C and C++ compilers. *) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks *) 30 aug 2007: bug fixed which makes this Borland C++ compatible *) 09 aug 2007: some VS2005 warnings removed again *) 21 jul 2007: deflate code placed in new namespace separate from zlib code *) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images *) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing invalid std::vector element [0] fixed, and level 3 and 4 warnings removed *) 02 jun 2007: made the encoder add a tag with version by default *) 27 may 2007: zlib and png code separated (but still in the same file), simple encoder/decoder functions added for more simple usage cases *) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), moved some examples from here to lodepng_examples.cpp *) 12 may 2007: palette decoding bug fixed *) 24 apr 2007: changed the license from BSD to the zlib license *) 11 mar 2007: very simple addition: ability to encode bKGD chunks. *) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding palettized PNG images. Plus little interface change with palette and texts. *) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. Fixed a bug where the end code of a block had length 0 in the Huffman tree. *) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented and supported by the encoder, resulting in smaller PNGs at the output. *) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. *) 24 jan 2007: gave encoder an error interface. Added color conversion from any greyscale type to 8-bit greyscale with or without alpha. *) 21 jan 2007: (!) Totally changed the interface. It allows more color types to convert to and is more uniform. See the manual for how it works now. *) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: encode/decode custom tEXt chunks, separate classes for zlib & deflate, and at last made the decoder give errors for incorrect Adler32 or Crc. *) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. *) 29 dec 2006: Added support for encoding images without alpha channel, and cleaned out code as well as making certain parts faster. *) 28 dec 2006: Added "Settings" to the encoder. *) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. Removed some code duplication in the decoder. Fixed little bug in an example. *) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. Fixed a bug of the decoder with 16-bit per color. *) 15 okt 2006: Changed documentation structure *) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the given image buffer, however for now it's not compressed. *) 08 sep 2006: (!) Changed to interface with a Decoder class *) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different way. Renamed decodePNG to decodePNGGeneric. *) 29 jul 2006: (!) Changed the interface: image info is now returned as a struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. *) 28 jul 2006: Cleaned the code and added new error checks. Corrected terminology "deflate" into "inflate". *) 23 jun 2006: Added SDL example in the documentation in the header, this example allows easy debugging by displaying the PNG and its transparency. *) 22 jun 2006: (!) Changed way to obtain error value. Added loadFile function for convenience. Made decodePNG32 faster. *) 21 jun 2006: (!) Changed type of info vector to unsigned. Changed position of palette in info vector. Fixed an important bug that happened on PNGs with an uncompressed block. *) 16 jun 2006: Internally changed unsigned into unsigned where needed, and performed some optimizations. *) 07 jun 2006: (!) Renamed functions to decodePNG and placed them in LodePNG namespace. Changed the order of the parameters. Rewrote the documentation in the header. Renamed files to lodepng.cpp and lodepng.h *) 22 apr 2006: Optimized and improved some code *) 07 sep 2005: (!) Changed to std::vector interface *) 12 aug 2005: Initial release (C++, decoder only) 13. contact information ----------------------- Feel free to contact me with suggestions, problems, comments, ... concerning LodePNG. If you encounter a PNG image that doesn't work properly with this decoder, feel free to send it and I'll use it to find and fix the problem. My email address is (puzzle the account and domain together with an @ symbol): Domain: gmail dot com. Account: lode dot vandevenne. Copyright (c) 2005-2017 Lode Vandevenne */ ���������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_rmisc.c������������������������������������������������������������������0000644�0000000�0000000�00000035534�13112111036�015463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_misc.c #include "quakedef.h" //johnfitz -- new cvars extern cvar_t r_stereo; extern cvar_t r_stereodepth; extern cvar_t r_clearcolor; extern cvar_t r_drawflat; extern cvar_t r_flatlightstyles; extern cvar_t gl_fullbrights; extern cvar_t gl_farclip; extern cvar_t gl_overbright; extern cvar_t gl_overbright_models; extern cvar_t r_waterquality; extern cvar_t r_oldwater; extern cvar_t r_waterwarp; extern cvar_t r_oldskyleaf; extern cvar_t r_drawworld; extern cvar_t r_showtris; extern cvar_t r_showbboxes; extern cvar_t r_lerpmodels; extern cvar_t r_lerpmove; extern cvar_t r_nolerp_list; extern cvar_t r_noshadow_list; //johnfitz extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix extern gltexture_t *playertextures[MAX_SCOREBOARD]; //johnfitz /* ==================== GL_Overbright_f -- johnfitz ==================== */ static void GL_Overbright_f (cvar_t *var) { R_RebuildAllLightmaps (); } /* ==================== GL_Fullbrights_f -- johnfitz ==================== */ static void GL_Fullbrights_f (cvar_t *var) { TexMgr_ReloadNobrightImages (); } /* ==================== R_SetClearColor_f -- johnfitz ==================== */ static void R_SetClearColor_f (cvar_t *var) { byte *rgb; int s; s = (int)r_clearcolor.value & 0xFF; rgb = (byte*)(d_8to24table + s); glClearColor (rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0,0); } /* ==================== R_Novis_f -- johnfitz ==================== */ static void R_VisChanged (cvar_t *var) { extern int vis_changed; vis_changed = 1; } /* =============== R_Model_ExtraFlags_List_f -- johnfitz -- called when r_nolerp_list or r_noshadow_list cvar changes =============== */ static void R_Model_ExtraFlags_List_f (cvar_t *var) { int i; for (i=0; i < MAX_MODELS; i++) Mod_SetExtraFlags (cl.model_precache[i]); } /* ==================== R_SetWateralpha_f -- ericw ==================== */ static void R_SetWateralpha_f (cvar_t *var) { map_wateralpha = var->value; } /* ==================== R_SetLavaalpha_f -- ericw ==================== */ static void R_SetLavaalpha_f (cvar_t *var) { map_lavaalpha = var->value; } /* ==================== R_SetTelealpha_f -- ericw ==================== */ static void R_SetTelealpha_f (cvar_t *var) { map_telealpha = var->value; } /* ==================== R_SetSlimealpha_f -- ericw ==================== */ static void R_SetSlimealpha_f (cvar_t *var) { map_slimealpha = var->value; } /* ==================== GL_WaterAlphaForSurfface -- ericw ==================== */ float GL_WaterAlphaForSurface (msurface_t *fa) { if (fa->flags & SURF_DRAWLAVA) return map_lavaalpha > 0 ? map_lavaalpha : map_wateralpha; else if (fa->flags & SURF_DRAWTELE) return map_telealpha > 0 ? map_telealpha : map_wateralpha; else if (fa->flags & SURF_DRAWSLIME) return map_slimealpha > 0 ? map_slimealpha : map_wateralpha; else return map_wateralpha; } /* =============== R_Init =============== */ void R_Init (void) { extern cvar_t gl_finish; Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_norefresh); Cvar_RegisterVariable (&r_lightmap); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_wateralpha); Cvar_SetCallback (&r_wateralpha, R_SetWateralpha_f); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); Cvar_SetCallback (&r_novis, R_VisChanged); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_pos); Cvar_RegisterVariable (&gl_finish); Cvar_RegisterVariable (&gl_clear); Cvar_RegisterVariable (&gl_cull); Cvar_RegisterVariable (&gl_smoothmodels); Cvar_RegisterVariable (&gl_affinemodels); Cvar_RegisterVariable (&gl_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); //johnfitz -- new cvars Cvar_RegisterVariable (&r_stereo); Cvar_RegisterVariable (&r_stereodepth); Cvar_RegisterVariable (&r_clearcolor); Cvar_SetCallback (&r_clearcolor, R_SetClearColor_f); Cvar_RegisterVariable (&r_waterquality); Cvar_RegisterVariable (&r_oldwater); Cvar_RegisterVariable (&r_waterwarp); Cvar_RegisterVariable (&r_drawflat); Cvar_RegisterVariable (&r_flatlightstyles); Cvar_RegisterVariable (&r_oldskyleaf); Cvar_SetCallback (&r_oldskyleaf, R_VisChanged); Cvar_RegisterVariable (&r_drawworld); Cvar_RegisterVariable (&r_showtris); Cvar_RegisterVariable (&r_showbboxes); Cvar_RegisterVariable (&gl_farclip); Cvar_RegisterVariable (&gl_fullbrights); Cvar_RegisterVariable (&gl_overbright); Cvar_SetCallback (&gl_fullbrights, GL_Fullbrights_f); Cvar_SetCallback (&gl_overbright, GL_Overbright_f); Cvar_RegisterVariable (&gl_overbright_models); Cvar_RegisterVariable (&r_lerpmodels); Cvar_RegisterVariable (&r_lerpmove); Cvar_RegisterVariable (&r_nolerp_list); Cvar_SetCallback (&r_nolerp_list, R_Model_ExtraFlags_List_f); Cvar_RegisterVariable (&r_noshadow_list); Cvar_SetCallback (&r_noshadow_list, R_Model_ExtraFlags_List_f); //johnfitz Cvar_RegisterVariable (&gl_zfix); // QuakeSpasm z-fighting fix Cvar_RegisterVariable (&r_lavaalpha); Cvar_RegisterVariable (&r_telealpha); Cvar_RegisterVariable (&r_slimealpha); Cvar_RegisterVariable (&r_scale); Cvar_SetCallback (&r_lavaalpha, R_SetLavaalpha_f); Cvar_SetCallback (&r_telealpha, R_SetTelealpha_f); Cvar_SetCallback (&r_slimealpha, R_SetSlimealpha_f); R_InitParticles (); R_SetClearColor_f (&r_clearcolor); //johnfitz Sky_Init (); //johnfitz Fog_Init (); //johnfitz } /* =============== R_TranslatePlayerSkin -- johnfitz -- rewritten. also, only handles new colors, not new skins =============== */ void R_TranslatePlayerSkin (int playernum) { int top, bottom; top = (cl.scores[playernum].colors & 0xf0)>>4; bottom = cl.scores[playernum].colors &15; //FIXME: if gl_nocolors is on, then turned off, the textures may be out of sync with the scoreboard colors. if (!gl_nocolors.value) if (playertextures[playernum]) TexMgr_ReloadImage (playertextures[playernum], top, bottom); } /* =============== R_TranslateNewPlayerSkin -- johnfitz -- split off of TranslatePlayerSkin -- this is called when the skin or model actually changes, instead of just new colors added bug fix from bengt jardup =============== */ void R_TranslateNewPlayerSkin (int playernum) { char name[64]; byte *pixels; aliashdr_t *paliashdr; int skinnum; //get correct texture pixels currententity = &cl_entities[1+playernum]; if (!currententity->model || currententity->model->type != mod_alias) return; paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); skinnum = currententity->skinnum; //TODO: move these tests to the place where skinnum gets received from the server if (skinnum < 0 || skinnum >= paliashdr->numskins) { Con_DPrintf("(%d): Invalid player skin #%d\n", playernum, skinnum); skinnum = 0; } pixels = (byte *)paliashdr + paliashdr->texels[skinnum]; // This is not a persistent place! //upload new image q_snprintf(name, sizeof(name), "player_%i", playernum); playertextures[playernum] = TexMgr_LoadImage (currententity->model, name, paliashdr->skinwidth, paliashdr->skinheight, SRC_INDEXED, pixels, paliashdr->gltextures[skinnum][0]->source_file, paliashdr->gltextures[skinnum][0]->source_offset, TEXPREF_PAD | TEXPREF_OVERWRITE); //now recolor it R_TranslatePlayerSkin (playernum); } /* =============== R_NewGame -- johnfitz -- handle a game switch =============== */ void R_NewGame (void) { int i; //clear playertexture pointers (the textures themselves were freed by texmgr_newgame) for (i=0; i<MAX_SCOREBOARD; i++) playertextures[i] = NULL; } /* ============= R_ParseWorldspawn called at map load ============= */ static void R_ParseWorldspawn (void) { char key[128], value[4096]; const char *data; map_wateralpha = r_wateralpha.value; map_lavaalpha = r_lavaalpha.value; map_telealpha = r_telealpha.value; map_slimealpha = r_slimealpha.value; data = COM_Parse(cl.worldmodel->entities); if (!data) return; // error if (com_token[0] != '{') return; // error while (1) { data = COM_Parse(data); if (!data) return; // error if (com_token[0] == '}') break; // end of worldspawn if (com_token[0] == '_') strcpy(key, com_token + 1); else strcpy(key, com_token); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; data = COM_Parse(data); if (!data) return; // error strcpy(value, com_token); if (!strcmp("wateralpha", key)) map_wateralpha = atof(value); if (!strcmp("lavaalpha", key)) map_lavaalpha = atof(value); if (!strcmp("telealpha", key)) map_telealpha = atof(value); if (!strcmp("slimealpha", key)) map_slimealpha = atof(value); } } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; for (i=0 ; i<256 ; i++) d_lightstylevalue[i] = 264; // normal light value // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i=0 ; i<cl.worldmodel->numleafs ; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); GL_BuildLightmaps (); GL_BuildBModelVertexBuffer (); //ericw -- no longer load alias models into a VBO here, it's done in Mod_LoadAliasModel r_framecount = 0; //johnfitz -- paranoid? r_visframecount = 0; //johnfitz -- paranoid? Sky_NewMap (); //johnfitz -- skybox in worldspawn Fog_NewMap (); //johnfitz -- global fog in worldspawn R_ParseWorldspawn (); //ericw -- wateralpha, lavaalpha, telealpha, slimealpha in worldspawn load_subdivide_size = gl_subdivide_size.value; //johnfitz -- is this the right place to set this? } /* ==================== R_TimeRefresh_f For program optimization ==================== */ void R_TimeRefresh_f (void) { int i; float start, stop, time; if (cls.state != ca_connected) { Con_Printf("Not connected to a server\n"); return; } start = Sys_DoubleTime (); for (i = 0; i < 128; i++) { GL_BeginRendering(&glx, &gly, &glwidth, &glheight); r_refdef.viewangles[1] = i/128.0*360.0; R_RenderView (); GL_EndRendering (); } glFinish (); stop = Sys_DoubleTime (); time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, 128/time); } void D_FlushCaches (void) { } static GLuint gl_programs[16]; static int gl_num_programs; static qboolean GL_CheckShader (GLuint shader) { GLint status; GL_GetShaderivFunc (shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { char infolog[1024]; memset(infolog, 0, sizeof(infolog)); GL_GetShaderInfoLogFunc (shader, sizeof(infolog), NULL, infolog); Con_Warning ("GLSL program failed to compile: %s", infolog); return false; } return true; } static qboolean GL_CheckProgram (GLuint program) { GLint status; GL_GetProgramivFunc (program, GL_LINK_STATUS, &status); if (status != GL_TRUE) { char infolog[1024]; memset(infolog, 0, sizeof(infolog)); GL_GetProgramInfoLogFunc (program, sizeof(infolog), NULL, infolog); Con_Warning ("GLSL program failed to link: %s", infolog); return false; } return true; } /* ============= GL_GetUniformLocation ============= */ GLint GL_GetUniformLocation (GLuint *programPtr, const char *name) { GLint location; if (!programPtr) return -1; location = GL_GetUniformLocationFunc(*programPtr, name); if (location == -1) { Con_Warning("GL_GetUniformLocationFunc %s failed\n", name); *programPtr = 0; } return location; } /* ==================== GL_CreateProgram Compiles and returns GLSL program. ==================== */ GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings) { int i; GLuint program, vertShader, fragShader; if (!gl_glsl_able) return 0; vertShader = GL_CreateShaderFunc (GL_VERTEX_SHADER); GL_ShaderSourceFunc (vertShader, 1, &vertSource, NULL); GL_CompileShaderFunc (vertShader); if (!GL_CheckShader (vertShader)) { GL_DeleteShaderFunc (vertShader); return 0; } fragShader = GL_CreateShaderFunc (GL_FRAGMENT_SHADER); GL_ShaderSourceFunc (fragShader, 1, &fragSource, NULL); GL_CompileShaderFunc (fragShader); if (!GL_CheckShader (fragShader)) { GL_DeleteShaderFunc (vertShader); GL_DeleteShaderFunc (fragShader); return 0; } program = GL_CreateProgramFunc (); GL_AttachShaderFunc (program, vertShader); GL_DeleteShaderFunc (vertShader); GL_AttachShaderFunc (program, fragShader); GL_DeleteShaderFunc (fragShader); for (i = 0; i < numbindings; i++) { GL_BindAttribLocationFunc (program, bindings[i].attrib, bindings[i].name); } GL_LinkProgramFunc (program); if (!GL_CheckProgram (program)) { GL_DeleteProgramFunc (program); return 0; } else { if (gl_num_programs == (sizeof(gl_programs)/sizeof(GLuint))) Host_Error ("gl_programs overflow"); gl_programs[gl_num_programs] = program; gl_num_programs++; return program; } } /* ==================== R_DeleteShaders Deletes any GLSL programs that have been created. ==================== */ void R_DeleteShaders (void) { int i; if (!gl_glsl_able) return; for (i = 0; i < gl_num_programs; i++) { GL_DeleteProgramFunc (gl_programs[i]); gl_programs[i] = 0; } gl_num_programs = 0; } GLuint current_array_buffer, current_element_array_buffer; /* ==================== GL_BindBuffer glBindBuffer wrapper ==================== */ void GL_BindBuffer (GLenum target, GLuint buffer) { GLuint *cache; if (!gl_vbo_able) return; switch (target) { case GL_ARRAY_BUFFER: cache = ¤t_array_buffer; break; case GL_ELEMENT_ARRAY_BUFFER: cache = ¤t_element_array_buffer; break; default: Host_Error("GL_BindBuffer: unsupported target %d", (int)target); return; } if (*cache != buffer) { *cache = buffer; GL_BindBufferFunc (target, *cache); } } /* ==================== GL_ClearBufferBindings This must be called if you do anything that could make the cached bindings invalid (e.g. manually binding, destroying the context). ==================== */ void GL_ClearBufferBindings () { if (!gl_vbo_able) return; current_array_buffer = 0; current_element_array_buffer = 0; GL_BindBufferFunc (GL_ARRAY_BUFFER, 0); GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cfgfile.h�������������������������������������������������������������������0000644�0000000�0000000�00000003024�12407762022�015272� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cfgfile.h -- misc reads from the config file * * Copyright (C) 2008-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CFGFILE_H #define __CFGFILE_H int CFG_OpenConfig (const char *cfg_name); // opens the given config file. only one open config file is // kept: previosly opened one, if any, will be closed. void CFG_CloseConfig (void); // closes the currently open config file. void CFG_ReadCvars (const char **vars, int num_vars); // reads the values of cvars in the given list from the opened // config file. void CFG_ReadCvarOverrides (const char **vars, int num_vars); // convenience function, reading the "+" command line override // values of cvars in the given list. doesn't do anything with // the config file. call this after CFG_ReadCvars() and before // locking your cvars. #endif /* __CFGFILE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/console.h�������������������������������������������������������������������0000644�0000000�0000000�00000004106�13136631402�015334� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __CONSOLE_H #define __CONSOLE_H // // console // extern int con_totallines; extern int con_backscroll; extern qboolean con_forcedup; // because no entities to refresh extern qboolean con_initialized; extern byte *con_chars; extern char con_lastcenterstring[]; //johnfitz void Con_DrawCharacter (int cx, int line, int num); void Con_CheckResize (void); void Con_Init (void); void Con_DrawConsole (int lines, qboolean drawinput); void Con_Printf (const char *fmt, ...) FUNC_PRINTF(1,2); void Con_DWarning (const char *fmt, ...) FUNC_PRINTF(1,2); //ericw void Con_Warning (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz void Con_DPrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void Con_DPrintf2 (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz void Con_SafePrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_ToggleConsole_f (void); void Con_NotifyBox (const char *text); // during startup for sound / cd warnings void Con_Show (void); void Con_Hide (void); const char *Con_Quakebar (int len); void Con_TabComplete (void); void Con_LogCenterPrint (const char *str); // // debuglog // void LOG_Init (quakeparms_t *parms); void LOG_Close (void); void Con_DebugLog (const char *msg); #endif /* __CONSOLE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/bgmusic.c�������������������������������������������������������������������0000644�0000000�0000000�00000024453�12732713724�015336� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Background music handling for Quakespasm (adapted from uHexen2) * Handles streaming music as raw sound samples and runs the midi driver * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_codec.h" #include "bgmusic.h" #define MUSIC_DIRNAME "music" qboolean bgmloop; cvar_t bgm_extmusic = {"bgm_extmusic", "1", CVAR_ARCHIVE}; static qboolean no_extmusic= false; static float old_volume = -1.0f; typedef enum _bgm_player { BGM_NONE = -1, BGM_MIDIDRV = 1, BGM_STREAMER } bgm_player_t; typedef struct music_handler_s { unsigned int type; /* 1U << n (see snd_codec.h) */ bgm_player_t player; /* Enumerated bgm player type */ int is_available; /* -1 means not present */ const char *ext; /* Expected file extension */ const char *dir; /* Where to look for music file */ struct music_handler_s *next; } music_handler_t; static music_handler_t wanted_handlers[] = { { CODECTYPE_VORBIS,BGM_STREAMER,-1, "ogg", MUSIC_DIRNAME, NULL }, { CODECTYPE_OPUS, BGM_STREAMER, -1, "opus", MUSIC_DIRNAME, NULL }, { CODECTYPE_MP3, BGM_STREAMER, -1, "mp3", MUSIC_DIRNAME, NULL }, { CODECTYPE_FLAC, BGM_STREAMER, -1, "flac", MUSIC_DIRNAME, NULL }, { CODECTYPE_WAV, BGM_STREAMER, -1, "wav", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "it", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "s3m", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "xm", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "mod", MUSIC_DIRNAME, NULL }, { CODECTYPE_UMX, BGM_STREAMER, -1, "umx", MUSIC_DIRNAME, NULL }, { CODECTYPE_NONE, BGM_NONE, -1, NULL, NULL, NULL } }; static music_handler_t *music_handlers = NULL; #define ANY_CODECTYPE 0xFFFFFFFF #define CDRIP_TYPES (CODECTYPE_VORBIS | CODECTYPE_MP3 | CODECTYPE_FLAC | CODECTYPE_WAV) #define CDRIPTYPE(x) (((x) & CDRIP_TYPES) != 0) static snd_stream_t *bgmstream = NULL; static void BGM_Play_f (void) { if (Cmd_Argc() == 2) { BGM_Play (Cmd_Argv(1)); } else { Con_Printf ("music <musicfile>\n"); return; } } static void BGM_Pause_f (void) { BGM_Pause (); } static void BGM_Resume_f (void) { BGM_Resume (); } static void BGM_Loop_f (void) { if (Cmd_Argc() == 2) { if (q_strcasecmp(Cmd_Argv(1), "0") == 0 || q_strcasecmp(Cmd_Argv(1),"off") == 0) bgmloop = false; else if (q_strcasecmp(Cmd_Argv(1), "1") == 0 || q_strcasecmp(Cmd_Argv(1),"on") == 0) bgmloop = true; else if (q_strcasecmp(Cmd_Argv(1),"toggle") == 0) bgmloop = !bgmloop; } if (bgmloop) Con_Printf("Music will be looped\n"); else Con_Printf("Music will not be looped\n"); } static void BGM_Stop_f (void) { BGM_Stop(); } qboolean BGM_Init (void) { music_handler_t *handlers = NULL; int i; Cvar_RegisterVariable(&bgm_extmusic); Cmd_AddCommand("music", BGM_Play_f); Cmd_AddCommand("music_pause", BGM_Pause_f); Cmd_AddCommand("music_resume", BGM_Resume_f); Cmd_AddCommand("music_loop", BGM_Loop_f); Cmd_AddCommand("music_stop", BGM_Stop_f); if (COM_CheckParm("-noextmusic") != 0) no_extmusic = true; bgmloop = true; for (i = 0; wanted_handlers[i].type != CODECTYPE_NONE; i++) { switch (wanted_handlers[i].player) { case BGM_MIDIDRV: /* not supported in quake */ break; case BGM_STREAMER: wanted_handlers[i].is_available = S_CodecIsAvailable(wanted_handlers[i].type); break; case BGM_NONE: default: break; } if (wanted_handlers[i].is_available != -1) { if (handlers) { handlers->next = &wanted_handlers[i]; handlers = handlers->next; } else { music_handlers = &wanted_handlers[i]; handlers = music_handlers; } } } return true; } void BGM_Shutdown (void) { BGM_Stop(); /* sever our connections to * midi_drv and snd_codec */ music_handlers = NULL; } static void BGM_Play_noext (const char *filename, unsigned int allowed_types) { char tmp[MAX_QPATH]; music_handler_t *handler; handler = music_handlers; while (handler) { if (! (handler->type & allowed_types)) { handler = handler->next; continue; } if (!handler->is_available) { handler = handler->next; continue; } q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", handler->dir, filename, handler->ext); switch (handler->player) { case BGM_MIDIDRV: /* not supported in quake */ break; case BGM_STREAMER: bgmstream = S_CodecOpenStreamType(tmp, handler->type); if (bgmstream) return; /* success */ break; case BGM_NONE: default: break; } handler = handler->next; } Con_Printf("Couldn't handle music file %s\n", filename); } void BGM_Play (const char *filename) { char tmp[MAX_QPATH]; const char *ext; music_handler_t *handler; BGM_Stop(); if (music_handlers == NULL) return; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return; } ext = COM_FileGetExtension(filename); if (! *ext) /* try all things */ { BGM_Play_noext(filename, ANY_CODECTYPE); return; } handler = music_handlers; while (handler) { if (handler->is_available && !q_strcasecmp(ext, handler->ext)) break; handler = handler->next; } if (!handler) { Con_Printf("Unhandled extension for %s\n", filename); return; } q_snprintf(tmp, sizeof(tmp), "%s/%s", handler->dir, filename); switch (handler->player) { case BGM_MIDIDRV: /* not supported in quake */ break; case BGM_STREAMER: bgmstream = S_CodecOpenStreamType(tmp, handler->type); if (bgmstream) return; /* success */ break; case BGM_NONE: default: break; } Con_Printf("Couldn't handle music file %s\n", filename); } void BGM_PlayCDtrack (byte track, qboolean looping) { /* instead of searching by the order of music_handlers, do so by * the order of searchpath priority: the file from the searchpath * with the highest path_id is most likely from our own gamedir * itself. This way, if a mod has track02 as a *.mp3 file, which * is below *.ogg in the music_handler order, the mp3 will still * have priority over track02.ogg from, say, id1. */ char tmp[MAX_QPATH]; const char *ext; unsigned int path_id, prev_id, type; music_handler_t *handler; BGM_Stop(); if (CDAudio_Play(track, looping) == 0) return; /* success */ if (music_handlers == NULL) return; if (no_extmusic || !bgm_extmusic.value) return; prev_id = 0; type = 0; ext = NULL; handler = music_handlers; while (handler) { if (! handler->is_available) goto _next; if (! CDRIPTYPE(handler->type)) goto _next; q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s", MUSIC_DIRNAME, (int)track, handler->ext); if (! COM_FileExists(tmp, &path_id)) goto _next; if (path_id > prev_id) { prev_id = path_id; type = handler->type; ext = handler->ext; } _next: handler = handler->next; } if (ext == NULL) Con_Printf("Couldn't find a cdrip for track %d\n", (int)track); else { q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s", MUSIC_DIRNAME, (int)track, ext); bgmstream = S_CodecOpenStreamType(tmp, type); if (! bgmstream) Con_Printf("Couldn't handle music file %s\n", tmp); } } void BGM_Stop (void) { if (bgmstream) { bgmstream->status = STREAM_NONE; S_CodecCloseStream(bgmstream); bgmstream = NULL; s_rawend = 0; } } void BGM_Pause (void) { if (bgmstream) { if (bgmstream->status == STREAM_PLAY) bgmstream->status = STREAM_PAUSE; } } void BGM_Resume (void) { if (bgmstream) { if (bgmstream->status == STREAM_PAUSE) bgmstream->status = STREAM_PLAY; } } static void BGM_UpdateStream (void) { qboolean did_rewind = false; int res; /* Number of bytes read. */ int bufferSamples; int fileSamples; int fileBytes; byte raw[16384]; if (bgmstream->status != STREAM_PLAY) return; /* don't bother playing anything if musicvolume is 0 */ if (bgmvolume.value <= 0) return; /* see how many samples should be copied into the raw buffer */ if (s_rawend < paintedtime) s_rawend = paintedtime; while (s_rawend < paintedtime + MAX_RAW_SAMPLES) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend - paintedtime); /* decide how much data needs to be read from the file */ fileSamples = bufferSamples * bgmstream->info.rate / shm->speed; if (!fileSamples) return; /* our max buffer size */ fileBytes = fileSamples * (bgmstream->info.width * bgmstream->info.channels); if (fileBytes > (int) sizeof(raw)) { fileBytes = (int) sizeof(raw); fileSamples = fileBytes / (bgmstream->info.width * bgmstream->info.channels); } /* Read */ res = S_CodecReadStream(bgmstream, fileBytes, raw); if (res < fileBytes) { fileBytes = res; fileSamples = res / (bgmstream->info.width * bgmstream->info.channels); } if (res > 0) /* data: add to raw buffer */ { S_RawSamples(fileSamples, bgmstream->info.rate, bgmstream->info.width, bgmstream->info.channels, raw, bgmvolume.value); did_rewind = false; } else if (res == 0) /* EOF */ { if (bgmloop) { if (did_rewind) { Con_Printf("Stream keeps returning EOF.\n"); BGM_Stop(); return; } res = S_CodecRewindStream(bgmstream); if (res != 0) { Con_Printf("Stream seek error (%i), stopping.\n", res); BGM_Stop(); return; } did_rewind = true; } else { BGM_Stop(); return; } } else /* res < 0: some read error */ { Con_Printf("Stream read error (%i), stopping.\n", res); BGM_Stop(); return; } } } void BGM_Update (void) { if (old_volume != bgmvolume.value) { if (bgmvolume.value < 0) Cvar_SetQuick (&bgmvolume, "0"); else if (bgmvolume.value > 1) Cvar_SetQuick (&bgmvolume, "1"); old_volume = bgmvolume.value; } if (bgmstream) BGM_UpdateStream (); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_defs.h������������������������������������������������������������������0000644�0000000�0000000�00000015601�13070044614�015463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * net_defs.h -- functions and data private to the network layer * net_sys.h and its dependencies must be included before net_defs.h. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_DEFS_H #define __NET_DEFS_H struct qsockaddr { #if defined(HAVE_SA_LEN) unsigned char qsa_len; unsigned char qsa_family; #else short qsa_family; #endif /* BSD, sockaddr */ unsigned char qsa_data[14]; }; #define NET_HEADERSIZE (2 * sizeof(unsigned int)) #define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE) // NetHeader flags #define NETFLAG_LENGTH_MASK 0x0000ffff #define NETFLAG_DATA 0x00010000 #define NETFLAG_ACK 0x00020000 #define NETFLAG_NAK 0x00040000 #define NETFLAG_EOM 0x00080000 #define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_CTL 0x80000000 #if (NETFLAG_LENGTH_MASK & NET_MAXMESSAGE) != NET_MAXMESSAGE #error "NET_MAXMESSAGE must fit within NETFLAG_LENGTH_MASK" #endif #define NET_PROTOCOL_VERSION 3 /** This is the network info/connection protocol. It is used to find Quake servers, get info about them, and connect to them. Once connected, the Quake game protocol (documented elsewhere) is used. General notes: game_name is currently always "QUAKE", but is there so this same protocol can be used for future games as well; can you say Quake2? CCREQ_CONNECT string game_name "QUAKE" byte net_protocol_version NET_PROTOCOL_VERSION CCREQ_SERVER_INFO string game_name "QUAKE" byte net_protocol_version NET_PROTOCOL_VERSION CCREQ_PLAYER_INFO byte player_number CCREQ_RULE_INFO string rule CCREP_ACCEPT long port CCREP_REJECT string reason CCREP_SERVER_INFO string server_address string host_name string level_name byte current_players byte max_players byte protocol_version NET_PROTOCOL_VERSION CCREP_PLAYER_INFO byte player_number string name long colors long frags long connect_time string address CCREP_RULE_INFO string rule string value note: There are two address forms used above. The short form is just a port number. The address that goes along with the port is defined as "whatever address you receive this reponse from". This lets us use the host OS to solve the problem of multiple host addresses (possibly with no routing between them); the host will use the right address when we reply to the inbound connection request. The long from is a full address and port in a string. It is used for returning the address of a server that is not running locally. **/ #define CCREQ_CONNECT 0x01 #define CCREQ_SERVER_INFO 0x02 #define CCREQ_PLAYER_INFO 0x03 #define CCREQ_RULE_INFO 0x04 #define CCREP_ACCEPT 0x81 #define CCREP_REJECT 0x82 #define CCREP_SERVER_INFO 0x83 #define CCREP_PLAYER_INFO 0x84 #define CCREP_RULE_INFO 0x85 typedef struct qsocket_s { struct qsocket_s *next; double connecttime; double lastMessageTime; double lastSendTime; qboolean disconnected; qboolean canSend; qboolean sendNext; int driver; int landriver; sys_socket_t socket; void *driverdata; unsigned int ackSequence; unsigned int sendSequence; unsigned int unreliableSendSequence; int sendMessageLength; byte sendMessage [NET_MAXMESSAGE]; unsigned int receiveSequence; unsigned int unreliableReceiveSequence; int receiveMessageLength; byte receiveMessage [NET_MAXMESSAGE]; struct qsockaddr addr; char address[NET_NAMELEN]; } qsocket_t; extern qsocket_t *net_activeSockets; extern qsocket_t *net_freeSockets; extern int net_numsockets; typedef struct { const char *name; qboolean initialized; sys_socket_t controlSock; sys_socket_t (*Init) (void); void (*Shutdown) (void); void (*Listen) (qboolean state); sys_socket_t (*Open_Socket) (int port); int (*Close_Socket) (sys_socket_t socketid); int (*Connect) (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t (*CheckNewConnections) (void); int (*Read) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int (*Write) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int (*Broadcast) (sys_socket_t socketid, byte *buf, int len); const char * (*AddrToString) (struct qsockaddr *addr); int (*StringToAddr) (const char *string, struct qsockaddr *addr); int (*GetSocketAddr) (sys_socket_t socketid, struct qsockaddr *addr); int (*GetNameFromAddr) (struct qsockaddr *addr, char *name); int (*GetAddrFromName) (const char *name, struct qsockaddr *addr); int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2); int (*GetSocketPort) (struct qsockaddr *addr); int (*SetSocketPort) (struct qsockaddr *addr, int port); } net_landriver_t; #define MAX_NET_DRIVERS 8 extern net_landriver_t net_landrivers[]; extern const int net_numlandrivers; typedef struct { const char *name; qboolean initialized; int (*Init) (void); void (*Listen) (qboolean state); void (*SearchForHosts) (qboolean xmit); qsocket_t *(*Connect) (const char *host); qsocket_t *(*CheckNewConnections) (void); int (*QGetMessage) (qsocket_t *sock); int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data); int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data); qboolean (*CanSendMessage) (qsocket_t *sock); qboolean (*CanSendUnreliableMessage) (qsocket_t *sock); void (*Close) (qsocket_t *sock); void (*Shutdown) (void); } net_driver_t; extern net_driver_t net_drivers[]; extern const int net_numdrivers; /* Loop driver must always be registered the first */ #define IS_LOOP_DRIVER(p) ((p) == 0) extern int net_driverlevel; extern int messagesSent; extern int messagesReceived; extern int unreliableMessagesSent; extern int unreliableMessagesReceived; qsocket_t *NET_NewQSocket (void); void NET_FreeQSocket(qsocket_t *); double SetNetTime(void); #define HOSTCACHESIZE 8 typedef struct { char name[16]; char map[16]; char cname[32]; int users; int maxusers; int driver; int ldriver; struct qsockaddr addr; } hostcache_t; extern int hostCacheCount; extern hostcache_t hostcache[HOSTCACHESIZE]; typedef struct _PollProcedure { struct _PollProcedure *next; double nextTime; void (*procedure)(void *); void *arg; } PollProcedure; void SchedulePollProcedure(PollProcedure *pp, double timeOffset); #endif /* __NET_DEFS_H */ �������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cfgfile.c�������������������������������������������������������������������0000644�0000000�0000000�00000007410�12407762022�015270� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cfgfile.c -- misc reads from the config file * * Copyright (C) 2008-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static fshandle_t *cfg_file; /* =================== CFG_ReadCvars used for doing early reads from config.cfg searching the list of given cvar names for the user-set values. a temporary solution until we merge a better cvar system. the num_vars argument must be the exact number of strings in the array, otherwise I have nothing against going out of bounds. =================== */ void CFG_ReadCvars (const char **vars, int num_vars) { char buff[1024], *tmp; int i, j; if (!cfg_file || num_vars < 1) return; j = 0; do { i = 0; memset (buff, 0, sizeof(buff)); // we expect a line in the format that Cvar_WriteVariables // writes to the config file. although I'm trying to be as // much cautious as possible, if the user screws it up by // editing it, it's his fault. if (FS_fgets(buff, sizeof(buff), cfg_file)) { // remove end-of-line characters while (buff[i]) { if (buff[i] == '\r' || buff[i] == '\n') buff[i] = '\0'; // while we're here, replace tabs with spaces if (buff[i] == '\t') buff[i] = ' '; i++; } // go to the last character while (buff[i] == 0 && i > 0) i--; // remove trailing spaces while (i > 0) { if (buff[i] == ' ') { buff[i] = '\0'; i--; } else break; } // the line must end with a quotation mark if (buff[i] != '\"') continue; buff[i] = '\0'; for (i = 0; i < num_vars && vars[i]; i++) { // look for the cvar name + one space tmp = strstr(buff, va("%s ",vars[i])); if (tmp != buff) continue; // locate the first quotation mark tmp = strchr(buff, '\"'); if (tmp) { Cvar_Set (vars[i], tmp + 1); j++; break; } } } if (j == num_vars) break; } while (!FS_feof(cfg_file) && !FS_ferror(cfg_file)); FS_rewind (cfg_file); } /* =================== CFG_ReadCvarOverrides convenience function, reading the "+" command line override values of cvars in the given list. doesn't do anything with the config file. =================== */ void CFG_ReadCvarOverrides (const char **vars, int num_vars) { char buff[64]; int i, j; if (num_vars < 1) return; buff[0] = '+'; for (i = 0; i < num_vars; i++) { q_strlcpy (&buff[1], vars[i], sizeof(buff) - 1); j = COM_CheckParm(buff); if (j != 0 && j < com_argc - 1) { if (com_argv[j + 1][0] != '-' && com_argv[j + 1][0] != '+') Cvar_Set(vars[i], com_argv[j + 1]); } } } void CFG_CloseConfig (void) { if (cfg_file) { FS_fclose(cfg_file); Z_Free(cfg_file); cfg_file = NULL; } } int CFG_OpenConfig (const char *cfg_name) { FILE *f; long length; qboolean pak; CFG_CloseConfig (); length = (long) COM_FOpenFile (cfg_name, &f, NULL); pak = file_from_pak; if (length == -1) return -1; cfg_file = (fshandle_t *) Z_Malloc(sizeof(fshandle_t)); cfg_file->file = f; cfg_file->start = ftell(f); cfg_file->pos = 0; cfg_file->length = length; cfg_file->pak = pak; return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_refrag.c�����������������������������������������������������������������0000644�0000000�0000000�00000011212�13070251546�015615� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_efrag.c #include "quakedef.h" mnode_t *r_pefragtopnode; //=========================================================================== /* =============================================================================== ENTITY FRAGMENT FUNCTIONS ericw -- GLQuake only uses efrags for static entities, and they're never removed, so I trimmed out unused functionality and fields in efrag_t. Now, efrags are just a linked list for each leaf of the static entities that touch that leaf. The efrags are hunk-allocated so there is no fixed limit. This is inspired by MH's tutorial, and code from RMQEngine. http://forums.insideqc.com/viewtopic.php?t=1930 =============================================================================== */ vec3_t r_emins, r_emaxs; entity_t *r_addent; #define EXTRA_EFRAGS 128 // based on RMQEngine static efrag_t *R_GetEfrag (void) { // we could just Hunk_Alloc a single efrag_t and return it, but since // the struct is so small (2 pointers) allocate groups of them // to avoid wasting too much space on the hunk allocation headers. if (cl.free_efrags) { efrag_t *ef = cl.free_efrags; cl.free_efrags = ef->leafnext; ef->leafnext = NULL; cl.num_efrags++; return ef; } else { int i; cl.free_efrags = (efrag_t *) Hunk_AllocName (EXTRA_EFRAGS * sizeof (efrag_t), "efrags"); for (i = 0; i < EXTRA_EFRAGS - 1; i++) cl.free_efrags[i].leafnext = &cl.free_efrags[i + 1]; cl.free_efrags[i].leafnext = NULL; // call recursively to get a newly allocated free efrag return R_GetEfrag (); } } /* =================== R_SplitEntityOnNode =================== */ void R_SplitEntityOnNode (mnode_t *node) { efrag_t *ef; mplane_t *splitplane; mleaf_t *leaf; int sides; if (node->contents == CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if ( node->contents < 0) { if (!r_pefragtopnode) r_pefragtopnode = node; leaf = (mleaf_t *)node; // grab an efrag off the free list ef = R_GetEfrag(); ef->entity = r_addent; // set the leaf links ef->leafnext = leaf->efrags; leaf->efrags = ef; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (!r_pefragtopnode) r_pefragtopnode = node; } // recurse down the contacted sides if (sides & 1) R_SplitEntityOnNode (node->children[0]); if (sides & 2) R_SplitEntityOnNode (node->children[1]); } /* =========== R_CheckEfrags -- johnfitz -- check for excessive efrag count =========== */ void R_CheckEfrags (void) { if (cls.signon < 2) return; //don't spam when still parsing signon packet full of static ents if (cl.num_efrags > 640 && dev_peakstats.efrags <= 640) Con_DWarning ("%i efrags exceeds standard limit of 640.\n", cl.num_efrags); dev_stats.efrags = cl.num_efrags; dev_peakstats.efrags = q_max(cl.num_efrags, dev_peakstats.efrags); } /* =========== R_AddEfrags =========== */ void R_AddEfrags (entity_t *ent) { qmodel_t *entmodel; int i; if (!ent->model) return; r_addent = ent; r_pefragtopnode = NULL; entmodel = ent->model; for (i=0 ; i<3 ; i++) { r_emins[i] = ent->origin[i] + entmodel->mins[i]; r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; } R_SplitEntityOnNode (cl.worldmodel->nodes); ent->topnode = r_pefragtopnode; R_CheckEfrags (); //johnfitz } /* ================ R_StoreEfrags -- johnfitz -- pointless switch statement removed. ================ */ void R_StoreEfrags (efrag_t **ppefrag) { entity_t *pent; efrag_t *pefrag; while ((pefrag = *ppefrag) != NULL) { pent = pefrag->entity; if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS)) { cl_visedicts[cl_numvisedicts++] = pent; pent->visframe = r_framecount; } ppefrag = &pefrag->leafnext; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/crc.h�����������������������������������������������������������������������0000644�0000000�0000000�00000002113�13070122360�014430� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_CRC_H #define _QUAKE_CRC_H /* crc.h */ void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_Block (const byte *start, int count); //johnfitz -- texture crc #endif /* _QUAKE_CRC_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mpg123.c����������������������������������������������������������������0000644�0000000�0000000�00000012537�12471320271�015551� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * MP3 decoding support using libmpg123, loosely based on an SDL_mixer * See: http://bubu.lv/changeset/4/public/libs/SDL/generated/SDL_mixer * * Copyright (C) 2011-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MP3) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mp3.h" #include <errno.h> #include <mpg123.h> #if !defined(MPG123_API_VERSION) || (MPG123_API_VERSION < 24) #error minimum required libmpg123 version is 1.12.0 (api version 24) #endif /* MPG123_API_VERSION */ /* Private data */ typedef struct _mp3_priv_t { int handle_newed, handle_opened; mpg123_handle* handle; } mp3_priv_t; /* CALLBACK FUNCTIONS: */ /* CAREFUL: libmpg123 expects POSIX read() and lseek() behavior, * however our FS_fread() and FS_fseek() return fread() and fseek() * compatible values. */ static ssize_t mp3_read (void *f, void *buf, size_t size) { ssize_t ret = (ssize_t) FS_fread(buf, 1, size, (fshandle_t *)f); if (ret == 0 && errno != 0) ret = -1; return ret; } static off_t mp3_seek (void *f, off_t offset, int whence) { if (f == NULL) return (-1); if (FS_fseek((fshandle_t *)f, (long) offset, whence) < 0) return (off_t)-1; return (off_t) FS_ftell((fshandle_t *)f); } static qboolean S_MP3_CodecInitialize (void) { if (!mp3_codec.initialized) { if (mpg123_init() != MPG123_OK) { Con_Printf ("Could not initialize mpg123\n"); return false; } mp3_codec.initialized = true; return true; } return true; } static void S_MP3_CodecShutdown (void) { if (mp3_codec.initialized) { mp3_codec.initialized = false; mpg123_exit(); } } static qboolean S_MP3_CodecOpenStream (snd_stream_t *stream) { long rate = 0; int encoding = 0, channels = 0; mp3_priv_t *priv = NULL; stream->priv = Z_Malloc(sizeof(mp3_priv_t)); priv = (mp3_priv_t *) stream->priv; priv->handle = mpg123_new(NULL, NULL); if (priv->handle == NULL) { Con_Printf("Unable to allocate mpg123 handle\n"); goto _fail; } priv->handle_newed = 1; if (mpg123_replace_reader_handle(priv->handle, mp3_read, mp3_seek, NULL) != MPG123_OK || mpg123_open_handle(priv->handle, &stream->fh) != MPG123_OK) { Con_Printf("Unable to open mpg123 handle\n"); goto _fail; } priv->handle_opened = 1; if (mpg123_getformat(priv->handle, &rate, &channels, &encoding) != MPG123_OK) { Con_Printf("Unable to retrieve mpg123 format for %s\n", stream->name); goto _fail; } switch (channels) { case MPG123_MONO: stream->info.channels = 1; break; case MPG123_STEREO: stream->info.channels = 2; break; default: Con_Printf("Unsupported number of channels %d in %s\n", channels, stream->name); goto _fail; } stream->info.rate = rate; switch (encoding) { case MPG123_ENC_UNSIGNED_8: stream->info.bits = 8; stream->info.width = 1; break; case MPG123_ENC_SIGNED_8: /* unsupported: force mpg123 to convert */ stream->info.bits = 8; stream->info.width = 1; encoding = MPG123_ENC_UNSIGNED_8; break; case MPG123_ENC_SIGNED_16: stream->info.bits = 16; stream->info.width = 2; break; case MPG123_ENC_UNSIGNED_16: default: /* unsupported: force mpg123 to convert */ stream->info.bits = 16; stream->info.width = 2; encoding = MPG123_ENC_SIGNED_16; break; } if (mpg123_format_support(priv->handle, rate, encoding) == 0) { Con_Printf("Unsupported format for %s\n", stream->name); goto _fail; } mpg123_format_none(priv->handle); mpg123_format(priv->handle, rate, channels, encoding); return true; _fail: if (priv) { if (priv->handle_opened) mpg123_close(priv->handle); if (priv->handle_newed) mpg123_delete(priv->handle); Z_Free(stream->priv); } return false; } static int S_MP3_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; size_t bytes_read = 0; int res = mpg123_read (priv->handle, (unsigned char *)buffer, (size_t)bytes, &bytes_read); switch (res) { case MPG123_DONE: Con_DPrintf("mp3 EOF\n"); case MPG123_OK: return (int)bytes_read; } return -1; /* error */ } static void S_MP3_CodecCloseStream (snd_stream_t *stream) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; mpg123_close(priv->handle); mpg123_delete(priv->handle); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_MP3_CodecRewindStream (snd_stream_t *stream) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; off_t res = mpg123_seek(priv->handle, 0, SEEK_SET); if (res >= 0) return (0); return res; } snd_codec_t mp3_codec = { CODECTYPE_MP3, false, "mp3", S_MP3_CodecInitialize, S_MP3_CodecShutdown, S_MP3_CodecOpenStream, S_MP3_CodecReadStream, S_MP3_CodecRewindStream, S_MP3_CodecCloseStream, NULL }; #endif /* USE_CODEC_MP3 */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/bgmusic.h�������������������������������������������������������������������0000644�0000000�0000000�00000002504�12113434521�015320� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Background music handling for Quakespasm (adapted from uHexen2) * Handles streaming music as raw sound samples and runs the midi driver * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _BGMUSIC_H_ #define _BGMUSIC_H_ extern qboolean bgmloop; extern cvar_t bgm_extmusic; qboolean BGM_Init (void); void BGM_Shutdown (void); void BGM_Play (const char *filename); void BGM_Stop (void); void BGM_Update (void); void BGM_Pause (void); void BGM_Resume (void); void BGM_PlayCDtrack (byte track, qboolean looping); #endif /* _BGMUSIC_H_ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/sv_phys.c�������������������������������������������������������������������0000644�0000000�0000000�00000066773�12407762022�015405� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_phys.c #include "quakedef.h" /* pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS corpses are SOLID_NOT and MOVETYPE_TOSS crates are SOLID_BBOX and MOVETYPE_TOSS walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY solid_edge items only clip against bsp models. */ cvar_t sv_friction = {"sv_friction","4",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_stopspeed = {"sv_stopspeed","100",CVAR_NONE}; cvar_t sv_gravity = {"sv_gravity","800",CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_maxvelocity = {"sv_maxvelocity","2000",CVAR_NONE}; cvar_t sv_nostep = {"sv_nostep","0",CVAR_NONE}; cvar_t sv_freezenonclients = {"sv_freezenonclients","0",CVAR_NONE}; #define MOVE_EPSILON 0.01 void SV_Physics_Toss (edict_t *ent); /* ================ SV_CheckAllEnts ================ */ void SV_CheckAllEnts (void) { int e; edict_t *check; // see if any solid entities are inside the final position check = NEXT_EDICT(sv.edicts); for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_NOCLIP) continue; if (SV_TestEntityPosition (check)) Con_Printf ("entity in invalid position\n"); } } /* ================ SV_CheckVelocity ================ */ void SV_CheckVelocity (edict_t *ent) { int i; // // bound velocity // for (i=0 ; i<3 ; i++) { if (IS_NAN(ent->v.velocity[i])) { Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); ent->v.velocity[i] = 0; } if (IS_NAN(ent->v.origin[i])) { Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); ent->v.origin[i] = 0; } if (ent->v.velocity[i] > sv_maxvelocity.value) ent->v.velocity[i] = sv_maxvelocity.value; else if (ent->v.velocity[i] < -sv_maxvelocity.value) ent->v.velocity[i] = -sv_maxvelocity.value; } } /* ============= SV_RunThink Runs thinking code if time. There is some play in the exact time the think function will be called, because it is called before any movement is done in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. ============= */ qboolean SV_RunThink (edict_t *ent) { float thinktime; float oldframe; //johnfitz int i; //johnfitz thinktime = ent->v.nextthink; if (thinktime <= 0 || thinktime > sv.time + host_frametime) return true; if (thinktime < sv.time) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. oldframe = ent->v.frame; //johnfitz ent->v.nextthink = 0; pr_global_struct->time = thinktime; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); //johnfitz -- PROTOCOL_FITZQUAKE //capture interval to nextthink here and send it to client for better //lerp timing, but only if interval is not 0.1 (which client assumes) ent->sendinterval = false; if (!ent->free && ent->v.nextthink && (ent->v.movetype == MOVETYPE_STEP || ent->v.frame != oldframe)) { i = Q_rint((ent->v.nextthink-thinktime)*255); if (i >= 0 && i < 256 && i != 25 && i != 26) //25 and 26 are close enough to 0.1 to not send ent->sendinterval = true; } //johnfitz return !ent->free; } /* ================== SV_Impact Two entities have touched, so run their touch functions ================== */ void SV_Impact (edict_t *e1, edict_t *e2) { int old_self, old_other; old_self = pr_global_struct->self; old_other = pr_global_struct->other; pr_global_struct->time = sv.time; if (e1->v.touch && e1->v.solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e1); pr_global_struct->other = EDICT_TO_PROG(e2); PR_ExecuteProgram (e1->v.touch); } if (e2->v.touch && e2->v.solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e2); pr_global_struct->other = EDICT_TO_PROG(e1); PR_ExecuteProgram (e2->v.touch); } pr_global_struct->self = old_self; pr_global_struct->other = old_other; } /* ================== ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (!normal[2]) blocked |= 2; // step backoff = DotProduct (in, normal) * overbounce; for (i=0 ; i<3 ; i++) { change = normal[i]*backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /* ============ SV_FlyMove The basic solid body movement clip that slides along multiple planes Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop If steptrace is not NULL, the trace of any vertical wall hit will be stored ============ */ #define MAX_CLIP_PLANES 5 int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (ent->v.velocity, original_velocity); VectorCopy (ent->v.velocity, primal_velocity); numplanes = 0; time_left = time; for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) { if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2]) break; for (i=0 ; i<3 ; i++) end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) { // entity is trapped in another solid VectorCopy (vec3_origin, ent->v.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, ent->v.origin); VectorCopy (ent->v.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance if (!trace.ent) Sys_Error ("SV_FlyMove: !trace.ent"); if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (trace.ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); } } if (!trace.plane.normal[2]) { blocked |= 2; // step if (steptrace) *steptrace = trace; // save for player extrafriction } // // run the impact function // SV_Impact (ent, trace.ent); if (ent->free) break; // removed by the impact function time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy (vec3_origin, ent->v.velocity); return 3; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i=0 ; i<numplanes ; i++) { ClipVelocity (original_velocity, planes[i], new_velocity, 1); for (j=0 ; j<numplanes ; j++) if (j != i) { if (DotProduct (new_velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane VectorCopy (new_velocity, ent->v.velocity); } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, ent->v.velocity); return 7; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, ent->v.velocity); VectorScale (dir, d, ent->v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (ent->v.velocity, primal_velocity) <= 0) { VectorCopy (vec3_origin, ent->v.velocity); return blocked; } } return blocked; } /* ============ SV_AddGravity ============ */ void SV_AddGravity (edict_t *ent) { float ent_gravity; eval_t *val; val = GetEdictFieldValue(ent, "gravity"); if (val && val->_float) ent_gravity = val->_float; else ent_gravity = 1.0; ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime; } /* =============================================================================== PUSHMOVE =============================================================================== */ /* ============ SV_PushEntity Does not change the entities velocity at all ============ */ trace_t SV_PushEntity (edict_t *ent, vec3_t push) { trace_t trace; vec3_t end; VectorAdd (ent->v.origin, push, end); if (ent->v.movetype == MOVETYPE_FLYMISSILE) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); else trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, true); if (trace.ent) SV_Impact (ent, trace.ent); return trace; } /* ============ SV_PushMove ============ */ void SV_PushMove (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t mins, maxs, move; vec3_t entorig, pushorig; int num_moved; edict_t **moved_edict; //johnfitz -- dynamically allocate vec3_t *moved_from; //johnfitz -- dynamically allocate int mark; //johnfitz if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) { pusher->v.ltime += movetime; return; } for (i=0 ; i<3 ; i++) { move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorCopy (pusher->v.origin, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); //johnfitz -- dynamically allocate mark = Hunk_LowMark (); moved_edict = (edict_t **) Hunk_Alloc (sv.num_edicts*sizeof(edict_t *)); moved_from = (vec3_t *) Hunk_Alloc (sv.num_edicts*sizeof(vec3_t)); //johnfitz // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if ( ! ( ((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher) ) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { pr_global_struct->self = EDICT_TO_PROG(pusher); pr_global_struct->other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i=0 ; i<num_moved ; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); SV_LinkEdict (moved_edict[i], false); } Hunk_FreeToLowMark (mark); //johnfitz return; } } Hunk_FreeToLowMark (mark); //johnfitz } /* ================ SV_Physics_Pusher ================ */ void SV_Physics_Pusher (edict_t *ent) { float thinktime; float oldltime; float movetime; oldltime = ent->v.ltime; thinktime = ent->v.nextthink; if (thinktime < ent->v.ltime + host_frametime) { movetime = thinktime - ent->v.ltime; if (movetime < 0) movetime = 0; } else movetime = host_frametime; if (movetime) { SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked } if (thinktime > oldltime && thinktime <= ent->v.ltime) { ent->v.nextthink = 0; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) return; } } /* =============================================================================== CLIENT MOVEMENT =============================================================================== */ /* ============= SV_CheckStuck This is a big hack to try and fix the rare case of getting stuck in the world clipping hull. ============= */ void SV_CheckStuck (edict_t *ent) { int i, j; int z; vec3_t org; if (!SV_TestEntityPosition(ent)) { VectorCopy (ent->v.origin, ent->v.oldorigin); return; } VectorCopy (ent->v.origin, org); VectorCopy (ent->v.oldorigin, ent->v.origin); if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } for (z=0 ; z< 18 ; z++) for (i=-1 ; i <= 1 ; i++) for (j=-1 ; j <= 1 ; j++) { ent->v.origin[0] = org[0] + i; ent->v.origin[1] = org[1] + j; ent->v.origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } } VectorCopy (org, ent->v.origin); Con_DPrintf ("player is stuck.\n"); } /* ============= SV_CheckWater ============= */ qboolean SV_CheckWater (edict_t *ent) { vec3_t point; int cont; point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; ent->v.waterlevel = 0; ent->v.watertype = CONTENTS_EMPTY; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { ent->v.watertype = cont; ent->v.waterlevel = 1; point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { ent->v.waterlevel = 2; point[2] = ent->v.origin[2] + ent->v.view_ofs[2]; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) ent->v.waterlevel = 3; } } return ent->v.waterlevel > 1; } /* ============ SV_WallFriction ============ */ void SV_WallFriction (edict_t *ent, trace_t *trace) { vec3_t forward, right, up; float d, i; vec3_t into, side; AngleVectors (ent->v.v_angle, forward, right, up); d = DotProduct (trace->plane.normal, forward); d += 0.5; if (d >= 0) return; // cut the tangential velocity i = DotProduct (trace->plane.normal, ent->v.velocity); VectorScale (trace->plane.normal, i, into); VectorSubtract (ent->v.velocity, into, side); ent->v.velocity[0] = side[0] * (1 + d); ent->v.velocity[1] = side[1] * (1 + d); } /* ===================== SV_TryUnstick Player has come to a dead stop, possibly due to the problem with limited float precision at some angle joins in the BSP hull. Try fixing by pushing one pixel in each direction. This is a hack, but in the interest of good gameplay... ====================== */ int SV_TryUnstick (edict_t *ent, vec3_t oldvel) { int i; vec3_t oldorg; vec3_t dir; int clip; trace_t steptrace; VectorCopy (ent->v.origin, oldorg); VectorCopy (vec3_origin, dir); for (i=0 ; i<8 ; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir[0] = 2; dir[1] = 0; break; case 1: dir[0] = 0; dir[1] = 2; break; case 2: dir[0] = -2; dir[1] = 0; break; case 3: dir[0] = 0; dir[1] = -2; break; case 4: dir[0] = 2; dir[1] = 2; break; case 5: dir[0] = -2; dir[1] = 2; break; case 6: dir[0] = 2; dir[1] = -2; break; case 7: dir[0] = -2; dir[1] = -2; break; } SV_PushEntity (ent, dir); // retry the original move ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, 0.1, &steptrace); if ( fabs(oldorg[1] - ent->v.origin[1]) > 4 || fabs(oldorg[0] - ent->v.origin[0]) > 4 ) { //Con_DPrintf ("unstuck!\n"); return clip; } // go back to the original pos and try again VectorCopy (oldorg, ent->v.origin); } VectorCopy (vec3_origin, ent->v.velocity); return 7; // still not moving } /* ===================== SV_WalkMove Only used by players ====================== */ #define STEPSIZE 18 void SV_WalkMove (edict_t *ent) { vec3_t upmove, downmove; vec3_t oldorg, oldvel; vec3_t nosteporg, nostepvel; int clip; int oldonground; trace_t steptrace, downtrace; // // do a regular slide move unless it looks like you ran into a step // oldonground = (int)ent->v.flags & FL_ONGROUND; ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; VectorCopy (ent->v.origin, oldorg); VectorCopy (ent->v.velocity, oldvel); clip = SV_FlyMove (ent, host_frametime, &steptrace); if ( !(clip & 2) ) return; // move didn't block on a step if (!oldonground && ent->v.waterlevel == 0) return; // don't stair up while jumping if (ent->v.movetype != MOVETYPE_WALK) return; // gibbed by a trigger if (sv_nostep.value) return; if ( (int)sv_player->v.flags & FL_WATERJUMP ) return; VectorCopy (ent->v.origin, nosteporg); VectorCopy (ent->v.velocity, nostepvel); // // try moving up and forward to go up a step // VectorCopy (oldorg, ent->v.origin); // back to start pos VectorCopy (vec3_origin, upmove); VectorCopy (vec3_origin, downmove); upmove[2] = STEPSIZE; downmove[2] = -STEPSIZE + oldvel[2]*host_frametime; // move up SV_PushEntity (ent, upmove); // FIXME: don't link? // move forward ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, host_frametime, &steptrace); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip) { if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 ) { // stepping up didn't make any progress clip = SV_TryUnstick (ent, oldvel); } } // extra friction based on view angle if ( clip & 2 ) SV_WallFriction (ent, &steptrace); // move down downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link? if (downtrace.plane.normal[2] > 0.7) { if (ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(downtrace.ent); } } else { // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb VectorCopy (nosteporg, ent->v.origin); VectorCopy (nostepvel, ent->v.velocity); } } /* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: if (!SV_RunThink (ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // // call standard player post-think // SV_LinkEdict (ent, true); pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPostThink); } //============================================================================ /* ============= SV_Physics_None Non moving objects can only think ============= */ void SV_Physics_None (edict_t *ent) { // regular thinking SV_RunThink (ent); } /* ============= SV_Physics_Noclip A moving object that doesn't obey physics ============= */ void SV_Physics_Noclip (edict_t *ent) { // regular thinking if (!SV_RunThink (ent)) return; VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); SV_LinkEdict (ent, false); } /* ============================================================================== TOSS / BOUNCE ============================================================================== */ /* ============= SV_CheckWaterTransition ============= */ void SV_CheckWaterTransition (edict_t *ent) { int cont; cont = SV_PointContents (ent->v.origin); if (!ent->v.watertype) { // just spawned here ent->v.watertype = cont; ent->v.waterlevel = 1; return; } if (cont <= CONTENTS_WATER) { if (ent->v.watertype == CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } ent->v.watertype = cont; ent->v.waterlevel = 1; } else { if (ent->v.watertype != CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = cont; } } /* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ void SV_Physics_Toss (edict_t *ent) { trace_t trace; vec3_t move; float backoff; // regular thinking if (!SV_RunThink (ent)) return; // if onground, return without moving if ( ((int)ent->v.flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent); // move angles VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); // move origin VectorScale (ent->v.velocity, host_frametime, move); trace = SV_PushEntity (ent, move); if (trace.fraction == 1) return; if (ent->free) return; if (ent->v.movetype == MOVETYPE_BOUNCE) backoff = 1.5; else backoff = 1; ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); // stop if on ground if (trace.plane.normal[2] > 0.7) { if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); VectorCopy (vec3_origin, ent->v.velocity); VectorCopy (vec3_origin, ent->v.avelocity); } } // check for in water SV_CheckWaterTransition (ent); } /* =============================================================================== STEPPING MOVEMENT =============================================================================== */ /* ============= SV_Physics_Step Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. This is also used for objects that have become still on the ground, but will fall if the floor is pulled out from under them. ============= */ void SV_Physics_Step (edict_t *ent) { qboolean hitsound; // freefall if not onground if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; else hitsound = false; SV_AddGravity (ent); SV_CheckVelocity (ent); SV_FlyMove (ent, host_frametime, NULL); SV_LinkEdict (ent, true); if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground { if (hitsound) SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); } } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } //============================================================================ /* ================ SV_Physics ================ */ void SV_Physics (void) { int i; int entity_cap; // For sv_freezenonclients edict_t *ent; // let the progs know that a new frame has started pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; PR_ExecuteProgram (pr_global_struct->StartFrame); //SV_CheckAllEnts (); // // treat each object in turn // ent = sv.edicts; if (sv_freezenonclients.value) entity_cap = svs.maxclients + 1; // Only run physics on clients and the world else entity_cap = sv.num_edicts; //for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) for (i=0 ; i<entity_cap ; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; if (pr_global_struct->force_retouch) { SV_LinkEdict (ent, true); // force retouch even for stationary } if (i > 0 && i <= svs.maxclients) SV_Physics_Client (ent, i); else if (ent->v.movetype == MOVETYPE_PUSH) SV_Physics_Pusher (ent); else if (ent->v.movetype == MOVETYPE_NONE) SV_Physics_None (ent); else if (ent->v.movetype == MOVETYPE_NOCLIP) SV_Physics_Noclip (ent); else if (ent->v.movetype == MOVETYPE_STEP) SV_Physics_Step (ent); else if (ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE || ent->v.movetype == MOVETYPE_FLY || ent->v.movetype == MOVETYPE_FLYMISSILE) SV_Physics_Toss (ent); else Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); } if (pr_global_struct->force_retouch) pr_global_struct->force_retouch--; if (!sv_freezenonclients.value) sv.time += host_frametime; } �����quakespasm-0.93.0/Quake/wsaerror.h������������������������������������������������������������������0000644�0000000�0000000�00000011526�11407642431�015545� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� /* strings for winsock error codes. * from online references, such as * http://aluigi.org/mytoolz/winerr.h * http://www.winsock-error.com or * http://www.sockets.com/err_lst1.htm */ #ifndef __wsaerr_static #define __wsaerr_static static #endif /* static */ __wsaerr_static const char *__WSAE_StrError (int err) { switch (err) { case 0: return "No error"; case WSAEINTR: return "Interrupted system call"; /* 10004 */ case WSAEBADF: return "Bad file number"; /* 10009 */ case WSAEACCES: return "Permission denied"; /* 10013 */ case WSAEFAULT: return "Bad address"; /* 10014 */ case WSAEINVAL: return "Invalid argument (not bind)"; /* 10022 */ case WSAEMFILE: return "Too many open files"; /* 10024 */ case WSAEWOULDBLOCK: return "Operation would block"; /* 10035 */ case WSAEINPROGRESS: return "Operation now in progress"; /* 10036 */ case WSAEALREADY: return "Operation already in progress"; /* 10037 */ case WSAENOTSOCK: return "Socket operation on non-socket"; /* 10038 */ case WSAEDESTADDRREQ: return "Destination address required"; /* 10039 */ case WSAEMSGSIZE: return "Message too long"; /* 10040 */ case WSAEPROTOTYPE: return "Protocol wrong type for socket"; /* 10041 */ case WSAENOPROTOOPT: return "Bad protocol option"; /* 10042 */ case WSAEPROTONOSUPPORT: return "Protocol not supported"; /* 10043 */ case WSAESOCKTNOSUPPORT: return "Socket type not supported"; /* 10044 */ case WSAEOPNOTSUPP: return "Operation not supported on socket"; /* 10045 */ case WSAEPFNOSUPPORT: return "Protocol family not supported"; /* 10046 */ case WSAEAFNOSUPPORT: return "Address family not supported by protocol family"; /* 10047 */ case WSAEADDRINUSE: return "Address already in use"; /* 10048 */ case WSAEADDRNOTAVAIL: return "Can't assign requested address"; /* 10049 */ case WSAENETDOWN: return "Network is down"; /* 10050 */ case WSAENETUNREACH: return "Network is unreachable"; /* 10051 */ case WSAENETRESET: return "Net dropped connection or reset"; /* 10052 */ case WSAECONNABORTED: return "Software caused connection abort"; /* 10053 */ case WSAECONNRESET: return "Connection reset by peer"; /* 10054 */ case WSAENOBUFS: return "No buffer space available"; /* 10055 */ case WSAEISCONN: return "Socket is already connected"; /* 10056 */ case WSAENOTCONN: return "Socket is not connected"; /* 10057 */ case WSAESHUTDOWN: return "Can't send after socket shutdown"; /* 10058 */ case WSAETOOMANYREFS: return "Too many references, can't splice"; /* 10059 */ case WSAETIMEDOUT: return "Connection timed out"; /* 10060 */ case WSAECONNREFUSED: return "Connection refused"; /* 10061 */ case WSAELOOP: return "Too many levels of symbolic links"; /* 10062 */ case WSAENAMETOOLONG: return "File name too long"; /* 10063 */ case WSAEHOSTDOWN: return "Host is down"; /* 10064 */ case WSAEHOSTUNREACH: return "No Route to Host"; /* 10065 */ case WSAENOTEMPTY: return "Directory not empty"; /* 10066 */ case WSAEPROCLIM: return "Too many processes"; /* 10067 */ case WSAEUSERS: return "Too many users"; /* 10068 */ case WSAEDQUOT: return "Disc Quota Exceeded"; /* 10069 */ case WSAESTALE: return "Stale NFS file handle"; /* 10070 */ case WSAEREMOTE: return "Too many levels of remote in path"; /* 10071 */ case WSAEDISCON: return "Graceful shutdown in progress"; /* 10101 */ case WSASYSNOTREADY: return "Network SubSystem is unavailable"; /* 10091 */ case WSAVERNOTSUPPORTED: return "WINSOCK DLL Version out of range"; /* 10092 */ case WSANOTINITIALISED: return "Successful WSASTARTUP not yet performed"; /* 10093 */ case WSAHOST_NOT_FOUND: return "Authoritative answer: Host not found"; /* 11001 */ case WSATRY_AGAIN: return "Non-Authoritative: Host not found or SERVERFAIL"; /* 11002 */ case WSANO_RECOVERY: return "Non-Recoverable errors, FORMERR, REFUSED, NOTIMP"; /* 11003 */ case WSANO_DATA: return "Valid name, no data record of requested type"; /* 11004 */ case WSAENOMORE: return "10102: No more results"; /* 10102 */ case WSAECANCELLED: return "10103: Call has been canceled"; /* 10103 */ case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid"; /* 10104 */ case WSAEINVALIDPROVIDER: return "Service provider is invalid"; /* 10105 */ case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize"; /* 10106 */ case WSASYSCALLFAILURE: return "System call failure"; /* 10107 */ case WSASERVICE_NOT_FOUND: return "Service not found"; /* 10108 */ case WSATYPE_NOT_FOUND: return "Class type not found"; /* 10109 */ case WSA_E_NO_MORE: return "10110: No more results"; /* 10110 */ case WSA_E_CANCELLED: return "10111: Call was canceled"; /* 10111 */ case WSAEREFUSED: return "Database query was refused"; /* 10112 */ default: { static char _err_unknown[64]; sprintf(_err_unknown, "Unknown WSAE error (%d)", err); return _err_unknown; } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/image.h���������������������������������������������������������������������0000644�0000000�0000000�00000002751�13142571430�014761� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GL_IMAGE_H #define GL_IMAGE_H //image.h -- image reading / writing //be sure to free the hunk after using these loading functions byte *Image_LoadTGA (FILE *f, int *width, int *height); byte *Image_LoadPCX (FILE *f, int *width, int *height); byte *Image_LoadImage (const char *name, int *width, int *height); qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown); qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown); qboolean Image_WriteJPG (const char *name, byte *data, int width, int height, int bpp, int quality, qboolean upsidedown); #endif /* GL_IMAGE_H */ �����������������������quakespasm-0.93.0/Quake/snd_wave.c������������������������������������������������������������������0000644�0000000�0000000�00000013012�12220541170�015462� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * WAV streaming music support. Adapted from ioquake3 with changes. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com> * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_WAVE) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_wave.h" /* ================= FGetLittleLong ================= */ static int FGetLittleLong (FILE *f) { int v; fread(&v, 1, sizeof(v), f); return LittleLong(v); } /* ================= FGetLittleShort ================= */ static short FGetLittleShort(FILE *f) { short v; fread(&v, 1, sizeof(v), f); return LittleShort(v); } /* ================= WAV_ReadChunkInfo ================= */ static int WAV_ReadChunkInfo(FILE *f, char *name) { int len, r; name[4] = 0; r = fread(name, 1, 4, f); if (r != 4) return -1; len = FGetLittleLong(f); if (len < 0) { Con_Printf("WAV: Negative chunk length\n"); return -1; } return len; } /* ================= WAV_FindRIFFChunk Returns the length of the data in the chunk, or -1 if not found ================= */ static int WAV_FindRIFFChunk(FILE *f, const char *chunk) { char name[5]; int len; while ((len = WAV_ReadChunkInfo(f, name)) >= 0) { /* If this is the right chunk, return */ if (!strncmp(name, chunk, 4)) return len; len = ((len + 1) & ~1); /* pad by 2 . */ /* Not the right chunk - skip it */ fseek(f, len, SEEK_CUR); } return -1; } /* ================= WAV_ReadRIFFHeader ================= */ static qboolean WAV_ReadRIFFHeader(const char *name, FILE *file, snd_info_t *info) { char dump[16]; int wav_format; int fmtlen = 0; if (fread(dump, 1, 12, file) < 12 || strncmp(dump, "RIFF", 4) != 0 || strncmp(&dump[8], "WAVE", 4) != 0) { Con_Printf("%s is missing RIFF/WAVE chunks\n", name); return false; } /* Scan for the format chunk */ if ((fmtlen = WAV_FindRIFFChunk(file, "fmt ")) < 0) { Con_Printf("%s is missing fmt chunk\n", name); return false; } /* Save the parameters */ wav_format = FGetLittleShort(file); if (wav_format != WAV_FORMAT_PCM) { Con_Printf("%s is not Microsoft PCM format\n", name); return false; } info->channels = FGetLittleShort(file); info->rate = FGetLittleLong(file); FGetLittleLong(file); FGetLittleShort(file); info->bits = FGetLittleShort(file); if (info->bits != 8 && info->bits != 16) { Con_Printf("%s is not 8 or 16 bit\n", name); return false; } info->width = info->bits / 8; info->dataofs = 0; /* Skip the rest of the format chunk if required */ if (fmtlen > 16) { fmtlen -= 16; fseek(file, fmtlen, SEEK_CUR); } /* Scan for the data chunk */ if ((info->size = WAV_FindRIFFChunk(file, "data")) < 0) { Con_Printf("%s is missing data chunk\n", name); return false; } if (info->channels != 1 && info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", info->channels, name); return false; } info->samples = (info->size / info->width) / info->channels; if (info->samples == 0) { Con_Printf("%s has zero samples\n", name); return false; } return true; } /* ================= S_WAV_CodecOpenStream ================= */ static qboolean S_WAV_CodecOpenStream(snd_stream_t *stream) { long start = stream->fh.start; /* Read the RIFF header */ /* The file reads are sequential, therefore no need * for the FS_*() functions: We will manipulate the * file by ourselves from now on. */ if (!WAV_ReadRIFFHeader(stream->name, stream->fh.file, &stream->info)) return false; stream->fh.start = ftell(stream->fh.file); /* reset to data position */ if (stream->fh.start - start + stream->info.size > stream->fh.length) { Con_Printf("%s data size mismatch\n", stream->name); return false; } return true; } /* ================= S_WAV_CodecReadStream ================= */ int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer) { int remaining = stream->info.size - stream->fh.pos; int i, samples; if (remaining <= 0) return 0; if (bytes > remaining) bytes = remaining; stream->fh.pos += bytes; fread(buffer, 1, bytes, stream->fh.file); if (stream->info.width == 2) { samples = bytes / 2; for (i = 0; i < samples; i++) ((short *)buffer)[i] = LittleShort( ((short *)buffer)[i] ); } return bytes; } static void S_WAV_CodecCloseStream (snd_stream_t *stream) { S_CodecUtilClose(&stream); } static int S_WAV_CodecRewindStream (snd_stream_t *stream) { FS_rewind(&stream->fh); return 0; } static qboolean S_WAV_CodecInitialize (void) { return true; } static void S_WAV_CodecShutdown (void) { } snd_codec_t wav_codec = { CODECTYPE_WAVE, true, /* always available. */ "wav", S_WAV_CodecInitialize, S_WAV_CodecShutdown, S_WAV_CodecOpenStream, S_WAV_CodecReadStream, S_WAV_CodecRewindStream, S_WAV_CodecCloseStream, NULL }; #endif /* USE_CODEC_WAVE */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/r_world.c�������������������������������������������������������������������0000644�0000000�0000000�00000075642�13176243376�015367� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_world.c: world model rendering #include "quakedef.h" extern cvar_t gl_fullbrights, r_drawflat, gl_overbright, r_oldwater, r_oldskyleaf, r_showtris; //johnfitz extern glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; byte *SV_FatPVS (vec3_t org, qmodel_t *worldmodel); int vis_changed; //if true, force pvs to be refreshed //============================================================================== // // SETUP CHAINS // //============================================================================== /* ================ R_ClearTextureChains -- ericw clears texture chains for all textures used by the given model, and also clears the lightmap chains ================ */ void R_ClearTextureChains (qmodel_t *mod, texchain_t chain) { int i; // set all chains to null for (i=0 ; i<mod->numtextures ; i++) if (mod->textures[i]) mod->textures[i]->texturechains[chain] = NULL; // clear lightmap chains memset (lightmap_polys, 0, sizeof(lightmap_polys)); } /* ================ R_ChainSurface -- ericw -- adds the given surface to its texture chain ================ */ void R_ChainSurface (msurface_t *surf, texchain_t chain) { surf->texturechain = surf->texinfo->texture->texturechains[chain]; surf->texinfo->texture->texturechains[chain] = surf; } /* =============== R_MarkSurfaces -- johnfitz -- mark surfaces based on PVS and rebuild texture chains =============== */ void R_MarkSurfaces (void) { byte *vis; mleaf_t *leaf; mnode_t *node; msurface_t *surf, **mark; int i, j; qboolean nearwaterportal; // clear lightmap chains memset (lightmap_polys, 0, sizeof(lightmap_polys)); // check this leaf for water portals // TODO: loop through all water surfs and use distance to leaf cullbox nearwaterportal = false; for (i=0, mark = r_viewleaf->firstmarksurface; i < r_viewleaf->nummarksurfaces; i++, mark++) if ((*mark)->flags & SURF_DRAWTURB) nearwaterportal = true; // choose vis data if (r_novis.value || r_viewleaf->contents == CONTENTS_SOLID || r_viewleaf->contents == CONTENTS_SKY) vis = Mod_NoVisPVS (cl.worldmodel); else if (nearwaterportal) vis = SV_FatPVS (r_origin, cl.worldmodel); else vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); // if surface chains don't need regenerating, just add static entities and return if (r_oldviewleaf == r_viewleaf && !vis_changed && !nearwaterportal) { leaf = &cl.worldmodel->leafs[1]; for (i=0 ; i<cl.worldmodel->numleafs ; i++, leaf++) if (vis[i>>3] & (1<<(i&7))) if (leaf->efrags) R_StoreEfrags (&leaf->efrags); return; } vis_changed = false; r_visframecount++; r_oldviewleaf = r_viewleaf; // iterate through leaves, marking surfaces leaf = &cl.worldmodel->leafs[1]; for (i=0 ; i<cl.worldmodel->numleafs ; i++, leaf++) { if (vis[i>>3] & (1<<(i&7))) { if (r_oldskyleaf.value || leaf->contents != CONTENTS_SKY) for (j=0, mark = leaf->firstmarksurface; j<leaf->nummarksurfaces; j++, mark++) (*mark)->visframe = r_visframecount; // add static models if (leaf->efrags) R_StoreEfrags (&leaf->efrags); } } // set all chains to null for (i=0 ; i<cl.worldmodel->numtextures ; i++) if (cl.worldmodel->textures[i]) cl.worldmodel->textures[i]->texturechains[chain_world] = NULL; // rebuild chains #if 1 //iterate through surfaces one node at a time to rebuild chains //need to do it this way if we want to work with tyrann's skip removal tool //becuase his tool doesn't actually remove the surfaces from the bsp surfaces lump //nor does it remove references to them in each leaf's marksurfaces list for (i=0, node = cl.worldmodel->nodes ; i<cl.worldmodel->numnodes ; i++, node++) for (j=0, surf=&cl.worldmodel->surfaces[node->firstsurface] ; j<node->numsurfaces ; j++, surf++) if (surf->visframe == r_visframecount) { R_ChainSurface(surf, chain_world); } #else //the old way surf = &cl.worldmodel->surfaces[cl.worldmodel->firstmodelsurface]; for (i=0 ; i<cl.worldmodel->nummodelsurfaces ; i++, surf++) { if (surf->visframe == r_visframecount) { R_ChainSurface(surf, chain_world); } } #endif } /* ================ R_BackFaceCull -- johnfitz -- returns true if the surface is facing away from vieworg ================ */ qboolean R_BackFaceCull (msurface_t *surf) { double dot; switch (surf->plane->type) { case PLANE_X: dot = r_refdef.vieworg[0] - surf->plane->dist; break; case PLANE_Y: dot = r_refdef.vieworg[1] - surf->plane->dist; break; case PLANE_Z: dot = r_refdef.vieworg[2] - surf->plane->dist; break; default: dot = DotProduct (r_refdef.vieworg, surf->plane->normal) - surf->plane->dist; break; } if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) return true; return false; } /* ================ R_CullSurfaces -- johnfitz ================ */ void R_CullSurfaces (void) { msurface_t *s; int i; texture_t *t; if (!r_drawworld_cheatsafe) return; // ericw -- instead of testing (s->visframe == r_visframecount) on all world // surfaces, use the chained surfaces, which is exactly the same set of sufaces for (i=0 ; i<cl.worldmodel->numtextures ; i++) { t = cl.worldmodel->textures[i]; if (!t || !t->texturechains[chain_world]) continue; for (s = t->texturechains[chain_world]; s; s = s->texturechain) { if (R_CullBox(s->mins, s->maxs) || R_BackFaceCull (s)) s->culled = true; else { s->culled = false; rs_brushpolys++; //count wpolys here if (s->texinfo->texture->warpimage) s->texinfo->texture->update_warp = true; } } } } /* ================ R_BuildLightmapChains -- johnfitz -- used for r_lightmap 1 ericw -- now always used at the start of R_DrawTextureChains for the mh dynamic lighting speedup ================ */ void R_BuildLightmapChains (qmodel_t *model, texchain_t chain) { texture_t *t; msurface_t *s; int i; // clear lightmap chains (already done in r_marksurfaces, but clearing them here to be safe becuase of r_stereo) memset (lightmap_polys, 0, sizeof(lightmap_polys)); // now rebuild them for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain]) continue; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) R_RenderDynamicLightmaps (s); } } //============================================================================== // // DRAW CHAINS // //============================================================================== /* ============= R_BeginTransparentDrawing -- ericw ============= */ static void R_BeginTransparentDrawing (float entalpha) { if (entalpha < 1.0f) { glDepthMask (GL_FALSE); glEnable (GL_BLEND); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f (1,1,1,entalpha); } } /* ============= R_EndTransparentDrawing -- ericw ============= */ static void R_EndTransparentDrawing (float entalpha) { if (entalpha < 1.0f) { glDepthMask (GL_TRUE); glDisable (GL_BLEND); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f (1, 1, 1); } } /* ================ R_DrawTextureChains_ShowTris -- johnfitz ================ */ void R_DrawTextureChains_ShowTris (qmodel_t *model, texchain_t chain) { int i; msurface_t *s; texture_t *t; glpoly_t *p; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t) continue; if (r_oldwater.value && t->texturechains[chain] && (t->texturechains[chain]->flags & SURF_DRAWTURB)) { for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) for (p = s->polys->next; p; p = p->next) { DrawGLTriangleFan (p); } } else { for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { DrawGLTriangleFan (s->polys); } } } } /* ================ R_DrawTextureChains_Drawflat -- johnfitz ================ */ void R_DrawTextureChains_Drawflat (qmodel_t *model, texchain_t chain) { int i; msurface_t *s; texture_t *t; glpoly_t *p; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t) continue; if (r_oldwater.value && t->texturechains[chain] && (t->texturechains[chain]->flags & SURF_DRAWTURB)) { for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) for (p = s->polys->next; p; p = p->next) { srand((unsigned int) (uintptr_t) p); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (p); rs_brushpasses++; } } else { for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { srand((unsigned int) (uintptr_t) s->polys); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (s->polys); rs_brushpasses++; } } } glColor3f (1,1,1); srand ((int) (cl.time * 1000)); } /* ================ R_DrawTextureChains_Glow -- johnfitz ================ */ void R_DrawTextureChains_Glow (qmodel_t *model, entity_t *ent, texchain_t chain) { int i; msurface_t *s; texture_t *t; gltexture_t *glt; qboolean bound; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || !(glt = R_TextureAnimation(t, ent != NULL ? ent->frame : 0)->fullbright)) continue; bound = false; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { GL_Bind (glt); bound = true; } DrawGLPoly (s->polys); rs_brushpasses++; } } } //============================================================================== // // VBO SUPPORT // //============================================================================== static unsigned int R_NumTriangleIndicesForSurf (msurface_t *s) { return 3 * (s->numedges - 2); } /* ================ R_TriangleIndicesForSurf Writes out the triangle indices needed to draw s as a triangle list. The number of indices it will write is given by R_NumTriangleIndicesForSurf. ================ */ static void R_TriangleIndicesForSurf (msurface_t *s, unsigned int *dest) { int i; for (i=2; i<s->numedges; i++) { *dest++ = s->vbo_firstvert; *dest++ = s->vbo_firstvert + i - 1; *dest++ = s->vbo_firstvert + i; } } #define MAX_BATCH_SIZE 4096 static unsigned int vbo_indices[MAX_BATCH_SIZE]; static unsigned int num_vbo_indices; /* ================ R_ClearBatch ================ */ static void R_ClearBatch () { num_vbo_indices = 0; } /* ================ R_FlushBatch Draw the current batch if non-empty and clears it, ready for more R_BatchSurface calls. ================ */ static void R_FlushBatch () { if (num_vbo_indices > 0) { glDrawElements (GL_TRIANGLES, num_vbo_indices, GL_UNSIGNED_INT, vbo_indices); num_vbo_indices = 0; } } /* ================ R_BatchSurface Add the surface to the current batch, or just draw it immediately if we're not using VBOs. ================ */ static void R_BatchSurface (msurface_t *s) { int num_surf_indices; num_surf_indices = R_NumTriangleIndicesForSurf (s); if (num_vbo_indices + num_surf_indices > MAX_BATCH_SIZE) R_FlushBatch(); R_TriangleIndicesForSurf (s, &vbo_indices[num_vbo_indices]); num_vbo_indices += num_surf_indices; } /* ================ R_DrawTextureChains_Multitexture -- johnfitz ================ */ void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_t chain) { int i, j; msurface_t *s; texture_t *t; float *v; qboolean bound; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE)) continue; bound = false; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture); if (t->texturechains[chain]->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip alpha test back on GL_EnableMultitexture(); // selects TEXTURE1 bound = true; } GL_Bind (lightmap_textures[s->lightmaptexturenum]); glBegin(GL_POLYGON); v = s->polys->verts[0]; for (j=0 ; j<s->polys->numverts ; j++, v+= VERTEXSIZE) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv (v); } glEnd (); rs_brushpasses++; } GL_DisableMultitexture(); // selects TEXTURE0 if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off } } /* ================ R_DrawTextureChains_NoTexture -- johnfitz draws surfs whose textures were missing from the BSP ================ */ void R_DrawTextureChains_NoTexture (qmodel_t *model, texchain_t chain) { int i; msurface_t *s; texture_t *t; qboolean bound; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_NOTEXTURE)) continue; bound = false; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { GL_Bind (t->gltexture); bound = true; } DrawGLPoly (s->polys); rs_brushpasses++; } } } /* ================ R_DrawTextureChains_TextureOnly -- johnfitz ================ */ void R_DrawTextureChains_TextureOnly (qmodel_t *model, entity_t *ent, texchain_t chain) { int i; msurface_t *s; texture_t *t; qboolean bound; for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) continue; bound = false; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture); if (t->texturechains[chain]->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip alpha test back on bound = true; } DrawGLPoly (s->polys); rs_brushpasses++; } if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off } } /* ================ GL_WaterAlphaForEntitySurface -- ericw Returns the water alpha to use for the entity and surface combination. ================ */ float GL_WaterAlphaForEntitySurface (entity_t *ent, msurface_t *s) { float entalpha; if (ent == NULL || ent->alpha == ENTALPHA_DEFAULT) entalpha = GL_WaterAlphaForSurface(s); else entalpha = ENTALPHA_DECODE(ent->alpha); return entalpha; } /* ================ R_DrawTextureChains_Water -- johnfitz ================ */ void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain) { int i; msurface_t *s; texture_t *t; glpoly_t *p; qboolean bound; float entalpha; if (r_drawflat_cheatsafe || r_lightmap_cheatsafe) // ericw -- !r_drawworld_cheatsafe check moved to R_DrawWorld_Water () return; if (r_oldwater.value) { for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTURB)) continue; bound = false; entalpha = 1.0f; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { entalpha = GL_WaterAlphaForEntitySurface (ent, s); R_BeginTransparentDrawing (entalpha); GL_Bind (t->gltexture); bound = true; } for (p = s->polys->next; p; p = p->next) { DrawWaterPoly (p); rs_brushpasses++; } } R_EndTransparentDrawing (entalpha); } } else { for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTURB)) continue; bound = false; entalpha = 1.0f; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { entalpha = GL_WaterAlphaForEntitySurface (ent, s); R_BeginTransparentDrawing (entalpha); GL_Bind (t->warpimage); if (model != cl.worldmodel) { // ericw -- this is copied from R_DrawSequentialPoly. // If the poly is not part of the world we have to // set this flag t->update_warp = true; // FIXME: one frame too late! } bound = true; } DrawGLPoly (s->polys); rs_brushpasses++; } R_EndTransparentDrawing (entalpha); } } } /* ================ R_DrawTextureChains_White -- johnfitz -- draw sky and water as white polys when r_lightmap is 1 ================ */ void R_DrawTextureChains_White (qmodel_t *model, texchain_t chain) { int i; msurface_t *s; texture_t *t; glDisable (GL_TEXTURE_2D); for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTILED)) continue; for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { DrawGLPoly (s->polys); rs_brushpasses++; } } glEnable (GL_TEXTURE_2D); } /* ================ R_DrawLightmapChains -- johnfitz -- R_BlendLightmaps stripped down to almost nothing ================ */ void R_DrawLightmapChains (void) { int i, j; glpoly_t *p; float *v; for (i=0 ; i<MAX_LIGHTMAPS ; i++) { if (!lightmap_polys[i]) continue; GL_Bind (lightmap_textures[i]); for (p = lightmap_polys[i]; p; p=p->chain) { glBegin (GL_POLYGON); v = p->verts[0]; for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); rs_brushpasses++; } } } static GLuint r_world_program; // uniforms used in vert shader // uniforms used in frag shader static GLuint texLoc; static GLuint LMTexLoc; static GLuint fullbrightTexLoc; static GLuint useFullbrightTexLoc; static GLuint useOverbrightLoc; static GLuint useAlphaTestLoc; static GLuint alphaLoc; #define vertAttrIndex 0 #define texCoordsAttrIndex 1 #define LMCoordsAttrIndex 2 /* ============= GLWorld_CreateShaders ============= */ void GLWorld_CreateShaders (void) { const glsl_attrib_binding_t bindings[] = { { "Vert", vertAttrIndex }, { "TexCoords", texCoordsAttrIndex }, { "LMCoords", LMCoordsAttrIndex } }; const GLchar *vertSource = \ "#version 110\n" "\n" "attribute vec3 Vert;\n" "attribute vec2 TexCoords;\n" "attribute vec2 LMCoords;\n" "\n" "void main()\n" "{\n" " gl_TexCoord[0] = vec4(TexCoords, 0.0, 0.0);\n" " gl_TexCoord[1] = vec4(LMCoords, 0.0, 0.0);\n" " gl_Position = gl_ModelViewProjectionMatrix * vec4(Vert, 1.0);\n" " // fog\n" " vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(Vert, 1.0));\n" " gl_FogFragCoord = abs(ecPosition.z);\n" "}\n"; const GLchar *fragSource = \ "#version 110\n" "\n" "uniform sampler2D Tex;\n" "uniform sampler2D LMTex;\n" "uniform sampler2D FullbrightTex;\n" "uniform bool UseFullbrightTex;\n" "uniform bool UseOverbright;\n" "uniform bool UseAlphaTest;\n" "uniform float Alpha;\n" "void main()\n" "{\n" " vec4 result = texture2D(Tex, gl_TexCoord[0].xy);\n" " if (UseAlphaTest && (result.a < 0.666))\n" " discard;\n" " result *= texture2D(LMTex, gl_TexCoord[1].xy);\n" " if (UseOverbright)\n" " result.rgb *= 2.0;\n" " if (UseFullbrightTex)\n" " result += texture2D(FullbrightTex, gl_TexCoord[0].xy);\n" " result = clamp(result, 0.0, 1.0);\n" " // apply GL_EXP2 fog (from the orange book)\n" " float fog = exp(-gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord);\n" " fog = clamp(fog, 0.0, 1.0);\n" " result = mix(gl_Fog.color, result, fog);\n" " result.a = Alpha;\n" " gl_FragColor = result;\n" "}\n"; if (!gl_glsl_alias_able) return; r_world_program = GL_CreateProgram (vertSource, fragSource, sizeof(bindings)/sizeof(bindings[0]), bindings); if (r_world_program != 0) { // get uniform locations texLoc = GL_GetUniformLocation (&r_world_program, "Tex"); LMTexLoc = GL_GetUniformLocation (&r_world_program, "LMTex"); fullbrightTexLoc = GL_GetUniformLocation (&r_world_program, "FullbrightTex"); useFullbrightTexLoc = GL_GetUniformLocation (&r_world_program, "UseFullbrightTex"); useOverbrightLoc = GL_GetUniformLocation (&r_world_program, "UseOverbright"); useAlphaTestLoc = GL_GetUniformLocation (&r_world_program, "UseAlphaTest"); alphaLoc = GL_GetUniformLocation (&r_world_program, "Alpha"); } } extern GLuint gl_bmodel_vbo; /* ================ R_DrawTextureChains_GLSL -- ericw Draw lightmapped surfaces with fulbrights in one pass, using VBO. Requires 3 TMUs, OpenGL 2.0 ================ */ void R_DrawTextureChains_GLSL (qmodel_t *model, entity_t *ent, texchain_t chain) { int i; msurface_t *s; texture_t *t; qboolean bound; int lastlightmap; gltexture_t *fullbright = NULL; float entalpha; entalpha = (ent != NULL) ? ENTALPHA_DECODE(ent->alpha) : 1.0f; // enable blending / disable depth writes if (entalpha < 1) { glDepthMask (GL_FALSE); glEnable (GL_BLEND); } GL_UseProgramFunc (r_world_program); // Bind the buffers GL_BindBuffer (GL_ARRAY_BUFFER, gl_bmodel_vbo); GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); // indices come from client memory! GL_EnableVertexAttribArrayFunc (vertAttrIndex); GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex); GL_EnableVertexAttribArrayFunc (LMCoordsAttrIndex); GL_VertexAttribPointerFunc (vertAttrIndex, 3, GL_FLOAT, GL_FALSE, VERTEXSIZE * sizeof(float), ((float *)0)); GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, VERTEXSIZE * sizeof(float), ((float *)0) + 3); GL_VertexAttribPointerFunc (LMCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, VERTEXSIZE * sizeof(float), ((float *)0) + 5); // set uniforms GL_Uniform1iFunc (texLoc, 0); GL_Uniform1iFunc (LMTexLoc, 1); GL_Uniform1iFunc (fullbrightTexLoc, 2); GL_Uniform1iFunc (useFullbrightTexLoc, 0); GL_Uniform1iFunc (useOverbrightLoc, (int)gl_overbright.value); GL_Uniform1iFunc (useAlphaTestLoc, 0); GL_Uniform1fFunc (alphaLoc, entalpha); for (i=0 ; i<model->numtextures ; i++) { t = model->textures[i]; if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE)) continue; // Enable/disable TMU 2 (fullbrights) // FIXME: Move below to where we bind GL_TEXTURE0 if (gl_fullbrights.value && (fullbright = R_TextureAnimation(t, ent != NULL ? ent->frame : 0)->fullbright)) { GL_SelectTexture (GL_TEXTURE2); GL_Bind (fullbright); GL_Uniform1iFunc (useFullbrightTexLoc, 1); } else GL_Uniform1iFunc (useFullbrightTexLoc, 0); R_ClearBatch (); bound = false; lastlightmap = 0; // avoid compiler warning for (s = t->texturechains[chain]; s; s = s->texturechain) if (!s->culled) { if (!bound) //only bind once we are sure we need this texture { GL_SelectTexture (GL_TEXTURE0); GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture); if (t->texturechains[chain]->flags & SURF_DRAWFENCE) GL_Uniform1iFunc (useAlphaTestLoc, 1); // Flip alpha test back on bound = true; lastlightmap = s->lightmaptexturenum; } if (s->lightmaptexturenum != lastlightmap) R_FlushBatch (); GL_SelectTexture (GL_TEXTURE1); GL_Bind (lightmap_textures[s->lightmaptexturenum]); lastlightmap = s->lightmaptexturenum; R_BatchSurface (s); rs_brushpasses++; } R_FlushBatch (); if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE) GL_Uniform1iFunc (useAlphaTestLoc, 0); // Flip alpha test back off } // clean up GL_DisableVertexAttribArrayFunc (vertAttrIndex); GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex); GL_DisableVertexAttribArrayFunc (LMCoordsAttrIndex); GL_UseProgramFunc (0); GL_SelectTexture (GL_TEXTURE0); if (entalpha < 1) { glDepthMask (GL_TRUE); glDisable (GL_BLEND); } } /* ============= R_DrawWorld -- johnfitz -- rewritten ============= */ void R_DrawTextureChains (qmodel_t *model, entity_t *ent, texchain_t chain) { float entalpha; if (ent != NULL) entalpha = ENTALPHA_DECODE(ent->alpha); else entalpha = 1; // ericw -- the mh dynamic lightmap speedup: make a first pass through all // surfaces we are going to draw, and rebuild any lightmaps that need it. // this also chains surfaces by lightmap which is used by r_lightmap 1. // the previous implementation of the speedup uploaded lightmaps one frame // late which was visible under some conditions, this method avoids that. R_BuildLightmapChains (model, chain); R_UploadLightmaps (); if (r_drawflat_cheatsafe) { glDisable (GL_TEXTURE_2D); R_DrawTextureChains_Drawflat (model, chain); glEnable (GL_TEXTURE_2D); return; } if (r_fullbright_cheatsafe) { R_BeginTransparentDrawing (entalpha); R_DrawTextureChains_TextureOnly (model, ent, chain); R_EndTransparentDrawing (entalpha); goto fullbrights; } if (r_lightmap_cheatsafe) { if (!gl_overbright.value) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0.5, 0.5, 0.5); } R_DrawLightmapChains (); if (!gl_overbright.value) { glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } R_DrawTextureChains_White (model, chain); return; } R_BeginTransparentDrawing (entalpha); R_DrawTextureChains_NoTexture (model, chain); // OpenGL 2 fast path if (r_world_program != 0) { R_EndTransparentDrawing (entalpha); R_DrawTextureChains_GLSL (model, ent, chain); return; } if (gl_overbright.value) { if (gl_texture_env_combine && gl_mtexable) //case 1: texture and lightmap in one pass, overbright using texture combiners { GL_EnableMultitexture (); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f); GL_DisableMultitexture (); R_DrawTextureChains_Multitexture (model, ent, chain); GL_EnableMultitexture (); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DisableMultitexture (); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (entalpha < 1) //case 2: can't do multipass if entity has alpha, so just draw the texture { R_DrawTextureChains_TextureOnly (model, ent, chain); } else //case 3: texture in one pass, lightmap in second pass using 2x modulation blend func, fog in third pass { //to make fog work with multipass lightmapping, need to do one pass //with no fog, one modulate pass with black fog, and one additive //pass with black geometry and normal fog Fog_DisableGFog (); R_DrawTextureChains_TextureOnly (model, ent, chain); Fog_EnableGFog (); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR); //2x modulate Fog_StartAdditive (); R_DrawLightmapChains (); Fog_StopAdditive (); if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); R_DrawTextureChains_TextureOnly (model, ent, chain); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } else { if (gl_mtexable) //case 4: texture and lightmap in one pass, regular modulation { GL_EnableMultitexture (); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DisableMultitexture (); R_DrawTextureChains_Multitexture (model, ent, chain); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (entalpha < 1) //case 5: can't do multipass if entity has alpha, so just draw the texture { R_DrawTextureChains_TextureOnly (model, ent, chain); } else //case 6: texture in one pass, lightmap in a second pass, fog in third pass { //to make fog work with multipass lightmapping, need to do one pass //with no fog, one modulate pass with black fog, and one additive //pass with black geometry and normal fog Fog_DisableGFog (); R_DrawTextureChains_TextureOnly (model, ent, chain); Fog_EnableGFog (); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc(GL_ZERO, GL_SRC_COLOR); //modulate Fog_StartAdditive (); R_DrawLightmapChains (); Fog_StopAdditive (); if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); R_DrawTextureChains_TextureOnly (model, ent, chain); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } R_EndTransparentDrawing (entalpha); fullbrights: if (gl_fullbrights.value) { glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f (entalpha, entalpha, entalpha); Fog_StartAdditive (); R_DrawTextureChains_Glow (model, ent, chain); Fog_StopAdditive (); glColor3f (1, 1, 1); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } /* ============= R_DrawWorld -- ericw -- moved from R_DrawTextureChains, which is no longer specific to the world. ============= */ void R_DrawWorld (void) { if (!r_drawworld_cheatsafe) return; R_DrawTextureChains (cl.worldmodel, NULL, chain_world); } /* ============= R_DrawWorld_Water -- ericw -- moved from R_DrawTextureChains_Water, which is no longer specific to the world. ============= */ void R_DrawWorld_Water (void) { if (!r_drawworld_cheatsafe) return; R_DrawTextureChains_Water (cl.worldmodel, NULL, chain_world); } /* ============= R_DrawWorld_ShowTris -- ericw -- moved from R_DrawTextureChains_ShowTris, which is no longer specific to the world. ============= */ void R_DrawWorld_ShowTris (void) { if (!r_drawworld_cheatsafe) return; R_DrawTextureChains_ShowTris (cl.worldmodel, chain_world); } ����������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cd_sdl.c��������������������������������������������������������������������0000644�0000000�0000000�00000030175�12420513075�015122� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cd_sdl.c * * Copyright (C) 1996-1997 Id Software, Inc. * Taken from the Twilight project with modifications * to make it work with Hexen II: Hammer of Thyrion. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #if defined(USE_SDL2) #include <SDL2/SDL.h> #else #include <SDL/SDL.h> #endif #else #include "SDL.h" #endif #ifndef SDL_INIT_CDROM /* SDL dropped support for cd audio since v1.3.0 */ #pragma message("Warning: SDL CDAudio support disabled") #include "cd_null.c" #else /* SDL_INIT_CDROM */ #include "quakedef.h" static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean enabled = true; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static double endOfTrack = -1.0, pausetime = -1.0; static SDL_CD *cd_handle; static int cd_dev = -1; static float old_cdvolume; static qboolean hw_vol_works = true; static void CDAudio_Eject(void) { if (!cd_handle || !enabled) return; #ifdef __linux__ SDL_CDStop(cd_handle); /* see CDAudio_Stop() */ #endif if (SDL_CDEject(cd_handle) == -1) Con_Printf ("Unable to eject CD-ROM: %s\n", SDL_GetError ()); } static int CDAudio_GetAudioDiskInfo(void) { cdValid = false; if (!cd_handle) return -1; if ( ! CD_INDRIVE(SDL_CDStatus(cd_handle)) ) return -1; cdValid = true; return 0; } int CDAudio_Play(byte track, qboolean looping) { int len_m, len_s, len_f; if (!cd_handle || !enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > cd_handle->numtracks) { Con_Printf ("CDAudio_Play: Bad track number %d.\n", track); return -1; } if (cd_handle->track[track-1].type == SDL_DATA_TRACK) { Con_Printf ("CDAudio_Play: track %d is not audio\n", track); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } if (SDL_CDPlay(cd_handle, cd_handle->track[track-1].offset, cd_handle->track[track-1].length) == -1) { Con_Printf ("CDAudio_Play: Unable to play track %d: %s\n", track, SDL_GetError ()); return -1; } playLooping = looping; playTrack = track; playing = true; FRAMES_TO_MSF(cd_handle->track[track-1].length, &len_m, &len_s, &len_f); endOfTrack = realtime + ((double)len_m * 60.0) + (double)len_s + (double)len_f / (double)CD_FPS; /* Add the pregap for the next track. This means that disc-at-once CDs * won't loop smoothly, but they wouldn't anyway so it doesn't really * matter. SDL doesn't give us pregap information anyway, so you'll * just have to live with it. */ endOfTrack += 2.0; pausetime = -1.0; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { if (!cd_handle || !enabled) return; if (!playing) return; #ifdef __linux__ /* Don't really stop, but just pause: On some devices, the CDROMSTOP * ioctl causes any followup ioctls to fail for a considerable time. * observed with a TSSTcorp CDW/DVD SH-M522C drive with TS05 and TS08 * firmware versions running under a 2.6.27.25 kernel, and with a * Samsung DVD r/w drive running under 2.6.35.6 kernel. * Therefore, avoid dead stops if playback may be resumed shortly. */ if (SDL_CDPause(cd_handle) == -1) Con_Printf ("CDAudio_Stop: Unable to stop CD-ROM (%s)\n", SDL_GetError()); #else if (SDL_CDStop(cd_handle) == -1) Con_Printf ("CDAudio_Stop: Unable to stop CD-ROM (%s)\n", SDL_GetError()); #endif wasPlaying = false; playing = false; pausetime = -1.0; endOfTrack = -1.0; } void CDAudio_Pause(void) { if (!cd_handle || !enabled) return; if (!playing) return; if (SDL_CDPause(cd_handle) == -1) Con_Printf ("Unable to pause CD-ROM: %s\n", SDL_GetError()); wasPlaying = playing; playing = false; pausetime = realtime; } void CDAudio_Resume(void) { if (!cd_handle || !enabled) return; if (!cdValid) return; if (!wasPlaying) return; if (SDL_CDResume(cd_handle) == -1) Con_Printf ("Unable to resume CD-ROM: %s\n", SDL_GetError()); playing = true; endOfTrack += realtime - pausetime; pausetime = -1.0; } static int get_first_audiotrk (void) { int i; for (i = 0; i < cd_handle->numtracks; ++i) if (cd_handle->track[i].type != SDL_DATA_TRACK) return ++i; return 1; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, info, next, prev\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n + 1)); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { n = atoi(Cmd_Argv (2)); if (n == 0) n = 1; CDAudio_Play((byte)n, false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { int current_min, current_sec, current_frame; int length_min, length_sec, length_frame; Con_Printf ("%u tracks\n", cd_handle->numtracks); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); if (playing || wasPlaying) { SDL_CDStatus(cd_handle); FRAMES_TO_MSF(cd_handle->cur_frame, ¤t_min, ¤t_sec, ¤t_frame); FRAMES_TO_MSF(cd_handle->track[playTrack-1].length, &length_min, &length_sec, &length_frame); Con_Printf ("Current position: %d:%02d.%02d (of %d:%02d.%02d)\n", current_min, current_sec, current_frame * 60 / CD_FPS, length_min, length_sec, length_frame * 60 / CD_FPS); } Con_Printf("Volume is %f\n", bgmvolume.value); return; } if (q_strcasecmp(command, "next") == 0) { if (playTrack == cd_handle->numtracks) playTrack = get_first_audiotrk() - 1; CDAudio_Play(playTrack + 1, playLooping); return; } if (q_strcasecmp(command, "prev") == 0) { if (playTrack == get_first_audiotrk()) playTrack = cd_handle->numtracks + 1; CDAudio_Play(playTrack - 1, playLooping); return; } Con_Printf ("cd: unknown command \"%s\"\n", command); } static qboolean CD_GetVolume (void *unused) { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return false; } static qboolean CD_SetVolume (void *unused) { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return false; } static qboolean CDAudio_SetVolume (float value) { if (!cd_handle || !enabled) return false; old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); if (!hw_vol_works) { return false; } else { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return CD_SetVolume (NULL); } } void CDAudio_Update(void) { CDstatus curstat; /* static double lastchk;*/ if (!cd_handle || !enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); /* if (playing && realtime > lastchk)*/ if (playing && realtime > endOfTrack) { /* lastchk = realtime + 2;*/ /* two seconds between chks */ curstat = SDL_CDStatus(cd_handle); if (curstat != CD_PLAYING && curstat != CD_PAUSED) { playing = false; endOfTrack = -1.0; if (playLooping) CDAudio_Play(playTrack, true); } } } static const char *get_cddev_arg (const char *arg) { #if defined(_WIN32) /* arg should be like "D:\", make sure it is so, * but tolerate args like "D" or "D:", as well. */ static char drive[4]; if (!arg || ! *arg) return NULL; if (arg[1] != '\0') { if (arg[1] != ':') return NULL; if (arg[2] != '\0') { if (arg[2] != '\\' && arg[2] != '/') return NULL; if (arg[3] != '\0') return NULL; } } if (*arg >= 'A' && *arg <= 'Z') { drive[0] = *arg; drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; return drive; } else if (*arg >= 'a' && *arg <= 'z') { /* make it uppercase for SDL */ drive[0] = *arg - ('a' - 'A'); drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; return drive; } return NULL; #else if (!arg || ! *arg) return NULL; return arg; #endif } static void export_cddev_arg (void) { /* Bad ugly hack to workaround SDL's cdrom device detection. * not needed for windows due to the way SDL_cdrom works. */ #if !defined(_WIN32) int i = COM_CheckParm("-cddev"); if (i != 0 && i < com_argc - 1 && com_argv[i+1][0] != '\0') { static char arg[64]; q_snprintf(arg, sizeof(arg), "SDL_CDROM=%s", com_argv[i+1]); putenv(arg); } #endif } int CDAudio_Init(void) { int i, sdl_num_drives; if (safemode || COM_CheckParm("-nocdaudio")) return -1; export_cddev_arg(); if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0) { Con_Printf("Couldn't init SDL cdrom: %s\n", SDL_GetError()); return -1; } sdl_num_drives = SDL_CDNumDrives (); Con_Printf ("SDL detected %d CD-ROM drive%c\n", sdl_num_drives, sdl_num_drives == 1 ? ' ' : 's'); if (sdl_num_drives < 1) return -1; if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) { const char *userdev = get_cddev_arg(com_argv[i+1]); if (!userdev) { Con_Printf("Invalid argument to -cddev\n"); return -1; } for (i = 0; i < sdl_num_drives; i++) { if (!q_strcasecmp(SDL_CDName(i), userdev)) { cd_dev = i; break; } } if (cd_dev == -1) { Con_Printf("SDL couldn't find cdrom device %s\n", userdev); return -1; } } if (cd_dev == -1) cd_dev = 0; /* default drive */ cd_handle = SDL_CDOpen(cd_dev); if (!cd_handle) { Con_Printf ("CDAudio_Init: Unable to open CD-ROM drive %s (%s)\n", SDL_CDName(cd_dev), SDL_GetError()); return -1; } for (i = 0; i < 100; i++) remap[i] = i; enabled = true; old_cdvolume = bgmvolume.value; Con_Printf("CDAudio initialized (SDL, using %s)\n", SDL_CDName(cd_dev)); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("CDAudio_Init: No CD in drive\n"); cdValid = false; } Cmd_AddCommand ("cd", CD_f); hw_vol_works = CD_GetVolume (NULL); /* no SDL support at present. */ if (hw_vol_works) hw_vol_works = CDAudio_SetVolume (bgmvolume.value); return 0; } void CDAudio_Shutdown(void) { if (!cd_handle) return; CDAudio_Stop(); if (hw_vol_works) CD_SetVolume (NULL); /* no SDL support at present. */ #ifdef __linux__ SDL_CDStop(cd_handle); /* see CDAudio_Stop() */ #endif SDL_CDClose(cd_handle); cd_handle = NULL; cd_dev = -1; SDL_QuitSubSystem(SDL_INIT_CDROM); } #endif /* SDL_INIT_CDROM */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_vorbis.h����������������������������������������������������������������0000644�0000000�0000000�00000000341�12113434521�016034� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Ogg/Vorbis streaming music support. */ #if !defined(_SND_VORBIS_H_) #define _SND_VORBIS_H_ 1 #if defined(USE_CODEC_VORBIS) extern snd_codec_t vorbis_codec; #endif /* USE_CODEC_VORBIS */ #endif /* ! _SND_VORBIS_H_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/net_dgrm.c������������������������������������������������������������������0000644�0000000�0000000�00000102072�13136631402�015465� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // This is enables a simple IP banning mechanism #define BAN_TEST #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" // these two macros are to make the code more readable #define sfunc net_landrivers[sock->landriver] #define dfunc net_landrivers[net_landriverlevel] static int net_landriverlevel; /* statistic counters */ static int packetsSent = 0; static int packetsReSent = 0; static int packetsReceived = 0; static int receivedDuplicateCount = 0; static int shortPacketCount = 0; static int droppedDatagrams; static struct { unsigned int length; unsigned int sequence; byte data[MAX_DATAGRAM]; } packetBuffer; static int myDriverLevel; extern qboolean m_return_onerror; extern char m_return_reason[32]; static char *StrAddr (struct qsockaddr *addr) { static char buf[34]; byte *p = (byte *)addr; int n; for (n = 0; n < 16; n++) sprintf (buf + n * 2, "%02x", *p++); return buf; } #ifdef BAN_TEST static struct in_addr banAddr; static struct in_addr banMask; static void NET_Ban_f (void) { char addrStr [32]; char maskStr [32]; void (*print_fn)(const char *fmt, ...) FUNCP_PRINTF(1,2); if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print_fn = Con_Printf; } else { if (pr_global_struct->deathmatch) return; print_fn = SV_ClientPrintf; } switch (Cmd_Argc ()) { case 1: if (banAddr.s_addr != INADDR_ANY) { Q_strcpy(addrStr, inet_ntoa(banAddr)); Q_strcpy(maskStr, inet_ntoa(banMask)); print_fn("Banning %s [%s]\n", addrStr, maskStr); } else print_fn("Banning not active\n"); break; case 2: if (q_strcasecmp(Cmd_Argv(1), "off") == 0) banAddr.s_addr = INADDR_ANY; else banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = INADDR_NONE; break; case 3: banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = inet_addr(Cmd_Argv(2)); break; default: print_fn("BAN ip_address [mask]\n"); break; } } #endif // BAN_TEST int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; #ifdef DEBUG if (data->cursize == 0) Sys_Error("Datagram_SendMessage: zero length message\n"); if (data->cursize > NET_MAXMESSAGE) Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize); if (sock->canSend == false) Sys_Error("SendMessage: called with canSend == false\n"); #endif Q_memcpy(sock->sendMessage, data->data, data->cursize); sock->sendMessageLength = data->cursize; if (data->cursize <= MAX_DATAGRAM) { dataLen = data->cursize; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->canSend = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int SendMessageNext (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int ReSendMessage (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence - 1); Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsReSent++; return 1; } qboolean Datagram_CanSendMessage (qsocket_t *sock) { if (sock->sendNext) SendMessageNext (sock); return sock->canSend; } qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock) { return true; } int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int packetLen; #ifdef DEBUG if (data->cursize == 0) Sys_Error("Datagram_SendUnreliableMessage: zero length message\n"); if (data->cursize > MAX_DATAGRAM) Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize); #endif packetLen = NET_HEADERSIZE + data->cursize; packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE); packetBuffer.sequence = BigLong(sock->unreliableSendSequence++); Q_memcpy (packetBuffer.data, data->data, data->cursize); if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; packetsSent++; return 1; } int Datagram_GetMessage (qsocket_t *sock) { unsigned int length; unsigned int flags; int ret = 0; struct qsockaddr readaddr; unsigned int sequence; unsigned int count; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while (1) { length = (unsigned int) sfunc.Read(sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); // if ((rand() & 255) > 220) // continue; if (length == 0) break; if (length == (unsigned int)-1) { Con_Printf("Read error\n"); return -1; } if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) { Con_Printf("Forged packet received\n"); Con_Printf("Expected: %s\n", StrAddr (&sock->addr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); continue; } if (length < NET_HEADERSIZE) { shortPacketCount++; continue; } length = BigLong(packetBuffer.length); flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; if (flags & NETFLAG_CTL) continue; sequence = BigLong(packetBuffer.sequence); packetsReceived++; if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { Con_DPrintf("Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; Con_DPrintf("Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; length -= NET_HEADERSIZE; SZ_Clear (&net_message); SZ_Write (&net_message, packetBuffer.data, length); ret = 2; break; } if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { Con_DPrintf("Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; if (sock->ackSequence != sock->sendSequence) Con_DPrintf("ack sequencing error\n"); } else { Con_DPrintf("Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; if (sock->sendMessageLength > 0) { memmove (sock->sendMessage, sock->sendMessage + MAX_DATAGRAM, sock->sendMessageLength); sock->sendNext = true; } else { sock->sendMessageLength = 0; sock->canSend = true; } continue; } if (flags & NETFLAG_DATA) { packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); packetBuffer.sequence = BigLong(sequence); sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; continue; } sock->receiveSequence++; length -= NET_HEADERSIZE; if (flags & NETFLAG_EOM) { SZ_Clear(&net_message); SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); SZ_Write(&net_message, packetBuffer.data, length); sock->receiveMessageLength = 0; ret = 1; break; } Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if (sock->sendNext) SendMessageNext (sock); return ret; } static void PrintStats(qsocket_t *s) { Con_Printf("canSend = %4u \n", s->canSend); Con_Printf("sendSeq = %4u ", s->sendSequence); Con_Printf("recvSeq = %4u \n", s->receiveSequence); Con_Printf("\n"); } static void NET_Stats_f (void) { qsocket_t *s; if (Cmd_Argc () == 1) { Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent); Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived); Con_Printf("reliable messages sent = %i\n", messagesSent); Con_Printf("reliable messages received = %i\n", messagesReceived); Con_Printf("packetsSent = %i\n", packetsSent); Con_Printf("packetsReSent = %i\n", packetsReSent); Con_Printf("packetsReceived = %i\n", packetsReceived); Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount); Con_Printf("shortPacketCount = %i\n", shortPacketCount); Con_Printf("droppedDatagrams = %i\n", droppedDatagrams); } else if (Q_strcmp(Cmd_Argv(1), "*") == 0) { for (s = net_activeSockets; s; s = s->next) PrintStats(s); for (s = net_freeSockets; s; s = s->next) PrintStats(s); } else { for (s = net_activeSockets; s; s = s->next) { if (q_strcasecmp(Cmd_Argv(1), s->address) == 0) break; } if (s == NULL) { for (s = net_freeSockets; s; s = s->next) { if (q_strcasecmp(Cmd_Argv(1), s->address) == 0) break; } } if (s == NULL) return; PrintStats(s); } } // recognize ip:port (based on ProQuake) static const char *Strip_Port (const char *host) { static char noport[MAX_QPATH]; /* array size as in Host_Connect_f() */ char *p; int port; if (!host || !*host) return host; q_strlcpy (noport, host, sizeof(noport)); if ((p = Q_strrchr(noport, ':')) == NULL) return host; *p++ = '\0'; port = Q_atoi (p); if (port > 0 && port < 65536 && port != net_hostport) { net_hostport = port; Con_Printf("Port set to %d\n", net_hostport); } return noport; } static qboolean testInProgress = false; static int testPollCount; static int testDriver; static sys_socket_t testSocket; static void Test_Poll (void *); static PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; static void Test_Poll (void *unused) { struct qsockaddr clientaddr; int control; int len; char name[32]; char address[64]; int colors; int frags; int connectTime; net_landriverlevel = testDriver; while (1) { len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) break; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) break; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) break; if ((control & NETFLAG_LENGTH_MASK) != len) break; if (MSG_ReadByte() != CCREP_PLAYER_INFO) Sys_Error("Unexpected repsonse to Player Info request\n"); MSG_ReadByte(); /* playerNumber */ Q_strcpy(name, MSG_ReadString()); colors = MSG_ReadLong(); frags = MSG_ReadLong(); connectTime = MSG_ReadLong(); Q_strcpy(address, MSG_ReadString()); Con_Printf("%s\n frags:%3i colors:%d %d time:%d\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); } testPollCount--; if (testPollCount) { SchedulePollProcedure(&testPollProcedure, 0.1); } else { dfunc.Close_Socket(testSocket); testInProgress = false; } } static void Test_f (void) { const char *host; int n; int maxusers = MAX_SCOREBOARD; struct qsockaddr sendaddr; if (testInProgress) return; host = Strip_Port (Cmd_Argv(1)); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; maxusers = hostcache[n].maxusers; Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) { Con_Printf("Could not resolve %s\n", host); return; } JustDoIt: testSocket = dfunc.Open_Socket(0); if (testSocket == INVALID_SOCKET) return; testInProgress = true; testPollCount = 20; testDriver = net_landriverlevel; for (n = 0; n < maxusers; n++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); MSG_WriteByte(&net_message, n); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); } SZ_Clear(&net_message); SchedulePollProcedure(&testPollProcedure, 0.1); } static qboolean test2InProgress = false; static int test2Driver; static sys_socket_t test2Socket; static void Test2_Poll (void *); static PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; static void Test2_Poll (void *unused) { struct qsockaddr clientaddr; int control; int len; char name[256]; char value[256]; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) goto Reschedule; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) goto Error; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) goto Error; if ((control & NETFLAG_LENGTH_MASK) != len) goto Error; if (MSG_ReadByte() != CCREP_RULE_INFO) goto Error; Q_strcpy(name, MSG_ReadString()); if (name[0] == 0) goto Done; Q_strcpy(value, MSG_ReadString()); Con_Printf("%-16.16s %-16.16s\n", name, value); SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, name); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); Reschedule: SchedulePollProcedure(&test2PollProcedure, 0.05); return; Error: Con_Printf("Unexpected repsonse to Rule Info request\n"); Done: dfunc.Close_Socket(test2Socket); test2InProgress = false; return; } static void Test2_f (void) { const char *host; int n; struct qsockaddr sendaddr; if (test2InProgress) return; host = Strip_Port (Cmd_Argv(1)); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) { Con_Printf("Could not resolve %s\n", host); return; } JustDoIt: test2Socket = dfunc.Open_Socket(0); if (test2Socket == INVALID_SOCKET) return; test2InProgress = true; test2Driver = net_landriverlevel; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, ""); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); SchedulePollProcedure(&test2PollProcedure, 0.05); } int Datagram_Init (void) { int i, num_inited; sys_socket_t csock; #ifdef BAN_TEST banAddr.s_addr = INADDR_ANY; banMask.s_addr = INADDR_NONE; #endif myDriverLevel = net_driverlevel; Cmd_AddCommand ("net_stats", NET_Stats_f); if (safemode || COM_CheckParm("-nolan")) return -1; num_inited = 0; for (i = 0; i < net_numlandrivers; i++) { csock = net_landrivers[i].Init (); if (csock == INVALID_SOCKET) continue; net_landrivers[i].initialized = true; net_landrivers[i].controlSock = csock; num_inited++; } if (num_inited == 0) return -1; #ifdef BAN_TEST Cmd_AddCommand ("ban", NET_Ban_f); #endif Cmd_AddCommand ("test", Test_f); Cmd_AddCommand ("test2", Test2_f); return 0; } void Datagram_Shutdown (void) { int i; // // shutdown the lan drivers // for (i = 0; i < net_numlandrivers; i++) { if (net_landrivers[i].initialized) { net_landrivers[i].Shutdown (); net_landrivers[i].initialized = false; } } } void Datagram_Close (qsocket_t *sock) { sfunc.Close_Socket(sock->socket); } void Datagram_Listen (qboolean state) { int i; for (i = 0; i < net_numlandrivers; i++) { if (net_landrivers[i].initialized) net_landrivers[i].Listen (state); } } static qsocket_t *_Datagram_CheckNewConnections (void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; sys_socket_t newsock; sys_socket_t acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; acceptsock = dfunc.CheckNewConnections(); if (acceptsock == INVALID_SOCKET) return NULL; SZ_Clear(&net_message); len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return NULL; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) break; } } if (clientNumber == svs.maxclients) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { const char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); var = Cvar_FindVarAfter (prevCvarName, CVAR_SERVERINFO); // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) return NULL; if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Incompatible version.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } #ifdef BAN_TEST // check for a ban if (clientaddr.qsa_family == AF_INET) { in_addr_t testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask.s_addr) == banAddr.s_addr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "You have been banned.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) continue; ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in NET_Close(s); return NULL; } } // allocate a QSocket sock = NET_NewQSocket (); if (sock == NULL) { // no room; try to let him know SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Server is full.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // allocate a network socket newsock = dfunc.Open_Socket(0); if (newsock == INVALID_SOCKET) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { dfunc.Close_Socket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; } qsocket_t *Datagram_CheckNewConnections (void) { qsocket_t *ret = NULL; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (net_landrivers[net_landriverlevel].initialized) { if ((ret = _Datagram_CheckNewConnections ()) != NULL) break; } } return ret; } static void _Datagram_SearchForHosts (qboolean xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < (int) sizeof(int)) continue; net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) continue; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) continue; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) continue; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) continue; if ((control & NETFLAG_LENGTH_MASK) != ret) continue; if (MSG_ReadByte() != CCREP_SERVER_INFO) continue; dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) { if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) break; } // is it already there? if (n < hostCacheCount) continue; // add it hostCacheCount++; Q_strcpy(hostcache[n].name, MSG_ReadString()); Q_strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { Q_strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; Q_strcpy(hostcache[n].name, "*"); Q_strcat(hostcache[n].name, hostcache[n].cname); } Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = Q_strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else hostcache[n].name[i-1]++; i = -1; } } } } void Datagram_SearchForHosts (qboolean xmit) { for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (hostCacheCount == HOSTCACHESIZE) break; if (net_landrivers[net_landriverlevel].initialized) _Datagram_SearchForHosts (xmit); } } static qsocket_t *_Datagram_Connect (const char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; sys_socket_t newsock; int ret; int reps; double start_time; int control; const char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) { Con_Printf("Could not resolve %s\n", host); return NULL; } newsock = dfunc.Open_Socket (0); if (newsock == INVALID_SOCKET) return NULL; sock = NET_NewQSocket (); if (sock == NULL) goto ErrorReturn2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect (newsock, &sendaddr) == -1) goto ErrorReturn; // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen (); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s | %s\n", dfunc.AddrToString (&sendaddr), StrAddr(&sendaddr)); Con_Printf("Received: %s | %s\n", dfunc.AddrToString (&readaddr), StrAddr(&readaddr)); SCR_UpdateScreen (); ret = 0; continue; } if (ret < (int) sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) break; Con_Printf("still trying...\n"); SCR_UpdateScreen (); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf("%s\n", reason); q_strlcpy(m_return_reason, reason, sizeof(m_return_reason)); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect (newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.Close_Socket(newsock); if (m_return_onerror) { IN_Deactivate(modestate == MS_WINDOWED); key_dest = key_menu; m_state = m_return_state; m_return_onerror = false; } return NULL; } qsocket_t *Datagram_Connect (const char *host) { qsocket_t *ret = NULL; host = Strip_Port (host); for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (net_landrivers[net_landriverlevel].initialized) { if ((ret = _Datagram_Connect (host)) != NULL) break; } } return ret; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cd_null.c�������������������������������������������������������������������0000644�0000000�0000000�00000002065�12407762022�015312� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cd_null.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" int CDAudio_Play(byte track, qboolean looping) { return -1; } void CDAudio_Stop(void) { } void CDAudio_Pause(void) { } void CDAudio_Resume(void) { } void CDAudio_Update(void) { } int CDAudio_Init(void) { Con_Printf("CDAudio disabled at compile time\n"); return -1; } void CDAudio_Shutdown(void) { } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_codeci.h����������������������������������������������������������������0000644�0000000�0000000�00000003550�12220541170�015761� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com> * Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SND_CODECI_H_ #define _SND_CODECI_H_ /* Codec internals */ typedef qboolean (*CODEC_INIT)(void); typedef void (*CODEC_SHUTDOWN)(void); typedef qboolean (*CODEC_OPEN)(snd_stream_t *stream); typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer); typedef int (*CODEC_REWIND)(snd_stream_t *stream); typedef void (*CODEC_CLOSE)(snd_stream_t *stream); struct snd_codec_s { unsigned int type; /* handled data type. (1U << n) */ qboolean initialized; /* init succeedded */ const char *ext; /* expected extension */ CODEC_INIT initialize; CODEC_SHUTDOWN shutdown; CODEC_OPEN codec_open; CODEC_READ codec_read; CODEC_REWIND codec_rewind; CODEC_CLOSE codec_close; snd_codec_t *next; }; qboolean S_CodecForwardStream (snd_stream_t *stream, unsigned int type); /* Forward a stream to another codec of 'type' type. */ #endif /* _SND_CODECI_H_ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/platform.h������������������������������������������������������������������0000644�0000000�0000000�00000002362�12407762022�015523� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2005 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _QUAKE_PLATFORM_H #define _QUAKE_PLATFORM_H /* platform dependent way to set the window icon */ void PL_SetWindowIcon(void); /* platform dependent cleanup */ void PL_VID_Shutdown (void); /* retrieve text from the clipboard (returns Z_Malloc()'ed data) */ char *PL_GetClipboardData (void); /* show an error dialog */ void PL_ErrorDialog(const char *text); #endif /* _QUAKE_PLATFORM_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/build_cross_osx-sdl2.sh�����������������������������������������������������0000755�0000000�0000000�00000002015�12406264714�020127� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh rm -f quakespasm.x86 quakespasm.x86_64 QuakeSpasm make clean OLDPATH=$PATH MAKE_CMD=make OSXBUILD=1 export OSXBUILD STRIP=/bin/true export STRIP # x86 PATH=/opt/cross_osx-x86.5/usr/bin:$OLDPATH CC=i686-apple-darwin9-gcc AS=i686-apple-darwin9-as AR=i686-apple-darwin9-ar RANLIB=i686-apple-darwin9-ranlib LIPO=i686-apple-darwin9-lipo export PATH CC AS AR RANLIB LIPO $MAKE_CMD MACH_TYPE=x86 USE_SDL2=1 -f Makefile.darwin $* || exit 1 i686-apple-darwin9-strip -S quakespasm || exit 1 mv quakespasm quakespasm.x86 || exit 1 $MAKE_CMD clean # x86_64 PATH=/opt/cross_osx-x86_64/usr/bin:$OLDPATH CC=x86_64-apple-darwin9-gcc AS=x86_64-apple-darwin9-as AR=x86_64-apple-darwin9-ar RANLIB=x86_64-apple-darwin9-ranlib LIPO=x86_64-apple-darwin9-lipo export PATH CC AS AR RANLIB LIPO $MAKE_CMD MACH_TYPE=x86_64 USE_SDL2=1 -f Makefile.darwin $* || exit 1 x86_64-apple-darwin9-strip -S quakespasm || exit 1 mv quakespasm quakespasm.x86_64 || exit 1 $MAKE_CMD clean $LIPO -create -o QuakeSpasm quakespasm.x86 quakespasm.x86_64 || exit 1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/r_alias.c�������������������������������������������������������������������0000644�0000000�0000000�00000070603�13202224450�015277� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //r_alias.c -- alias model rendering #include "quakedef.h" extern cvar_t r_drawflat, gl_overbright_models, gl_fullbrights, r_lerpmodels, r_lerpmove; //johnfitz //up to 16 color translated skins gltexture_t *playertextures[MAX_SCOREBOARD]; //johnfitz -- changed to an array of pointers #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; extern vec3_t lightcolor; //johnfitz -- replaces "float shadelight" for lit support // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 float r_avertexnormal_dots[SHADEDOT_QUANT][256] = { #include "anorm_dots.h" }; extern vec3_t lightspot; float *shadedots = r_avertexnormal_dots[0]; vec3_t shadevector; float entalpha; //johnfitz qboolean overbright; //johnfitz qboolean shading = true; //johnfitz -- if false, disable vertex shading for various reasons (fullbright, r_lightmap, showtris, etc) //johnfitz -- struct for passing lerp information to drawing functions typedef struct { short pose1; short pose2; float blend; vec3_t origin; vec3_t angles; } lerpdata_t; //johnfitz static GLuint r_alias_program; // uniforms used in vert shader static GLuint blendLoc; static GLuint shadevectorLoc; static GLuint lightColorLoc; // uniforms used in frag shader static GLuint texLoc; static GLuint fullbrightTexLoc; static GLuint useFullbrightTexLoc; static GLuint useOverbrightLoc; static GLuint useAlphaTestLoc; #define pose1VertexAttrIndex 0 #define pose1NormalAttrIndex 1 #define pose2VertexAttrIndex 2 #define pose2NormalAttrIndex 3 #define texCoordsAttrIndex 4 /* ============= GLARB_GetXYZOffset Returns the offset of the first vertex's meshxyz_t.xyz in the vbo for the given model and pose. ============= */ static void *GLARB_GetXYZOffset (aliashdr_t *hdr, int pose) { const int xyzoffs = offsetof (meshxyz_t, xyz); return (void *) (currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + xyzoffs); } /* ============= GLARB_GetNormalOffset Returns the offset of the first vertex's meshxyz_t.normal in the vbo for the given model and pose. ============= */ static void *GLARB_GetNormalOffset (aliashdr_t *hdr, int pose) { const int normaloffs = offsetof (meshxyz_t, normal); return (void *)(currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs); } /* ============= GLAlias_CreateShaders ============= */ void GLAlias_CreateShaders (void) { const glsl_attrib_binding_t bindings[] = { { "TexCoords", texCoordsAttrIndex }, { "Pose1Vert", pose1VertexAttrIndex }, { "Pose1Normal", pose1NormalAttrIndex }, { "Pose2Vert", pose2VertexAttrIndex }, { "Pose2Normal", pose2NormalAttrIndex } }; const GLchar *vertSource = \ "#version 110\n" "\n" "uniform float Blend;\n" "uniform vec3 ShadeVector;\n" "uniform vec4 LightColor;\n" "attribute vec4 TexCoords; // only xy are used \n" "attribute vec4 Pose1Vert;\n" "attribute vec3 Pose1Normal;\n" "attribute vec4 Pose2Vert;\n" "attribute vec3 Pose2Normal;\n" "float r_avertexnormal_dot(vec3 vertexnormal) // from MH \n" "{\n" " float dot = dot(vertexnormal, ShadeVector);\n" " // wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case\n" " if (dot < 0.0)\n" " return 1.0 + dot * (13.0 / 44.0);\n" " else\n" " return 1.0 + dot;\n" "}\n" "void main()\n" "{\n" " gl_TexCoord[0] = TexCoords;\n" " vec4 lerpedVert = mix(Pose1Vert, Pose2Vert, Blend);\n" " gl_Position = gl_ModelViewProjectionMatrix * lerpedVert;\n" " float dot1 = r_avertexnormal_dot(Pose1Normal);\n" " float dot2 = r_avertexnormal_dot(Pose2Normal);\n" " gl_FrontColor = LightColor * vec4(vec3(mix(dot1, dot2, Blend)), 1.0);\n" " // fog\n" " vec3 ecPosition = vec3(gl_ModelViewMatrix * lerpedVert);\n" " gl_FogFragCoord = abs(ecPosition.z);\n" "}\n"; const GLchar *fragSource = \ "#version 110\n" "\n" "uniform sampler2D Tex;\n" "uniform sampler2D FullbrightTex;\n" "uniform bool UseFullbrightTex;\n" "uniform bool UseOverbright;\n" "uniform bool UseAlphaTest;\n" "void main()\n" "{\n" " vec4 result = texture2D(Tex, gl_TexCoord[0].xy);\n" " if (UseAlphaTest && (result.a < 0.666))\n" " discard;\n" " result *= gl_Color;\n" " if (UseOverbright)\n" " result.rgb *= 2.0;\n" " if (UseFullbrightTex)\n" " result += texture2D(FullbrightTex, gl_TexCoord[0].xy);\n" " result = clamp(result, 0.0, 1.0);\n" " // apply GL_EXP2 fog (from the orange book)\n" " float fog = exp(-gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord);\n" " fog = clamp(fog, 0.0, 1.0);\n" " result = mix(gl_Fog.color, result, fog);\n" " result.a = gl_Color.a;\n" " gl_FragColor = result;\n" "}\n"; if (!gl_glsl_alias_able) return; r_alias_program = GL_CreateProgram (vertSource, fragSource, sizeof(bindings)/sizeof(bindings[0]), bindings); if (r_alias_program != 0) { // get uniform locations blendLoc = GL_GetUniformLocation (&r_alias_program, "Blend"); shadevectorLoc = GL_GetUniformLocation (&r_alias_program, "ShadeVector"); lightColorLoc = GL_GetUniformLocation (&r_alias_program, "LightColor"); texLoc = GL_GetUniformLocation (&r_alias_program, "Tex"); fullbrightTexLoc = GL_GetUniformLocation (&r_alias_program, "FullbrightTex"); useFullbrightTexLoc = GL_GetUniformLocation (&r_alias_program, "UseFullbrightTex"); useOverbrightLoc = GL_GetUniformLocation (&r_alias_program, "UseOverbright"); useAlphaTestLoc = GL_GetUniformLocation (&r_alias_program, "UseAlphaTest"); } } /* ============= GL_DrawAliasFrame_GLSL -- ericw Optimized alias model drawing codepath. Compared to the original GL_DrawAliasFrame, this makes 1 draw call, no vertex data is uploaded (it's already in the r_meshvbo and r_meshindexesvbo static VBOs), and lerping and lighting is done in the vertex shader. Supports optional overbright, optional fullbright pixels. Based on code by MH from RMQEngine ============= */ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltexture_t *tx, gltexture_t *fb) { float blend; if (lerpdata.pose1 != lerpdata.pose2) { blend = lerpdata.blend; } else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled { blend = 0; } GL_UseProgramFunc (r_alias_program); GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo); GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo); GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex); GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex); GL_EnableVertexAttribArrayFunc (pose2VertexAttrIndex); GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex); GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex); GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, (void *)(intptr_t)currententity->model->vbostofs); GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose1)); GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose2)); // GL_TRUE to normalize the signed bytes to [-1 .. 1] GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1)); GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2)); // set uniforms GL_Uniform1fFunc (blendLoc, blend); GL_Uniform3fFunc (shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]); GL_Uniform4fFunc (lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha); GL_Uniform1iFunc (texLoc, 0); GL_Uniform1iFunc (fullbrightTexLoc, 1); GL_Uniform1iFunc (useFullbrightTexLoc, (fb != NULL) ? 1 : 0); GL_Uniform1fFunc (useOverbrightLoc, overbright ? 1 : 0); GL_Uniform1iFunc (useAlphaTestLoc, (currententity->model->flags & MF_HOLEY) ? 1 : 0); // set textures GL_SelectTexture (GL_TEXTURE0); GL_Bind (tx); if (fb) { GL_SelectTexture (GL_TEXTURE1); GL_Bind (fb); } // draw glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs); // clean up GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex); GL_DisableVertexAttribArrayFunc (pose1VertexAttrIndex); GL_DisableVertexAttribArrayFunc (pose2VertexAttrIndex); GL_DisableVertexAttribArrayFunc (pose1NormalAttrIndex); GL_DisableVertexAttribArrayFunc (pose2NormalAttrIndex); GL_UseProgramFunc (0); GL_SelectTexture (GL_TEXTURE0); rs_aliaspasses += paliashdr->numtris; } /* ============= GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata) { float vertcolor[4]; trivertx_t *verts1, *verts2; int *commands; int count; float u,v; float blend, iblend; qboolean lerping; if (lerpdata.pose1 != lerpdata.pose2) { lerping = true; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += lerpdata.pose1 * paliashdr->poseverts; verts2 += lerpdata.pose2 * paliashdr->poseverts; blend = lerpdata.blend; iblend = 1.0f - blend; } else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled { lerping = false; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; // avoid bogus compiler warning verts1 += lerpdata.pose1 * paliashdr->poseverts; blend = iblend = 0; // avoid bogus compiler warning } commands = (int *)((byte *)paliashdr + paliashdr->commands); vertcolor[3] = entalpha; //never changes, so there's no need to put this inside the loop while (1) { // get the vertex count and primitive type count = *commands++; if (!count) break; // done if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { u = ((float *)commands)[0]; v = ((float *)commands)[1]; if (mtexenabled) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, u, v); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, u, v); } else glTexCoord2f (u, v); commands += 2; if (shading) { if (r_drawflat_cheatsafe) { srand(count * (unsigned int)(src_offset_t)commands); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); } else if (lerping) { vertcolor[0] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[0]; vertcolor[1] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[1]; vertcolor[2] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[2]; glColor4fv (vertcolor); } else { vertcolor[0] = shadedots[verts1->lightnormalindex] * lightcolor[0]; vertcolor[1] = shadedots[verts1->lightnormalindex] * lightcolor[1]; vertcolor[2] = shadedots[verts1->lightnormalindex] * lightcolor[2]; glColor4fv (vertcolor); } } if (lerping) { glVertex3f (verts1->v[0]*iblend + verts2->v[0]*blend, verts1->v[1]*iblend + verts2->v[1]*blend, verts1->v[2]*iblend + verts2->v[2]*blend); verts1++; verts2++; } else { glVertex3f (verts1->v[0], verts1->v[1], verts1->v[2]); verts1++; } } while (--count); glEnd (); } rs_aliaspasses += paliashdr->numtris; } /* ================= R_SetupAliasFrame -- johnfitz -- rewritten to support lerping ================= */ void R_SetupAliasFrame (aliashdr_t *paliashdr, int frame, lerpdata_t *lerpdata) { entity_t *e = currententity; int posenum, numposes; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d for '%s'\n", frame, e->model->name); frame = 0; } posenum = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { e->lerptime = paliashdr->frames[frame].interval; posenum += (int)(cl.time / e->lerptime) % numposes; } else e->lerptime = 0.1; if (e->lerpflags & LERP_RESETANIM) //kill any lerp in progress { e->lerpstart = 0; e->previouspose = posenum; e->currentpose = posenum; e->lerpflags -= LERP_RESETANIM; } else if (e->currentpose != posenum) // pose changed, start new lerp { if (e->lerpflags & LERP_RESETANIM2) //defer lerping one more time { e->lerpstart = 0; e->previouspose = posenum; e->currentpose = posenum; e->lerpflags -= LERP_RESETANIM2; } else { e->lerpstart = cl.time; e->previouspose = e->currentpose; e->currentpose = posenum; } } //set up values if (r_lerpmodels.value && !(e->model->flags & MOD_NOLERP && r_lerpmodels.value != 2)) { if (e->lerpflags & LERP_FINISH && numposes == 1) lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / (e->lerpfinish - e->lerpstart), 1); else lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / e->lerptime, 1); lerpdata->pose1 = e->previouspose; lerpdata->pose2 = e->currentpose; } else //don't lerp { lerpdata->blend = 1; lerpdata->pose1 = posenum; lerpdata->pose2 = posenum; } } /* ================= R_SetupEntityTransform -- johnfitz -- set up transform part of lerpdata ================= */ void R_SetupEntityTransform (entity_t *e, lerpdata_t *lerpdata) { float blend; vec3_t d; int i; // if LERP_RESETMOVE, kill any lerps in progress if (e->lerpflags & LERP_RESETMOVE) { e->movelerpstart = 0; VectorCopy (e->origin, e->previousorigin); VectorCopy (e->origin, e->currentorigin); VectorCopy (e->angles, e->previousangles); VectorCopy (e->angles, e->currentangles); e->lerpflags -= LERP_RESETMOVE; } else if (!VectorCompare (e->origin, e->currentorigin) || !VectorCompare (e->angles, e->currentangles)) // origin/angles changed, start new lerp { e->movelerpstart = cl.time; VectorCopy (e->currentorigin, e->previousorigin); VectorCopy (e->origin, e->currentorigin); VectorCopy (e->currentangles, e->previousangles); VectorCopy (e->angles, e->currentangles); } //set up values if (r_lerpmove.value && e != &cl.viewent && e->lerpflags & LERP_MOVESTEP) { if (e->lerpflags & LERP_FINISH) blend = CLAMP (0, (cl.time - e->movelerpstart) / (e->lerpfinish - e->movelerpstart), 1); else blend = CLAMP (0, (cl.time - e->movelerpstart) / 0.1, 1); //translation VectorSubtract (e->currentorigin, e->previousorigin, d); lerpdata->origin[0] = e->previousorigin[0] + d[0] * blend; lerpdata->origin[1] = e->previousorigin[1] + d[1] * blend; lerpdata->origin[2] = e->previousorigin[2] + d[2] * blend; //rotation VectorSubtract (e->currentangles, e->previousangles, d); for (i = 0; i < 3; i++) { if (d[i] > 180) d[i] -= 360; if (d[i] < -180) d[i] += 360; } lerpdata->angles[0] = e->previousangles[0] + d[0] * blend; lerpdata->angles[1] = e->previousangles[1] + d[1] * blend; lerpdata->angles[2] = e->previousangles[2] + d[2] * blend; } else //don't lerp { VectorCopy (e->origin, lerpdata->origin); VectorCopy (e->angles, lerpdata->angles); } } /* ================= R_SetupAliasLighting -- johnfitz -- broken out from R_DrawAliasModel and rewritten ================= */ void R_SetupAliasLighting (entity_t *e) { vec3_t dist; float add; int i; int quantizedangle; float radiansangle; R_LightPoint (e->origin); //add dlights for (i=0 ; i<MAX_DLIGHTS ; i++) { if (cl_dlights[i].die >= cl.time) { VectorSubtract (currententity->origin, cl_dlights[i].origin, dist); add = cl_dlights[i].radius - VectorLength(dist); if (add > 0) VectorMA (lightcolor, add, cl_dlights[i].color, lightcolor); } } // minimum light value on gun (24) if (e == &cl.viewent) { add = 72.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]); if (add > 0.0f) { lightcolor[0] += add / 3.0f; lightcolor[1] += add / 3.0f; lightcolor[2] += add / 3.0f; } } // minimum light value on players (8) if (currententity > cl_entities && currententity <= cl_entities + cl.maxclients) { add = 24.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]); if (add > 0.0f) { lightcolor[0] += add / 3.0f; lightcolor[1] += add / 3.0f; lightcolor[2] += add / 3.0f; } } // clamp lighting so it doesn't overbright as much (96) if (overbright) { add = 288.0f / (lightcolor[0] + lightcolor[1] + lightcolor[2]); if (add < 1.0f) VectorScale(lightcolor, add, lightcolor); } //hack up the brightness when fullbrights but no overbrights (256) if (gl_fullbrights.value && !gl_overbright_models.value) if (e->model->flags & MOD_FBRIGHTHACK) { lightcolor[0] = 256.0f; lightcolor[1] = 256.0f; lightcolor[2] = 256.0f; } quantizedangle = ((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1); //ericw -- shadevector is passed to the shader to compute shadedots inside the //shader, see GLAlias_CreateShaders() radiansangle = (quantizedangle / 16.0) * 2.0 * 3.14159; shadevector[0] = cos(-radiansangle); shadevector[1] = sin(-radiansangle); shadevector[2] = 1; VectorNormalize(shadevector); //ericw -- shadedots = r_avertexnormal_dots[quantizedangle]; VectorScale (lightcolor, 1.0f / 200.0f, lightcolor); } /* ================= R_DrawAliasModel -- johnfitz -- almost completely rewritten ================= */ void R_DrawAliasModel (entity_t *e) { aliashdr_t *paliashdr; int i, anim, skinnum; gltexture_t *tx, *fb; lerpdata_t lerpdata; qboolean alphatest = !!(e->model->flags & MF_HOLEY); // // setup pose/lerp data -- do it first so we don't miss updates due to culling // paliashdr = (aliashdr_t *)Mod_Extradata (e->model); R_SetupAliasFrame (paliashdr, e->frame, &lerpdata); R_SetupEntityTransform (e, &lerpdata); // // cull it // if (R_CullModelForEntity(e)) return; // // transform it // glPushMatrix (); R_RotateForEntity (lerpdata.origin, lerpdata.angles); glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); // // random stuff // if (gl_smoothmodels.value && !r_drawflat_cheatsafe) glShadeModel (GL_SMOOTH); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); overbright = gl_overbright_models.value; shading = true; // // set up for alpha blending // if (r_drawflat_cheatsafe || r_lightmap_cheatsafe) //no alpha in drawflat or lightmap mode entalpha = 1; else entalpha = ENTALPHA_DECODE(e->alpha); if (entalpha == 0) goto cleanup; if (entalpha < 1) { if (!gl_texture_env_combine) overbright = false; //overbright can't be done in a single pass without combiners glDepthMask(GL_FALSE); glEnable(GL_BLEND); } else if (alphatest) glEnable (GL_ALPHA_TEST); // // set up lighting // rs_aliaspolys += paliashdr->numtris; R_SetupAliasLighting (e); // // set up textures // GL_DisableMultitexture(); anim = (int)(cl.time*10) & 3; skinnum = e->skinnum; if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { Con_DPrintf ("R_DrawAliasModel: no such skin # %d for '%s'\n", skinnum, e->model->name); // ericw -- display skin 0 for winquake compatibility skinnum = 0; } tx = paliashdr->gltextures[skinnum][anim]; fb = paliashdr->fbtextures[skinnum][anim]; if (e->colormap != vid.colormap && !gl_nocolors.value) { i = e - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) tx = playertextures[i - 1]; } if (!gl_fullbrights.value) fb = NULL; // // draw it // if (r_drawflat_cheatsafe) { glDisable (GL_TEXTURE_2D); GL_DrawAliasFrame (paliashdr, lerpdata); glEnable (GL_TEXTURE_2D); srand((int) (cl.time * 1000)); //restore randomness } else if (r_fullbright_cheatsafe) { GL_Bind (tx); shading = false; glColor4f(1,1,1,entalpha); GL_DrawAliasFrame (paliashdr, lerpdata); if (fb) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind(fb); glEnable(GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glDepthMask(GL_FALSE); glColor3f(entalpha,entalpha,entalpha); Fog_StartAdditive (); GL_DrawAliasFrame (paliashdr, lerpdata); Fog_StopAdditive (); glDepthMask(GL_TRUE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } } else if (r_lightmap_cheatsafe) { glDisable (GL_TEXTURE_2D); shading = false; glColor3f(1,1,1); GL_DrawAliasFrame (paliashdr, lerpdata); glEnable (GL_TEXTURE_2D); } // call fast path if possible. if the shader compliation failed for some reason, // r_alias_program will be 0. else if (r_alias_program != 0) { GL_DrawAliasFrame_GLSL (paliashdr, lerpdata, tx, fb); } else if (overbright) { if (gl_texture_env_combine && gl_mtexable && gl_texture_env_add && fb) //case 1: everything in one pass { GL_Bind (tx); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (fb); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); glEnable(GL_BLEND); GL_DrawAliasFrame (paliashdr, lerpdata); glDisable(GL_BLEND); GL_DisableMultitexture(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (gl_texture_env_combine) //case 2: overbright in one pass, then fullbright pass { // first pass GL_Bind(tx); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f); GL_DrawAliasFrame (paliashdr, lerpdata); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // second pass if (fb) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind(fb); glEnable(GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glDepthMask(GL_FALSE); shading = false; glColor3f(entalpha,entalpha,entalpha); Fog_StartAdditive (); GL_DrawAliasFrame (paliashdr, lerpdata); Fog_StopAdditive (); glDepthMask(GL_TRUE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } } else //case 3: overbright in two passes, then fullbright pass { // first pass GL_Bind(tx); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DrawAliasFrame (paliashdr, lerpdata); // second pass -- additive with black fog, to double the object colors but not the fog color glEnable(GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glDepthMask(GL_FALSE); Fog_StartAdditive (); GL_DrawAliasFrame (paliashdr, lerpdata); Fog_StopAdditive (); glDepthMask(GL_TRUE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); // third pass if (fb) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind(fb); glEnable(GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glDepthMask(GL_FALSE); shading = false; glColor3f(entalpha,entalpha,entalpha); Fog_StartAdditive (); GL_DrawAliasFrame (paliashdr, lerpdata); Fog_StopAdditive (); glDepthMask(GL_TRUE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } } } else { if (gl_mtexable && gl_texture_env_add && fb) //case 4: fullbright mask using multitexture { GL_DisableMultitexture(); // selects TEXTURE0 GL_Bind (tx); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (fb); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); glEnable(GL_BLEND); GL_DrawAliasFrame (paliashdr, lerpdata); glDisable(GL_BLEND); GL_DisableMultitexture(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else //case 5: fullbright mask without multitexture { // first pass GL_Bind(tx); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DrawAliasFrame (paliashdr, lerpdata); // second pass if (fb) { GL_Bind(fb); glEnable(GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glDepthMask(GL_FALSE); shading = false; glColor3f(entalpha,entalpha,entalpha); Fog_StartAdditive (); GL_DrawAliasFrame (paliashdr, lerpdata); Fog_StopAdditive (); glDepthMask(GL_TRUE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } } } cleanup: glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glShadeModel (GL_FLAT); glDepthMask(GL_TRUE); glDisable(GL_BLEND); if (alphatest) glDisable (GL_ALPHA_TEST); glColor3f(1,1,1); glPopMatrix (); } //johnfitz -- values for shadow matrix #define SHADOW_SKEW_X -0.7 //skew along x axis. -0.7 to mimic glquake shadows #define SHADOW_SKEW_Y 0 //skew along y axis. 0 to mimic glquake shadows #define SHADOW_VSCALE 0 //0=completely flat #define SHADOW_HEIGHT 0.1 //how far above the floor to render the shadow //johnfitz /* ============= GL_DrawAliasShadow -- johnfitz -- rewritten TODO: orient shadow onto "lightplane" (a global mplane_t*) ============= */ void GL_DrawAliasShadow (entity_t *e) { float shadowmatrix[16] = {1, 0, 0, 0, 0, 1, 0, 0, SHADOW_SKEW_X, SHADOW_SKEW_Y, SHADOW_VSCALE, 0, 0, 0, SHADOW_HEIGHT, 1}; float lheight; aliashdr_t *paliashdr; lerpdata_t lerpdata; if (R_CullModelForEntity(e)) return; if (e == &cl.viewent || e->model->flags & MOD_NOSHADOW) return; entalpha = ENTALPHA_DECODE(e->alpha); if (entalpha == 0) return; paliashdr = (aliashdr_t *)Mod_Extradata (e->model); R_SetupAliasFrame (paliashdr, e->frame, &lerpdata); R_SetupEntityTransform (e, &lerpdata); R_LightPoint (e->origin); lheight = currententity->origin[2] - lightspot[2]; // set up matrix glPushMatrix (); glTranslatef (lerpdata.origin[0], lerpdata.origin[1], lerpdata.origin[2]); glTranslatef (0,0,-lheight); glMultMatrixf (shadowmatrix); glTranslatef (0,0,lheight); glRotatef (lerpdata.angles[1], 0, 0, 1); glRotatef (-lerpdata.angles[0], 0, 1, 0); glRotatef (lerpdata.angles[2], 1, 0, 0); glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); // draw it glDepthMask(GL_FALSE); glEnable (GL_BLEND); GL_DisableMultitexture (); glDisable (GL_TEXTURE_2D); shading = false; glColor4f(0,0,0,entalpha * 0.5); GL_DrawAliasFrame (paliashdr, lerpdata); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glDepthMask(GL_TRUE); //clean up glPopMatrix (); } /* ================= R_DrawAliasModel_ShowTris -- johnfitz ================= */ void R_DrawAliasModel_ShowTris (entity_t *e) { aliashdr_t *paliashdr; lerpdata_t lerpdata; if (R_CullModelForEntity(e)) return; paliashdr = (aliashdr_t *)Mod_Extradata (e->model); R_SetupAliasFrame (paliashdr, e->frame, &lerpdata); R_SetupEntityTransform (e, &lerpdata); glPushMatrix (); R_RotateForEntity (lerpdata.origin,lerpdata.angles); glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); shading = false; glColor3f(1,1,1); GL_DrawAliasFrame (paliashdr, lerpdata); glPopMatrix (); } �����������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/q_stdinc.h������������������������������������������������������������������0000644�0000000�0000000�00000016067�13164227721�015515� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * q_stdinc.h - includes the minimum necessary stdc headers, * defines common and / or missing types. * * NOTE: for net stuff use net_sys.h, * for byte order use q_endian.h, * for math stuff use mathlib.h, * for locale-insensitive ctype.h functions use q_ctype.h. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2007-2011 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QSTDINC_H #define __QSTDINC_H #include <sys/types.h> #include <stddef.h> #include <limits.h> #ifndef _WIN32 /* others we support without sys/param.h? */ #include <sys/param.h> #endif #include <stdio.h> /* NOTES on TYPE SIZES: Quake/Hexen II engine relied on 32 bit int type size with ILP32 (not LP32) model in mind. We now support LP64 and LLP64, too. We expect: sizeof (char) == 1 sizeof (short) == 2 sizeof (int) == 4 sizeof (float) == 4 sizeof (long) == 4 / 8 sizeof (pointer *) == 4 / 8 For this, we need stdint.h (or inttypes.h) FIXME: On some platforms, only inttypes.h is available. FIXME: Properly replace certain short and int usage with int16_t and int32_t. */ #if defined(_MSC_VER) && (_MSC_VER < 1600) /* MS Visual Studio provides stdint.h only starting with * version 2010. Even in VS2010, there is no inttypes.h.. */ #include "msinttypes/stdint.h" #else #include <stdint.h> #endif #include <stdlib.h> #include <stdarg.h> #include <string.h> /*==========================================================================*/ #ifndef NULL #if defined(__cplusplus) #define NULL 0 #else #define NULL ((void *)0) #endif #endif #define Q_MAXCHAR ((char)0x7f) #define Q_MAXSHORT ((short)0x7fff) #define Q_MAXINT ((int)0x7fffffff) #define Q_MAXLONG ((int)0x7fffffff) #define Q_MAXFLOAT ((int)0x7fffffff) #define Q_MINCHAR ((char)0x80) #define Q_MINSHORT ((short)0x8000) #define Q_MININT ((int)0x80000000) #define Q_MINLONG ((int)0x80000000) #define Q_MINFLOAT ((int)0x7fffffff) /* Make sure the types really have the right * sizes: These macros are from SDL headers. */ #define COMPILE_TIME_ASSERT(name, x) \ typedef int dummy_ ## name[(x) * 2 - 1] COMPILE_TIME_ASSERT(char, sizeof(char) == 1); COMPILE_TIME_ASSERT(float, sizeof(float) == 4); COMPILE_TIME_ASSERT(long, sizeof(long) >= 4); COMPILE_TIME_ASSERT(int, sizeof(int) == 4); COMPILE_TIME_ASSERT(short, sizeof(short) == 2); /* make sure enums are the size of ints for structure packing */ typedef enum { THE_DUMMY_VALUE } THE_DUMMY_ENUM; COMPILE_TIME_ASSERT(enum, sizeof(THE_DUMMY_ENUM) == sizeof(int)); /* Provide a substitute for offsetof() if we don't have one. * This variant works on most (but not *all*) systems... */ #ifndef offsetof #define offsetof(t,m) ((size_t)&(((t *)0)->m)) #endif /*==========================================================================*/ typedef unsigned char byte; #undef true #undef false #if defined(__cplusplus) /* some structures have qboolean members and the x86 asm code expect * those members to be 4 bytes long. therefore, qboolean must be 32 * bits and it can NOT be binary compatible with the 8 bit C++ bool. */ typedef int qboolean; COMPILE_TIME_ASSERT(falsehood, (0 == false)); COMPILE_TIME_ASSERT(truth, (1 == true)); #else typedef enum { false = 0, true = 1 } qboolean; COMPILE_TIME_ASSERT(falsehood, ((1 != 1) == false)); COMPILE_TIME_ASSERT(truth, ((1 == 1) == true)); #endif COMPILE_TIME_ASSERT(qboolean, sizeof(qboolean) == 4); /*==========================================================================*/ /* math */ typedef float vec_t; typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; typedef vec_t vec5_t[5]; typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; /*==========================================================================*/ /* MAX_OSPATH (max length of a filesystem pathname, i.e. PATH_MAX) * Note: See GNU Hurd and others' notes about brokenness of this: * http://www.gnu.org/software/hurd/community/gsoc/project_ideas/maxpath.html * http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html */ #if !defined(PATH_MAX) /* equivalent values? */ #if defined(MAXPATHLEN) #define PATH_MAX MAXPATHLEN #elif defined(_WIN32) && defined(_MAX_PATH) #define PATH_MAX _MAX_PATH #elif defined(_WIN32) && defined(MAX_PATH) #define PATH_MAX MAX_PATH #else /* fallback */ #define PATH_MAX 1024 #endif #endif /* PATH_MAX */ #define MAX_OSPATH PATH_MAX /*==========================================================================*/ /* missing types */ #if defined(_MSC_VER) #if defined(_WIN64) #define ssize_t SSIZE_T #else typedef int ssize_t; #endif /* _WIN64 */ #endif /* _MSC_VER */ /*==========================================================================*/ /* function attributes, etc */ #if defined(__GNUC__) #define FUNC_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) #else #define FUNC_PRINTF(x,y) #endif /* argument format attributes for function pointers are supported for gcc >= 3.1 */ #if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)) #define FUNCP_PRINTF FUNC_PRINTF #else #define FUNCP_PRINTF(x,y) #endif /* llvm's optnone function attribute started with clang-3.5.0 */ #if defined(__clang__) && \ (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5)) #define FUNC_NO_OPTIMIZE __attribute__((__optnone__)) /* function optimize attribute is added starting with gcc 4.4.0 */ #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3)) #define FUNC_NO_OPTIMIZE __attribute__((__optimize__("0"))) #else #define FUNC_NO_OPTIMIZE #endif #if defined(__GNUC__) #define FUNC_NORETURN __attribute__((__noreturn__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) #define FUNC_NORETURN __declspec(noreturn) #elif defined(__WATCOMC__) #define FUNC_NORETURN /* use the 'aborts' aux pragma */ #else #define FUNC_NORETURN #endif #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define FUNC_NOINLINE __attribute__((__noinline__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1300) #define FUNC_NOINLINE __declspec(noinline) #else #define FUNC_NOINLINE #endif #if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #define FUNC_NOCLONE __attribute__((__noclone__)) #else #define FUNC_NOCLONE #endif #if defined(_MSC_VER) && !defined(__cplusplus) #define inline __inline #endif /* _MSC_VER */ /*==========================================================================*/ #endif /* __QSTDINC_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/r_part.c��������������������������������������������������������������������0000644�0000000�0000000�00000052351�13136713740�015167� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #define MAX_PARTICLES 2048 // default max # of particles at one // time #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's // on the command line int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3}; particle_t *active_particles, *free_particles, *particles; vec3_t r_pright, r_pup, r_ppn; int r_numparticles; gltexture_t *particletexture, *particletexture1, *particletexture2, *particletexture3, *particletexture4; //johnfitz float texturescalefactor; //johnfitz -- compensate for apparent size of different particle textures cvar_t r_particles = {"r_particles","1", CVAR_ARCHIVE}; //johnfitz cvar_t r_quadparticles = {"r_quadparticles","1", CVAR_ARCHIVE}; //johnfitz /* =============== R_ParticleTextureLookup -- johnfitz -- generate nice antialiased 32x32 circle for particles =============== */ int R_ParticleTextureLookup (int x, int y, int sharpness) { int r; //distance from point x,y to circle origin, squared int a; //alpha value to return x -= 16; y -= 16; r = x * x + y * y; r = r > 255 ? 255 : r; a = sharpness * (255 - r); a = q_min(a,255); return a; } /* =============== R_InitParticleTextures -- johnfitz -- rewritten =============== */ void R_InitParticleTextures (void) { int x,y; static byte particle1_data[64*64*4]; static byte particle2_data[2*2*4]; static byte particle3_data[64*64*4]; byte *dst; // particle texture 1 -- circle dst = particle1_data; for (x=0 ; x<64 ; x++) for (y=0 ; y<64 ; y++) { *dst++ = 255; *dst++ = 255; *dst++ = 255; *dst++ = R_ParticleTextureLookup(x, y, 8); } particletexture1 = TexMgr_LoadImage (NULL, "particle1", 64, 64, SRC_RGBA, particle1_data, "", (src_offset_t)particle1_data, TEXPREF_PERSIST | TEXPREF_ALPHA | TEXPREF_LINEAR); // particle texture 2 -- square dst = particle2_data; for (x=0 ; x<2 ; x++) for (y=0 ; y<2 ; y++) { *dst++ = 255; *dst++ = 255; *dst++ = 255; *dst++ = x || y ? 0 : 255; } particletexture2 = TexMgr_LoadImage (NULL, "particle2", 2, 2, SRC_RGBA, particle2_data, "", (src_offset_t)particle2_data, TEXPREF_PERSIST | TEXPREF_ALPHA | TEXPREF_NEAREST); // particle texture 3 -- blob dst = particle3_data; for (x=0 ; x<64 ; x++) for (y=0 ; y<64 ; y++) { *dst++ = 255; *dst++ = 255; *dst++ = 255; *dst++ = R_ParticleTextureLookup(x, y, 2); } particletexture3 = TexMgr_LoadImage (NULL, "particle3", 64, 64, SRC_RGBA, particle3_data, "", (src_offset_t)particle3_data, TEXPREF_PERSIST | TEXPREF_ALPHA | TEXPREF_LINEAR); //set default particletexture = particletexture1; texturescalefactor = 1.27; } /* =============== R_SetParticleTexture_f -- johnfitz =============== */ static void R_SetParticleTexture_f (cvar_t *var) { switch ((int)(r_particles.value)) { case 1: particletexture = particletexture1; texturescalefactor = 1.27; break; case 2: particletexture = particletexture2; texturescalefactor = 1.0; break; // case 3: // particletexture = particletexture3; // texturescalefactor = 1.5; // break; } } /* =============== R_InitParticles =============== */ void R_InitParticles (void) { int i; i = COM_CheckParm ("-particles"); if (i) { r_numparticles = (int)(Q_atoi(com_argv[i+1])); if (r_numparticles < ABSOLUTE_MIN_PARTICLES) r_numparticles = ABSOLUTE_MIN_PARTICLES; } else { r_numparticles = MAX_PARTICLES; } particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); Cvar_RegisterVariable (&r_particles); //johnfitz Cvar_SetCallback (&r_particles, R_SetParticleTexture_f); Cvar_RegisterVariable (&r_quadparticles); //johnfitz R_InitParticleTextures (); //johnfitz } /* =============== R_EntityParticles =============== */ #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; vec3_t avelocities[NUMVERTEXNORMALS]; float beamlength = 16; vec3_t avelocity = {23, 7, 3}; float partstep = 0.01; float timescale = 0.01; void R_EntityParticles (entity_t *ent) { int i; particle_t *p; float angle; float sp, sy, cp, cy; // float sr, cr; // int count; vec3_t forward; float dist; dist = 64; // count = 50; if (!avelocities[0][0]) { for (i = 0; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (rand() & 255) * 0.01; avelocities[i][1] = (rand() & 255) * 0.01; avelocities[i][2] = (rand() & 255) * 0.01; } } for (i = 0; i < NUMVERTEXNORMALS; i++) { angle = cl.time * avelocities[i][0]; sy = sin(angle); cy = cos(angle); angle = cl.time * avelocities[i][1]; sp = sin(angle); cp = cos(angle); angle = cl.time * avelocities[i][2]; // sr = sin(angle); // cr = cos(angle); forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 0.01; p->color = 0x6f; p->type = pt_explode; p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; } } /* =============== R_ClearParticles =============== */ void R_ClearParticles (void) { int i; free_particles = &particles[0]; active_particles = NULL; for (i=0 ;i<r_numparticles ; i++) particles[i].next = &particles[i+1]; particles[r_numparticles-1].next = NULL; } /* =============== R_ReadPointFile_f =============== */ void R_ReadPointFile_f (void) { FILE *f; vec3_t org; int r; int c; particle_t *p; char name[MAX_QPATH]; if (cls.state != ca_connected) return; // need an active map. q_snprintf (name, sizeof(name), "maps/%s.pts", cl.mapname); COM_FOpenFile (name, &f, NULL); if (!f) { Con_Printf ("couldn't open %s\n", name); return; } Con_Printf ("Reading %s...\n", name); c = 0; org[0] = org[1] = org[2] = 0; // silence pesky compiler warnings for ( ;; ) { r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]); if (r != 3) break; c++; if (!free_particles) { Con_Printf ("Not enough free particles\n"); break; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = 99999; p->color = (-c)&15; p->type = pt_static; VectorCopy (vec3_origin, p->vel); VectorCopy (org, p->org); } fclose (f); Con_Printf ("%i points read\n", c); } /* =============== R_ParseParticleEffect Parse an effect out of the server message =============== */ void R_ParseParticleEffect (void) { vec3_t org, dir; int i, count, msgcount, color; for (i=0 ; i<3 ; i++) org[i] = MSG_ReadCoord (cl.protocolflags); for (i=0 ; i<3 ; i++) dir[i] = MSG_ReadChar () * (1.0/16); msgcount = MSG_ReadByte (); color = MSG_ReadByte (); if (msgcount == 255) count = 1024; else count = msgcount; R_RunParticleEffect (org, dir, color, count); } /* =============== R_ParticleExplosion =============== */ void R_ParticleExplosion (vec3_t org) { int i, j; particle_t *p; for (i=0 ; i<1024 ; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand()&3; if (i & 1) { p->type = pt_explode; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } else { p->type = pt_explode2; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } } /* =============== R_ParticleExplosion2 =============== */ void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) { int i, j; particle_t *p; int colorMod = 0; for (i=0; i<512; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 0.3; p->color = colorStart + (colorMod % colorLength); colorMod++; p->type = pt_blob; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } /* =============== R_BlobExplosion =============== */ void R_BlobExplosion (vec3_t org) { int i, j; particle_t *p; for (i=0 ; i<1024 ; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 1 + (rand()&8)*0.05; if (i & 1) { p->type = pt_blob; p->color = 66 + rand()%6; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } else { p->type = pt_blob2; p->color = 150 + rand()%6; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } } /* =============== R_RunParticleEffect =============== */ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) { int i, j; particle_t *p; for (i=0 ; i<count ; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; if (count == 1024) { // rocket explosion p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand()&3; if (i & 1) { p->type = pt_explode; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } else { p->type = pt_explode2; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } else { p->die = cl.time + 0.1*(rand()%5); p->color = (color&~7) + (rand()&7); p->type = pt_slowgrav; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()&15)-8); p->vel[j] = dir[j]*15;// + (rand()%300)-150; } } } } /* =============== R_LavaSplash =============== */ void R_LavaSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i=-16 ; i<16 ; i++) for (j=-16 ; j<16 ; j++) for (k=0 ; k<1 ; k++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 2 + (rand()&31) * 0.02; p->color = 224 + (rand()&7); p->type = pt_slowgrav; dir[0] = j*8 + (rand()&7); dir[1] = i*8 + (rand()&7); dir[2] = 256; p->org[0] = org[0] + dir[0]; p->org[1] = org[1] + dir[1]; p->org[2] = org[2] + (rand()&63); VectorNormalize (dir); vel = 50 + (rand()&63); VectorScale (dir, vel, p->vel); } } /* =============== R_TeleportSplash =============== */ void R_TeleportSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i=-16 ; i<16 ; i+=4) for (j=-16 ; j<16 ; j+=4) for (k=-24 ; k<32 ; k+=4) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 0.2 + (rand()&7) * 0.02; p->color = 7 + (rand()&7); p->type = pt_slowgrav; dir[0] = j*8; dir[1] = i*8; dir[2] = k*8; p->org[0] = org[0] + i + (rand()&3); p->org[1] = org[1] + j + (rand()&3); p->org[2] = org[2] + k + (rand()&3); VectorNormalize (dir); vel = 50 + (rand()&63); VectorScale (dir, vel, p->vel); } } /* =============== R_RocketTrail FIXME -- rename function and use #defined types instead of numbers =============== */ void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t vec; float len; int j; particle_t *p; int dec; static int tracercount; VectorSubtract (end, start, vec); len = VectorNormalize (vec); if (type < 128) dec = 3; else { dec = 1; type -= 128; } while (len > 0) { len -= dec; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorCopy (vec3_origin, p->vel); p->die = cl.time + 2; switch (type) { case 0: // rocket trail p->ramp = (rand()&3); p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 1: // smoke smoke p->ramp = (rand()&3) + 2; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 2: // blood p->type = pt_grav; p->color = 67 + (rand()&3); for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 3: case 5: // tracer p->die = cl.time + 0.5; p->type = pt_static; if (type == 3) p->color = 52 + ((tracercount&4)<<1); else p->color = 230 + ((tracercount&4)<<1); tracercount++; VectorCopy (start, p->org); if (tracercount & 1) { p->vel[0] = 30*vec[1]; p->vel[1] = 30*-vec[0]; } else { p->vel[0] = 30*-vec[1]; p->vel[1] = 30*vec[0]; } break; case 4: // slight blood p->type = pt_grav; p->color = 67 + (rand()&3); for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); len -= 3; break; case 6: // voor trail p->color = 9*16 + 8 + (rand()&3); p->type = pt_static; p->die = cl.time + 0.3; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()&15)-8); break; } VectorAdd (start, vec, start); } } /* =============== CL_RunParticles -- johnfitz -- all the particle behavior, separated from R_DrawParticles =============== */ void CL_RunParticles (void) { particle_t *p, *kill; int i; float time1, time2, time3, dvel, frametime, grav; extern cvar_t sv_gravity; frametime = cl.time - cl.oldtime; time3 = frametime * 15; time2 = frametime * 10; time1 = frametime * 5; grav = frametime * sv_gravity.value * 0.05; dvel = 4*frametime; for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p=active_particles ; p ; p=p->next) { for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if (p->ramp >= 6) p->die = -1; else p->color = ramp3[(int)p->ramp]; p->vel[2] += grav; break; case pt_explode: p->ramp += time2; if (p->ramp >=8) p->die = -1; else p->color = ramp1[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; if (p->ramp >=8) p->die = -1; else p->color = ramp2[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] -= p->vel[i]*frametime; p->vel[2] -= grav; break; case pt_blob: for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_blob2: for (i=0 ; i<2 ; i++) p->vel[i] -= p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_grav: case pt_slowgrav: p->vel[2] -= grav; break; } } } /* =============== R_DrawParticles -- johnfitz -- moved all non-drawing code to CL_RunParticles =============== */ void R_DrawParticles (void) { particle_t *p; float scale; vec3_t up, right, p_up, p_right, p_upright; //johnfitz -- p_ vectors GLubyte color[4], *c; //johnfitz -- particle transparency extern cvar_t r_particles; //johnfitz //float alpha; //johnfitz -- particle transparency if (!r_particles.value) return; //ericw -- avoid empty glBegin(),glEnd() pair below; causes issues on AMD if (!active_particles) return; VectorScale (vup, 1.5, up); VectorScale (vright, 1.5, right); GL_Bind(particletexture); glEnable (GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDepthMask (GL_FALSE); //johnfitz -- fix for particle z-buffer bug if (r_quadparticles.value) //johnitz -- quads save fillrate { glBegin (GL_QUADS); for (p=active_particles ; p ; p=p->next) { // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] - r_origin[1]) * vpn[1] + (p->org[2] - r_origin[2]) * vpn[2]; if (scale < 20) scale = 1 + 0.08; //johnfitz -- added .08 to be consistent else scale = 1 + scale * 0.004; scale /= 2.0; //quad is half the size of triangle scale *= texturescalefactor; //johnfitz -- compensate for apparent size of different particle textures //johnfitz -- particle transparency and fade out c = (GLubyte *) &d_8to24table[(int)p->color]; color[0] = c[0]; color[1] = c[1]; color[2] = c[2]; //alpha = CLAMP(0, p->die + 0.5 - cl.time, 1); color[3] = 255; //(int)(alpha * 255); glColor4ubv(color); //johnfitz glTexCoord2f (0,0); glVertex3fv (p->org); glTexCoord2f (0.5,0); VectorMA (p->org, scale, up, p_up); glVertex3fv (p_up); glTexCoord2f (0.5,0.5); VectorMA (p_up, scale, right, p_upright); glVertex3fv (p_upright); glTexCoord2f (0,0.5); VectorMA (p->org, scale, right, p_right); glVertex3fv (p_right); rs_particles++; //johnfitz //FIXME: just use r_numparticles } glEnd (); } else //johnitz -- triangles save verts { glBegin (GL_TRIANGLES); for (p=active_particles ; p ; p=p->next) { // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] - r_origin[1]) * vpn[1] + (p->org[2] - r_origin[2]) * vpn[2]; if (scale < 20) scale = 1 + 0.08; //johnfitz -- added .08 to be consistent else scale = 1 + scale * 0.004; scale *= texturescalefactor; //johnfitz -- compensate for apparent size of different particle textures //johnfitz -- particle transparency and fade out c = (GLubyte *) &d_8to24table[(int)p->color]; color[0] = c[0]; color[1] = c[1]; color[2] = c[2]; //alpha = CLAMP(0, p->die + 0.5 - cl.time, 1); color[3] = 255; //(int)(alpha * 255); glColor4ubv(color); //johnfitz glTexCoord2f (0,0); glVertex3fv (p->org); glTexCoord2f (1,0); VectorMA (p->org, scale, up, p_up); glVertex3fv (p_up); glTexCoord2f (0,1); VectorMA (p->org, scale, right, p_right); glVertex3fv (p_right); rs_particles++; //johnfitz //FIXME: just use r_numparticles } glEnd (); } glDepthMask (GL_TRUE); //johnfitz -- fix for particle z-buffer bug glDisable (GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1,1,1); } /* =============== R_DrawParticles_ShowTris -- johnfitz =============== */ void R_DrawParticles_ShowTris (void) { particle_t *p; float scale; vec3_t up, right, p_up, p_right, p_upright; extern cvar_t r_particles; if (!r_particles.value) return; VectorScale (vup, 1.5, up); VectorScale (vright, 1.5, right); if (r_quadparticles.value) { for (p=active_particles ; p ; p=p->next) { glBegin (GL_TRIANGLE_FAN); // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] - r_origin[1]) * vpn[1] + (p->org[2] - r_origin[2]) * vpn[2]; if (scale < 20) scale = 1 + 0.08; //johnfitz -- added .08 to be consistent else scale = 1 + scale * 0.004; scale /= 2.0; //quad is half the size of triangle scale *= texturescalefactor; //compensate for apparent size of different particle textures glVertex3fv (p->org); VectorMA (p->org, scale, up, p_up); glVertex3fv (p_up); VectorMA (p_up, scale, right, p_upright); glVertex3fv (p_upright); VectorMA (p->org, scale, right, p_right); glVertex3fv (p_right); glEnd (); } } else { glBegin (GL_TRIANGLES); for (p=active_particles ; p ; p=p->next) { // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] - r_origin[1]) * vpn[1] + (p->org[2] - r_origin[2]) * vpn[2]; if (scale < 20) scale = 1 + 0.08; //johnfitz -- added .08 to be consistent else scale = 1 + scale * 0.004; scale *= texturescalefactor; //compensate for apparent size of different particle textures glVertex3fv (p->org); VectorMA (p->org, scale, up, p_up); glVertex3fv (p_up); VectorMA (p->org, scale, right, p_right); glVertex3fv (p_right); } glEnd (); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pr_cmds.c�������������������������������������������������������������������0000644�0000000�0000000�00000104001�13136015044�015305� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #define STRINGTEMP_BUFFERS 16 #define STRINGTEMP_LENGTH 1024 static char pr_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH]; static byte pr_string_tempindex = 0; static char *PR_GetTempString (void) { return pr_string_temp[(STRINGTEMP_BUFFERS-1) & ++pr_string_tempindex]; } #define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) #define MSG_BROADCAST 0 // unreliable to all #define MSG_ONE 1 // reliable to one (msg_entity) #define MSG_ALL 2 // reliable to all #define MSG_INIT 3 // write to the init string /* =============================================================================== BUILT-IN FUNCTIONS =============================================================================== */ static char *PF_VarString (int first) { int i; static char out[1024]; size_t s; out[0] = 0; s = 0; for (i = first; i < pr_argc; i++) { s = q_strlcat(out, G_STRING((OFS_PARM0+i*3)), sizeof(out)); if (s >= sizeof(out)) { Con_Warning("PF_VarString: overflow (string truncated)\n"); return out; } } if (s > 255) { if (!dev_overflows.varstring || dev_overflows.varstring + CONSOLE_RESPAM_TIME < realtime) { Con_DWarning("PF_VarString: %i characters exceeds standard limit of 255 (max = %d).\n", (int) s, (int)(sizeof(out) - 1)); dev_overflows.varstring = realtime; } } return out; } /* ================= PF_error This is a TERMINAL error, which will kill off the entire server. Dumps self. error(value) ================= */ static void PF_error (void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf ("======SERVER ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s); ed = PROG_TO_EDICT(pr_global_struct->self); ED_Print (ed); Host_Error ("Program error"); } /* ================= PF_objerror Dumps out self, then an error message. The program is aborted and self is removed, but the level can continue. objerror(value) ================= */ static void PF_objerror (void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s); ed = PROG_TO_EDICT(pr_global_struct->self); ED_Print (ed); ED_Free (ed); //Host_Error ("Program error"); //johnfitz -- by design, this should not be fatal } /* ============== PF_makevectors Writes new values for v_forward, v_up, and v_right based on angles makevectors(vector) ============== */ static void PF_makevectors (void) { AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); } /* ================= PF_setorigin This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. setorigin (entity, origin) ================= */ static void PF_setorigin (void) { edict_t *e; float *org; e = G_EDICT(OFS_PARM0); org = G_VECTOR(OFS_PARM1); VectorCopy (org, e->v.origin); SV_LinkEdict (e, false); } static void SetMinMaxSize (edict_t *e, float *minvec, float *maxvec, qboolean rotate) { float *angles; vec3_t rmin, rmax; float bounds[2][3]; float xvector[2], yvector[2]; float a; vec3_t base, transformed; int i, j, k, l; for (i = 0; i < 3; i++) if (minvec[i] > maxvec[i]) PR_RunError ("backwards mins/maxs"); rotate = false; // FIXME: implement rotation properly again if (!rotate) { VectorCopy (minvec, rmin); VectorCopy (maxvec, rmax); } else { // find min / max for rotations angles = e->v.angles; a = angles[1]/180 * M_PI; xvector[0] = cos(a); xvector[1] = sin(a); yvector[0] = -sin(a); yvector[1] = cos(a); VectorCopy (minvec, bounds[0]); VectorCopy (maxvec, bounds[1]); rmin[0] = rmin[1] = rmin[2] = 9999; rmax[0] = rmax[1] = rmax[2] = -9999; for (i = 0; i <= 1; i++) { base[0] = bounds[i][0]; for (j = 0; j <= 1; j++) { base[1] = bounds[j][1]; for (k = 0; k <= 1; k++) { base[2] = bounds[k][2]; // transform the point transformed[0] = xvector[0]*base[0] + yvector[0]*base[1]; transformed[1] = xvector[1]*base[0] + yvector[1]*base[1]; transformed[2] = base[2]; for (l = 0; l < 3; l++) { if (transformed[l] < rmin[l]) rmin[l] = transformed[l]; if (transformed[l] > rmax[l]) rmax[l] = transformed[l]; } } } } } // set derived values VectorCopy (rmin, e->v.mins); VectorCopy (rmax, e->v.maxs); VectorSubtract (maxvec, minvec, e->v.size); SV_LinkEdict (e, false); } /* ================= PF_setsize the size box is rotated by the current angle setsize (entity, minvector, maxvector) ================= */ static void PF_setsize (void) { edict_t *e; float *minvec, *maxvec; e = G_EDICT(OFS_PARM0); minvec = G_VECTOR(OFS_PARM1); maxvec = G_VECTOR(OFS_PARM2); SetMinMaxSize (e, minvec, maxvec, false); } /* ================= PF_setmodel setmodel(entity, model) ================= */ static void PF_setmodel (void) { int i; const char *m, **check; qmodel_t *mod; edict_t *e; e = G_EDICT(OFS_PARM0); m = G_STRING(OFS_PARM1); // check to see if model was properly precached for (i = 0, check = sv.model_precache; *check; i++, check++) { if (!strcmp(*check, m)) break; } if (!*check) { PR_RunError ("no precache: %s", m); } e->v.model = PR_SetEngineString(*check); e->v.modelindex = i; //SV_ModelIndex (m); mod = sv.models[ (int)e->v.modelindex]; // Mod_ForName (m, true); if (mod) //johnfitz -- correct physics cullboxes for bmodels { if (mod->type == mod_brush) SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true); else SetMinMaxSize (e, mod->mins, mod->maxs, true); } //johnfitz else SetMinMaxSize (e, vec3_origin, vec3_origin, true); } /* ================= PF_bprint broadcast print to everyone on server bprint(value) ================= */ static void PF_bprint (void) { char *s; s = PF_VarString(0); SV_BroadcastPrintf ("%s", s); } /* ================= PF_sprint single print to a specific client sprint(clientent, value) ================= */ static void PF_sprint (void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > svs.maxclients) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; MSG_WriteChar (&client->message,svc_print); MSG_WriteString (&client->message, s ); } /* ================= PF_centerprint single print to a specific client centerprint(clientent, value) ================= */ static void PF_centerprint (void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > svs.maxclients) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; MSG_WriteChar (&client->message,svc_centerprint); MSG_WriteString (&client->message, s); } /* ================= PF_normalize vector normalize(vector) ================= */ static void PF_normalize (void) { float *value1; vec3_t newvalue; float new_temp; value1 = G_VECTOR(OFS_PARM0); new_temp = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; new_temp = sqrt (new_temp); if (new_temp == 0) newvalue[0] = newvalue[1] = newvalue[2] = 0; else { new_temp = 1 / new_temp; newvalue[0] = value1[0] * new_temp; newvalue[1] = value1[1] * new_temp; newvalue[2] = value1[2] * new_temp; } VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); } /* ================= PF_vlen scalar vlen(vector) ================= */ static void PF_vlen (void) { float *value1; float new_temp; value1 = G_VECTOR(OFS_PARM0); new_temp = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; new_temp = sqrt(new_temp); G_FLOAT(OFS_RETURN) = new_temp; } /* ================= PF_vectoyaw float vectoyaw(vector) ================= */ static void PF_vectoyaw (void) { float *value1; float yaw; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) yaw = 0; else { yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; } G_FLOAT(OFS_RETURN) = yaw; } /* ================= PF_vectoangles vector vectoangles(vector) ================= */ static void PF_vectoangles (void) { float *value1; float forward; float yaw, pitch; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) { yaw = 0; if (value1[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } G_FLOAT(OFS_RETURN+0) = pitch; G_FLOAT(OFS_RETURN+1) = yaw; G_FLOAT(OFS_RETURN+2) = 0; } /* ================= PF_Random Returns a number from 0 <= num < 1 random() ================= */ static void PF_random (void) { float num; num = (rand() & 0x7fff) / ((float)0x7fff); G_FLOAT(OFS_RETURN) = num; } /* ================= PF_particle particle(origin, color, count) ================= */ static void PF_particle (void) { float *org, *dir; float color; float count; org = G_VECTOR(OFS_PARM0); dir = G_VECTOR(OFS_PARM1); color = G_FLOAT(OFS_PARM2); count = G_FLOAT(OFS_PARM3); SV_StartParticle (org, dir, color, count); } /* ================= PF_ambientsound ================= */ static void PF_ambientsound (void) { const char *samp, **check; float *pos; float vol, attenuation; int i, soundnum; int large = false; //johnfitz -- PROTOCOL_FITZQUAKE pos = G_VECTOR (OFS_PARM0); samp = G_STRING(OFS_PARM1); vol = G_FLOAT(OFS_PARM2); attenuation = G_FLOAT(OFS_PARM3); // check to see if samp was properly precached for (soundnum = 0, check = sv.sound_precache; *check; check++, soundnum++) { if (!strcmp(*check, samp)) break; } if (!*check) { Con_Printf ("no precache: %s\n", samp); return; } //johnfitz -- PROTOCOL_FITZQUAKE if (soundnum > 255) { if (sv.protocol == PROTOCOL_NETQUAKE) return; //don't send any info protocol can't support else large = true; } //johnfitz // add an svc_spawnambient command to the level signon packet //johnfitz -- PROTOCOL_FITZQUAKE if (large) MSG_WriteByte (&sv.signon,svc_spawnstaticsound2); else MSG_WriteByte (&sv.signon,svc_spawnstaticsound); //johnfitz for (i = 0; i < 3; i++) MSG_WriteCoord(&sv.signon, pos[i], sv.protocolflags); //johnfitz -- PROTOCOL_FITZQUAKE if (large) MSG_WriteShort(&sv.signon, soundnum); else MSG_WriteByte (&sv.signon, soundnum); //johnfitz MSG_WriteByte (&sv.signon, vol*255); MSG_WriteByte (&sv.signon, attenuation*64); } /* ================= PF_sound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. ================= */ static void PF_sound (void) { const char *sample; int channel; edict_t *entity; int volume; float attenuation; entity = G_EDICT(OFS_PARM0); channel = G_FLOAT(OFS_PARM1); sample = G_STRING(OFS_PARM2); volume = G_FLOAT(OFS_PARM3) * 255; attenuation = G_FLOAT(OFS_PARM4); if (volume < 0 || volume > 255) Host_Error ("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) Host_Error ("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 7) Host_Error ("SV_StartSound: channel = %i", channel); SV_StartSound (entity, channel, sample, volume, attenuation); } /* ================= PF_break break() ================= */ static void PF_break (void) { Con_Printf ("break statement\n"); *(int *)-4 = 0; // dump to debugger // PR_RunError ("break statement"); } /* ================= PF_traceline Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. traceline (vector1, vector2, tryents) ================= */ static void PF_traceline (void) { float *v1, *v2; trace_t trace; int nomonsters; edict_t *ent; v1 = G_VECTOR(OFS_PARM0); v2 = G_VECTOR(OFS_PARM1); nomonsters = G_FLOAT(OFS_PARM2); ent = G_EDICT(OFS_PARM3); /* FIXME FIXME FIXME: Why do we hit this with certain progs.dat ?? */ if (developer.value) { if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2])) { Con_Warning ("NAN in traceline:\nv1(%f %f %f) v2(%f %f %f)\nentity %d\n", v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], NUM_FOR_EDICT(ent)); } } if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2])) v1[0] = v1[1] = v1[2] = 0; if (IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2])) v2[0] = v2[1] = v2[2] = 0; trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); pr_global_struct->trace_allsolid = trace.allsolid; pr_global_struct->trace_startsolid = trace.startsolid; pr_global_struct->trace_fraction = trace.fraction; pr_global_struct->trace_inwater = trace.inwater; pr_global_struct->trace_inopen = trace.inopen; VectorCopy (trace.endpos, pr_global_struct->trace_endpos); VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal); pr_global_struct->trace_plane_dist = trace.plane.dist; if (trace.ent) pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); else pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); } /* ================= PF_checkpos Returns true if the given entity can move to the given position from it's current position by walking or rolling. FIXME: make work... scalar checkpos (entity, vector) ================= */ #if 0 static void PF_checkpos (void) { } #endif //============================================================================ static byte *checkpvs; //ericw -- changed to malloc static int checkpvs_capacity; static int PF_newcheckclient (int check) { int i; byte *pvs; edict_t *ent; mleaf_t *leaf; vec3_t org; int pvsbytes; // cycle to the next one if (check < 1) check = 1; if (check > svs.maxclients) check = svs.maxclients; if (check == svs.maxclients) i = 1; else i = check + 1; for ( ; ; i++) { if (i == svs.maxclients+1) i = 1; ent = EDICT_NUM(i); if (i == check) break; // didn't find anything else if (ent->free) continue; if (ent->v.health <= 0) continue; if ((int)ent->v.flags & FL_NOTARGET) continue; // anything that is a client, or has a client as an enemy break; } // get the PVS for the entity VectorAdd (ent->v.origin, ent->v.view_ofs, org); leaf = Mod_PointInLeaf (org, sv.worldmodel); pvs = Mod_LeafPVS (leaf, sv.worldmodel); pvsbytes = (sv.worldmodel->numleafs+7)>>3; if (checkpvs == NULL || pvsbytes > checkpvs_capacity) { checkpvs_capacity = pvsbytes; checkpvs = (byte *) realloc (checkpvs, checkpvs_capacity); if (!checkpvs) Sys_Error ("PF_newcheckclient: realloc() failed on %d bytes", checkpvs_capacity); } memcpy (checkpvs, pvs, pvsbytes); return i; } /* ================= PF_checkclient Returns a client (or object that has a client enemy) that would be a valid target. If there are more than one valid options, they are cycled each frame If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all. name checkclient () ================= */ #define MAX_CHECK 16 static int c_invis, c_notvis; static void PF_checkclient (void) { edict_t *ent, *self; mleaf_t *leaf; int l; vec3_t view; // find a new check if on a new frame if (sv.time - sv.lastchecktime >= 0.1) { sv.lastcheck = PF_newcheckclient (sv.lastcheck); sv.lastchecktime = sv.time; } // return check if it might be visible ent = EDICT_NUM(sv.lastcheck); if (ent->free || ent->v.health <= 0) { RETURN_EDICT(sv.edicts); return; } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT(pr_global_struct->self); VectorAdd (self->v.origin, self->v.view_ofs, view); leaf = Mod_PointInLeaf (view, sv.worldmodel); l = (leaf - sv.worldmodel->leafs) - 1; if ( (l < 0) || !(checkpvs[l>>3] & (1 << (l & 7))) ) { c_notvis++; RETURN_EDICT(sv.edicts); return; } // might be able to see it c_invis++; RETURN_EDICT(ent); } //============================================================================ /* ================= PF_stuffcmd Sends text over to the client's execution buffer stuffcmd (clientent, value) ================= */ static void PF_stuffcmd (void) { int entnum; const char *str; client_t *old; entnum = G_EDICTNUM(OFS_PARM0); if (entnum < 1 || entnum > svs.maxclients) PR_RunError ("Parm 0 not a client"); str = G_STRING(OFS_PARM1); old = host_client; host_client = &svs.clients[entnum-1]; Host_ClientCommands ("%s", str); host_client = old; } /* ================= PF_localcmd Sends text over to the client's execution buffer localcmd (string) ================= */ static void PF_localcmd (void) { const char *str; str = G_STRING(OFS_PARM0); Cbuf_AddText (str); } /* ================= PF_cvar float cvar (string) ================= */ static void PF_cvar (void) { const char *str; str = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); } /* ================= PF_cvar_set float cvar (string) ================= */ static void PF_cvar_set (void) { const char *var, *val; var = G_STRING(OFS_PARM0); val = G_STRING(OFS_PARM1); Cvar_Set (var, val); } /* ================= PF_findradius Returns a chain of entities that have origins within a spherical area findradius (origin, radius) ================= */ static void PF_findradius (void) { edict_t *ent, *chain; float rad; float *org; vec3_t eorg; int i, j; chain = (edict_t *)sv.edicts; org = G_VECTOR(OFS_PARM0); rad = G_FLOAT(OFS_PARM1); ent = NEXT_EDICT(sv.edicts); for (i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; if (ent->v.solid == SOLID_NOT) continue; for (j = 0; j < 3; j++) eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j]) * 0.5); if (VectorLength(eorg) > rad) continue; ent->v.chain = EDICT_TO_PROG(chain); chain = ent; } RETURN_EDICT(chain); } /* ========= PF_dprint ========= */ static void PF_dprint (void) { Con_DPrintf ("%s",PF_VarString(0)); } static void PF_ftos (void) { float v; char *s; v = G_FLOAT(OFS_PARM0); s = PR_GetTempString(); if (v == (int)v) sprintf (s, "%d",(int)v); else sprintf (s, "%5.1f",v); G_INT(OFS_RETURN) = PR_SetEngineString(s); } static void PF_fabs (void) { float v; v = G_FLOAT(OFS_PARM0); G_FLOAT(OFS_RETURN) = fabs(v); } static void PF_vtos (void) { char *s; s = PR_GetTempString(); sprintf (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); G_INT(OFS_RETURN) = PR_SetEngineString(s); } static void PF_Spawn (void) { edict_t *ed; ed = ED_Alloc(); RETURN_EDICT(ed); } static void PF_Remove (void) { edict_t *ed; ed = G_EDICT(OFS_PARM0); ED_Free (ed); } // entity (entity start, .string field, string match) find = #5; static void PF_Find (void) { int e; int f; const char *s, *t; edict_t *ed; e = G_EDICTNUM(OFS_PARM0); f = G_INT(OFS_PARM1); s = G_STRING(OFS_PARM2); if (!s) PR_RunError ("PF_Find: bad search string"); for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); if (ed->free) continue; t = E_STRING(ed,f); if (!t) continue; if (!strcmp(t,s)) { RETURN_EDICT(ed); return; } } RETURN_EDICT(sv.edicts); } static void PR_CheckEmptyString (const char *s) { if (s[0] <= ' ') PR_RunError ("Bad string"); } static void PF_precache_file (void) { // precache_file is only used to copy files with qcc, it does nothing G_INT(OFS_RETURN) = G_INT(OFS_PARM0); } static void PF_precache_sound (void) { const char *s; int i; if (sv.state != ss_loading) PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString (s); for (i = 0; i < MAX_SOUNDS; i++) { if (!sv.sound_precache[i]) { sv.sound_precache[i] = s; return; } if (!strcmp(sv.sound_precache[i], s)) return; } PR_RunError ("PF_precache_sound: overflow"); } static void PF_precache_model (void) { const char *s; int i; if (sv.state != ss_loading) PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString (s); for (i = 0; i < MAX_MODELS; i++) { if (!sv.model_precache[i]) { sv.model_precache[i] = s; sv.models[i] = Mod_ForName (s, true); return; } if (!strcmp(sv.model_precache[i], s)) return; } PR_RunError ("PF_precache_model: overflow"); } static void PF_coredump (void) { ED_PrintEdicts (); } static void PF_traceon (void) { pr_trace = true; } static void PF_traceoff (void) { pr_trace = false; } static void PF_eprint (void) { ED_PrintNum (G_EDICTNUM(OFS_PARM0)); } /* =============== PF_walkmove float(float yaw, float dist) walkmove =============== */ static void PF_walkmove (void) { edict_t *ent; float yaw, dist; vec3_t move; dfunction_t *oldf; int oldself; ent = PROG_TO_EDICT(pr_global_struct->self); yaw = G_FLOAT(OFS_PARM0); dist = G_FLOAT(OFS_PARM1); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; // save program state, because SV_movestep may call other progs oldf = pr_xfunction; oldself = pr_global_struct->self; G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true); // restore program state pr_xfunction = oldf; pr_global_struct->self = oldself; } /* =============== PF_droptofloor void() droptofloor =============== */ static void PF_droptofloor (void) { edict_t *ent; vec3_t end; trace_t trace; ent = PROG_TO_EDICT(pr_global_struct->self); VectorCopy (ent->v.origin, end); end[2] -= 256; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; else { VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, false); ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); G_FLOAT(OFS_RETURN) = 1; } } /* =============== PF_lightstyle void(float style, string value) lightstyle =============== */ static void PF_lightstyle (void) { int style; const char *val; client_t *client; int j; style = G_FLOAT(OFS_PARM0); val = G_STRING(OFS_PARM1); // bounds check to avoid clobbering sv struct if (style < 0 || style >= MAX_LIGHTSTYLES) { Con_DWarning("PF_lightstyle: invalid style %d\n", style); return; } // change the string in sv sv.lightstyles[style] = val; // send message to all clients on this server if (sv.state != ss_active) return; for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (client->active || client->spawned) { MSG_WriteChar (&client->message, svc_lightstyle); MSG_WriteChar (&client->message, style); MSG_WriteString (&client->message, val); } } } static void PF_rint (void) { float f; f = G_FLOAT(OFS_PARM0); if (f > 0) G_FLOAT(OFS_RETURN) = (int)(f + 0.5); else G_FLOAT(OFS_RETURN) = (int)(f - 0.5); } static void PF_floor (void) { G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); } static void PF_ceil (void) { G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); } /* ============= PF_checkbottom ============= */ static void PF_checkbottom (void) { edict_t *ent; ent = G_EDICT(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); } /* ============= PF_pointcontents ============= */ static void PF_pointcontents (void) { float *v; v = G_VECTOR(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_PointContents (v); } /* ============= PF_nextent entity nextent(entity) ============= */ static void PF_nextent (void) { int i; edict_t *ent; i = G_EDICTNUM(OFS_PARM0); while (1) { i++; if (i == sv.num_edicts) { RETURN_EDICT(sv.edicts); return; } ent = EDICT_NUM(i); if (!ent->free) { RETURN_EDICT(ent); return; } } } /* ============= PF_aim Pick a vector for the player to shoot along vector aim(entity, missilespeed) ============= */ cvar_t sv_aim = {"sv_aim", "1", CVAR_NONE}; // ericw -- turn autoaim off by default. was 0.93 static void PF_aim (void) { edict_t *ent, *check, *bestent; vec3_t start, dir, end, bestdir; int i, j; trace_t tr; float dist, bestdist; float speed; ent = G_EDICT(OFS_PARM0); speed = G_FLOAT(OFS_PARM1); (void) speed; /* variable set but not used */ VectorCopy (ent->v.origin, start); start[2] += 20; // try sending a trace straight VectorCopy (pr_global_struct->v_forward, dir); VectorMA (start, 2048, dir, end); tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM && (!teamplay.value || ent->v.team <= 0 || ent->v.team != tr.ent->v.team) ) { VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); return; } // try all possible entities VectorCopy (dir, bestdir); bestdist = sv_aim.value; bestent = NULL; check = NEXT_EDICT(sv.edicts); for (i = 1; i < sv.num_edicts; i++, check = NEXT_EDICT(check) ) { if (check->v.takedamage != DAMAGE_AIM) continue; if (check == ent) continue; if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team) continue; // don't aim at teammate for (j = 0; j < 3; j++) end[j] = check->v.origin[j] + 0.5 * (check->v.mins[j] + check->v.maxs[j]); VectorSubtract (end, start, dir); VectorNormalize (dir); dist = DotProduct (dir, pr_global_struct->v_forward); if (dist < bestdist) continue; // to far to turn tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent == check) { // can shoot at this one bestdist = dist; bestent = check; } } if (bestent) { VectorSubtract (bestent->v.origin, ent->v.origin, dir); dist = DotProduct (dir, pr_global_struct->v_forward); VectorScale (pr_global_struct->v_forward, dist, end); end[2] = dir[2]; VectorNormalize (end); VectorCopy (end, G_VECTOR(OFS_RETURN)); } else { VectorCopy (bestdir, G_VECTOR(OFS_RETURN)); } } /* ============== PF_changeyaw This was a major timewaster in progs, so it was converted to C ============== */ void PF_changeyaw (void) { edict_t *ent; float ideal, current, move, speed; ent = PROG_TO_EDICT(pr_global_struct->self); current = anglemod( ent->v.angles[1] ); ideal = ent->v.ideal_yaw; speed = ent->v.yaw_speed; if (current == ideal) return; move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->v.angles[1] = anglemod (current + move); } /* =============================================================================== MESSAGE WRITING =============================================================================== */ static sizebuf_t *WriteDest (void) { int entnum; int dest; edict_t *ent; dest = G_FLOAT(OFS_PARM0); switch (dest) { case MSG_BROADCAST: return &sv.datagram; case MSG_ONE: ent = PROG_TO_EDICT(pr_global_struct->msg_entity); entnum = NUM_FOR_EDICT(ent); if (entnum < 1 || entnum > svs.maxclients) PR_RunError ("WriteDest: not a client"); return &svs.clients[entnum-1].message; case MSG_ALL: return &sv.reliable_datagram; case MSG_INIT: return &sv.signon; default: PR_RunError ("WriteDest: bad destination"); break; } return NULL; } static void PF_WriteByte (void) { MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteChar (void) { MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteShort (void) { MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteLong (void) { MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteAngle (void) { MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1), sv.protocolflags); } static void PF_WriteCoord (void) { MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1), sv.protocolflags); } static void PF_WriteString (void) { MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); } static void PF_WriteEntity (void) { MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); } //============================================================================= static void PF_makestatic (void) { edict_t *ent; int i; int bits = 0; //johnfitz -- PROTOCOL_FITZQUAKE ent = G_EDICT(OFS_PARM0); //johnfitz -- don't send invisible static entities if (ent->alpha == ENTALPHA_ZERO) { ED_Free (ent); return; } //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE if (sv.protocol == PROTOCOL_NETQUAKE) { if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00 || (int)(ent->v.frame) & 0xFF00) { ED_Free (ent); return; //can't display the correct model & frame, so don't show it at all } } else { if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00) bits |= B_LARGEMODEL; if ((int)(ent->v.frame) & 0xFF00) bits |= B_LARGEFRAME; if (ent->alpha != ENTALPHA_DEFAULT) bits |= B_ALPHA; } if (bits) { MSG_WriteByte (&sv.signon, svc_spawnstatic2); MSG_WriteByte (&sv.signon, bits); } else MSG_WriteByte (&sv.signon, svc_spawnstatic); if (bits & B_LARGEMODEL) MSG_WriteShort (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); else MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); if (bits & B_LARGEFRAME) MSG_WriteShort (&sv.signon, ent->v.frame); else MSG_WriteByte (&sv.signon, ent->v.frame); //johnfitz MSG_WriteByte (&sv.signon, ent->v.colormap); MSG_WriteByte (&sv.signon, ent->v.skin); for (i = 0; i < 3; i++) { MSG_WriteCoord(&sv.signon, ent->v.origin[i], sv.protocolflags); MSG_WriteAngle(&sv.signon, ent->v.angles[i], sv.protocolflags); } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & B_ALPHA) MSG_WriteByte (&sv.signon, ent->alpha); //johnfitz // throw the entity away now ED_Free (ent); } //============================================================================= /* ============== PF_setspawnparms ============== */ static void PF_setspawnparms (void) { edict_t *ent; int i; client_t *client; ent = G_EDICT(OFS_PARM0); i = NUM_FOR_EDICT(ent); if (i < 1 || i > svs.maxclients) PR_RunError ("Entity is not a client"); // copy spawn parms out of the client_t client = svs.clients + (i-1); for (i = 0; i < NUM_SPAWN_PARMS; i++) (&pr_global_struct->parm1)[i] = client->spawn_parms[i]; } /* ============== PF_changelevel ============== */ static void PF_changelevel (void) { const char *s; // make sure we don't issue two changelevels if (svs.changelevel_issued) return; svs.changelevel_issued = true; s = G_STRING(OFS_PARM0); Cbuf_AddText (va("changelevel %s\n",s)); } static void PF_Fixme (void) { PR_RunError ("unimplemented builtin"); } static builtin_t pr_builtin[] = { PF_Fixme, PF_makevectors, // void(entity e) makevectors = #1 PF_setorigin, // void(entity e, vector o) setorigin = #2 PF_setmodel, // void(entity e, string m) setmodel = #3 PF_setsize, // void(entity e, vector min, vector max) setsize = #4 PF_Fixme, // void(entity e, vector min, vector max) setabssize = #5 PF_break, // void() break = #6 PF_random, // float() random = #7 PF_sound, // void(entity e, float chan, string samp) sound = #8 PF_normalize, // vector(vector v) normalize = #9 PF_error, // void(string e) error = #10 PF_objerror, // void(string e) objerror = #11 PF_vlen, // float(vector v) vlen = #12 PF_vectoyaw, // float(vector v) vectoyaw = #13 PF_Spawn, // entity() spawn = #14 PF_Remove, // void(entity e) remove = #15 PF_traceline, // float(vector v1, vector v2, float tryents) traceline = #16 PF_checkclient, // entity() clientlist = #17 PF_Find, // entity(entity start, .string fld, string match) find = #18 PF_precache_sound, // void(string s) precache_sound = #19 PF_precache_model, // void(string s) precache_model = #20 PF_stuffcmd, // void(entity client, string s)stuffcmd = #21 PF_findradius, // entity(vector org, float rad) findradius = #22 PF_bprint, // void(string s) bprint = #23 PF_sprint, // void(entity client, string s) sprint = #24 PF_dprint, // void(string s) dprint = #25 PF_ftos, // void(string s) ftos = #26 PF_vtos, // void(string s) vtos = #27 PF_coredump, PF_traceon, PF_traceoff, PF_eprint, // void(entity e) debug print an entire entity PF_walkmove, // float(float yaw, float dist) walkmove PF_Fixme, // float(float yaw, float dist) walkmove PF_droptofloor, PF_lightstyle, PF_rint, PF_floor, PF_ceil, PF_Fixme, PF_checkbottom, PF_pointcontents, PF_Fixme, PF_fabs, PF_aim, PF_cvar, PF_localcmd, PF_nextent, PF_particle, PF_changeyaw, PF_Fixme, PF_vectoangles, PF_WriteByte, PF_WriteChar, PF_WriteShort, PF_WriteLong, PF_WriteCoord, PF_WriteAngle, PF_WriteString, PF_WriteEntity, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, SV_MoveToGoal, PF_precache_file, PF_makestatic, PF_changelevel, PF_Fixme, PF_cvar_set, PF_centerprint, PF_ambientsound, PF_precache_model, PF_precache_sound, // precache_sound2 is different only for qcc PF_precache_file, PF_setspawnparms }; builtin_t *pr_builtins = pr_builtin; int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mp3.c�������������������������������������������������������������������0000644�0000000�0000000�00000036541�12550656216�015251� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * MP3 decoding support using libmad: Adapted from the SoX library at * http://sourceforge.net/projects/sox/, LGPLv2, Copyright (c) 2007-2009 * SoX contributors, written by Fabrizio Gennari <fabrizio.ge@tiscali.it>, * with the decoding part based on the decoder tutorial program madlld * written by Bertrand Petit <madlld@phoe.fmug.org> (BSD license, see at * http://www.bsd-dk.dk/~elrond/audio/madlld/). The tag identification * functions were adapted from the GPL-licensed libid3tag library, see at * http://www.underbit.com/products/mad/. Adapted to Quake and Hexen II * game engines by O.Sezer : * Copyright (C) 2010-2015 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MP3) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mp3.h" #include <mad.h> #define ID3_TAG_FLAG_FOOTERPRESENT 0x10 /* Under Windows, importing data from DLLs is a dicey proposition. This is true * when using dlopen, but also true if linking directly against the DLL if the * header does not mark the data as __declspec(dllexport), which mad.h does not. * Sidestep the issue by defining our own mad_timer_zero. This is needed because * mad_timer_zero is used in some of the mad.h macros. */ #define mad_timer_zero mad_timer_zero_stub static mad_timer_t const mad_timer_zero_stub = {0, 0}; /* MAD returns values with MAD_F_FRACBITS (28) bits of precision, though it's not certain that all of them are meaningful. Default to 16 bits to align with most users expectation of output file should be 16 bits. */ #define MP3_MAD_SAMPLEBITS 16 #define MP3_MAD_SAMPLEWIDTH 2 #define MP3_BUFFER_SIZE (5 * 8192) /* Private data */ typedef struct _mp3_priv_t { unsigned char mp3_buffer[MP3_BUFFER_SIZE]; struct mad_stream Stream; struct mad_frame Frame; struct mad_synth Synth; mad_timer_t Timer; ptrdiff_t cursamp; size_t FrameCount; } mp3_priv_t; /* This function merges the functions tagtype() and id3_tag_query() * from MAD's libid3tag, so we don't have to link to it * Returns 0 if the frame is not an ID3 tag, tag length if it is */ static inline qboolean tag_is_id3v1(const unsigned char *data, size_t length) { if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G') { return true; } return false; } static inline qboolean tag_is_id3v2(const unsigned char *data, size_t length) { if (length >= 10 && (data[0] == 'I' && data[1] == 'D' && data[2] == '3') && data[3] < 0xff && data[4] < 0xff && data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80) { return true; } return false; } /* http://wiki.hydrogenaud.io/index.php?title=APEv1_specification * http://wiki.hydrogenaud.io/index.php?title=APEv2_specification * Detect an APEv2 tag. (APEv1 has no header, so no luck.) */ static inline qboolean tag_is_apetag(const unsigned char *data, size_t length) { unsigned int v; if (length < 32) return false; if (memcmp(data,"APETAGEX",8) != 0) return false; v = (data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]; if (v != 2000U/* && v != 1000U*/) return false; v = 0; if (memcmp(&data[24],&v,4) != 0 || memcmp(&data[28],&v,4) != 0) return false; return true; } static size_t mp3_tagsize(const unsigned char *data, size_t length) { size_t size; if (tag_is_id3v1(data, length)) return 128; if (tag_is_id3v2(data, length)) { unsigned char flags = data[5]; size = 10 + (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9]; if (flags & ID3_TAG_FLAG_FOOTERPRESENT) size += 10; for ( ; size < length && !data[size]; ++size) ; /* Consume padding */ return size; } if (tag_is_apetag(data, length)) { size = (data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12]; size += 32; return size; } return 0; } /* Attempts to read an ID3 tag at the current location in stream and * consume it all. Returns -1 if no tag is found. Its up to caller * to recover. */ static int mp3_inputtag(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; int rc = -1; size_t remaining; size_t tagsize; /* FIXME: This needs some more work if we are to ever * look at the ID3 frame. This is because the Stream * may not be able to hold the complete ID3 frame. * We should consume the whole frame inside tagtype() * instead of outside of tagframe(). That would support * recovering when Stream contains less then 8-bytes (header) * and also when ID3v2 is bigger then Stream buffer size. * Need to pass in stream so that buffer can be * consumed as well as letting additional data to be * read in. */ remaining = p->Stream.bufend - p->Stream.next_frame; tagsize = mp3_tagsize(p->Stream.this_frame, remaining); if (tagsize != 0) { mad_stream_skip(&p->Stream, tagsize); rc = 0; } /* We know that a valid frame hasn't been found yet * so help libmad out and go back into frame seek mode. * This is true whether an ID3 tag was found or not. */ mad_stream_sync(&p->Stream); return rc; } /* (Re)fill the stream buffer that is to be decoded. If any data * still exists in the buffer then they are first shifted to be * front of the stream buffer. */ static int mp3_inputdata(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t bytes_read; size_t remaining; remaining = p->Stream.bufend - p->Stream.next_frame; /* libmad does not consume all the buffer it's given. Some * data, part of a truncated frame, is left unused at the * end of the buffer. That data must be put back at the * beginning of the buffer and taken in account for * refilling the buffer. This means that the input buffer * must be large enough to hold a complete frame at the * highest observable bit-rate (currently 448 kb/s). * TODO: Is 2016 bytes the size of the largest frame? * (448000*(1152/32000))/8 */ memmove(p->mp3_buffer, p->Stream.next_frame, remaining); bytes_read = FS_fread(p->mp3_buffer + remaining, 1, MP3_BUFFER_SIZE - remaining, &stream->fh); if (bytes_read == 0) return -1; mad_stream_buffer(&p->Stream, p->mp3_buffer, bytes_read+remaining); p->Stream.error = MAD_ERROR_NONE; return 0; } static int mp3_startread(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t ReadSize; mad_stream_init(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); mad_timer_reset(&p->Timer); /* Decode at least one valid frame to find out the input * format. The decoded frame will be saved off so that it * can be processed later. */ ReadSize = FS_fread(p->mp3_buffer, 1, MP3_BUFFER_SIZE, &stream->fh); if (!ReadSize || FS_ferror(&stream->fh)) return -1; mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize); /* Find a valid frame before starting up. This makes sure * that we have a valid MP3 and also skips past ID3v2 tags * at the beginning of the audio file. */ p->Stream.error = MAD_ERROR_NONE; while (mad_frame_decode(&p->Frame,&p->Stream)) { /* check whether input buffer needs a refill */ if (p->Stream.error == MAD_ERROR_BUFLEN) { if (mp3_inputdata(stream) == -1) return -1;/* EOF with no valid data */ continue; } /* Consume any ID3 tags */ mp3_inputtag(stream); /* FIXME: We should probably detect when we've read * a bunch of non-ID3 data and still haven't found a * frame. In that case we can abort early without * scanning the whole file. */ p->Stream.error = MAD_ERROR_NONE; } if (p->Stream.error) { Con_Printf("MP3: No valid MP3 frame found\n"); return -1; } switch(p->Frame.header.mode) { case MAD_MODE_SINGLE_CHANNEL: case MAD_MODE_DUAL_CHANNEL: case MAD_MODE_JOINT_STEREO: case MAD_MODE_STEREO: stream->info.channels = MAD_NCHANNELS(&p->Frame.header); break; default: Con_Printf("MP3: Cannot determine number of channels\n"); return -1; } p->FrameCount = 1; mad_timer_add(&p->Timer,p->Frame.header.duration); mad_synth_frame(&p->Synth,&p->Frame); stream->info.rate = p->Synth.pcm.samplerate; stream->info.bits = MP3_MAD_SAMPLEBITS; stream->info.width = MP3_MAD_SAMPLEWIDTH; p->cursamp = 0; return 0; } /* Read up to len samples from p->Synth * If needed, read some more MP3 data, decode them and synth them * Place in buf[]. * Return number of samples read. */ static int mp3_decode(snd_stream_t *stream, byte *buf, int len) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; int donow, i, done = 0; mad_fixed_t sample; int chan, x; do { x = (p->Synth.pcm.length - p->cursamp) * stream->info.channels; donow = q_min(len, x); i = 0; while (i < donow) { for (chan = 0; chan < stream->info.channels; chan++) { sample = p->Synth.pcm.samples[chan][p->cursamp]; /* convert from fixed to short, * write in host-endian format. */ if (sample <= -MAD_F_ONE) sample = -0x7FFF; else if (sample >= MAD_F_ONE) sample = 0x7FFF; else sample >>= (MAD_F_FRACBITS + 1 - 16); if (host_bigendian) { *buf++ = (sample >> 8) & 0xFF; *buf++ = sample & 0xFF; } else /* assumed LITTLE_ENDIAN. */ { *buf++ = sample & 0xFF; *buf++ = (sample >> 8) & 0xFF; } i++; } p->cursamp++; } len -= donow; done += donow; if (len == 0) break; /* check whether input buffer needs a refill */ if (p->Stream.error == MAD_ERROR_BUFLEN) { if (mp3_inputdata(stream) == -1) { /* check feof() ?? */ Con_DPrintf("mp3 EOF\n"); break; } } if (mad_frame_decode(&p->Frame, &p->Stream)) { if (MAD_RECOVERABLE(p->Stream.error)) { mp3_inputtag(stream); continue; } else { if (p->Stream.error == MAD_ERROR_BUFLEN) continue; else { Con_Printf("MP3: unrecoverable frame level error (%s)\n", mad_stream_errorstr(&p->Stream)); break; } } } p->FrameCount++; mad_timer_add(&p->Timer, p->Frame.header.duration); mad_synth_frame(&p->Synth, &p->Frame); p->cursamp = 0; } while (1); return done; } static int mp3_stopread(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t*) stream->priv; mad_synth_finish(&p->Synth); mad_frame_finish(&p->Frame); mad_stream_finish(&p->Stream); return 0; } static int mp3_madseek(snd_stream_t *stream, unsigned long offset) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t initial_bitrate = p->Frame.header.bitrate; size_t tagsize = 0, consumed = 0; int vbr = 0; /* Variable Bit Rate, bool */ qboolean depadded = false; unsigned long to_skip_samples = 0; /* Reset all */ FS_rewind(&stream->fh); mad_timer_reset(&p->Timer); p->FrameCount = 0; /* They where opened in startread */ mad_synth_finish(&p->Synth); mad_frame_finish(&p->Frame); mad_stream_finish(&p->Stream); mad_stream_init(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); offset /= stream->info.channels; to_skip_samples = offset; while (1) /* Read data from the MP3 file */ { int bytes_read, padding = 0; size_t leftover = p->Stream.bufend - p->Stream.next_frame; memcpy(p->mp3_buffer, p->Stream.this_frame, leftover); bytes_read = FS_fread(p->mp3_buffer + leftover, (size_t) 1, MP3_BUFFER_SIZE - leftover, &stream->fh); if (bytes_read <= 0) { Con_DPrintf("seek failure. unexpected EOF (frames=%lu leftover=%lu)\n", (unsigned long)p->FrameCount, (unsigned long)leftover); break; } for ( ; !depadded && padding < bytes_read && !p->mp3_buffer[padding]; ++padding) ; depadded = true; mad_stream_buffer(&p->Stream, p->mp3_buffer + padding, leftover + bytes_read - padding); while (1) /* Decode frame headers */ { static unsigned short samples; p->Stream.error = MAD_ERROR_NONE; /* Not an audio frame */ if (mad_header_decode(&p->Frame.header, &p->Stream) == -1) { if (p->Stream.error == MAD_ERROR_BUFLEN) break; /* Normal behaviour; get some more data from the file */ if (!MAD_RECOVERABLE(p->Stream.error)) { Con_DPrintf("unrecoverable MAD error\n"); break; } if (p->Stream.error == MAD_ERROR_LOSTSYNC) { unsigned long available = (p->Stream.bufend - p->Stream.this_frame); tagsize = mp3_tagsize(p->Stream.this_frame, (size_t) available); if (tagsize) { /* It's some ID3 tags, so just skip */ if (tagsize >= available) { FS_fseek(&stream->fh, (long)(tagsize - available), SEEK_CUR); depadded = false; } mad_stream_skip(&p->Stream, q_min(tagsize, available)); } else { Con_DPrintf("MAD lost sync\n"); } } else { Con_DPrintf("recoverable MAD error\n"); } continue; } consumed += p->Stream.next_frame - p->Stream.this_frame; vbr |= (p->Frame.header.bitrate != initial_bitrate); samples = 32 * MAD_NSBSAMPLES(&p->Frame.header); p->FrameCount++; mad_timer_add(&p->Timer, p->Frame.header.duration); if (to_skip_samples <= samples) { mad_frame_decode(&p->Frame,&p->Stream); mad_synth_frame(&p->Synth, &p->Frame); p->cursamp = to_skip_samples; return 0; } else to_skip_samples -= samples; /* If not VBR, we can extrapolate frame size */ if (p->FrameCount == 64 && !vbr) { p->FrameCount = offset / samples; to_skip_samples = offset % samples; if (0 != FS_fseek(&stream->fh, (p->FrameCount * consumed / 64) + tagsize, SEEK_SET)) return -1; /* Reset Stream for refilling buffer */ mad_stream_finish(&p->Stream); mad_stream_init(&p->Stream); break; } } } return -1; } static qboolean S_MP3_CodecInitialize (void) { return true; } static void S_MP3_CodecShutdown (void) { } static qboolean S_MP3_CodecOpenStream (snd_stream_t *stream) { int err; stream->priv = calloc(1, sizeof(mp3_priv_t)); if (!stream->priv) { Con_Printf("Insufficient memory for MP3 audio\n"); return false; } err = mp3_startread(stream); if (err != 0) { Con_Printf("%s is not a valid mp3 file\n", stream->name); } else if (stream->info.channels != 1 && stream->info.channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", stream->info.channels, stream->name); } else { return true; } free(stream->priv); return false; } static int S_MP3_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int res = mp3_decode(stream, (byte *)buffer, bytes / stream->info.width); return res * stream->info.width; } static void S_MP3_CodecCloseStream (snd_stream_t *stream) { mp3_stopread(stream); free(stream->priv); S_CodecUtilClose(&stream); } static int S_MP3_CodecRewindStream (snd_stream_t *stream) { /* mp3_stopread(stream); FS_rewind(&stream->fh); return mp3_startread(stream); */ return mp3_madseek(stream, 0); } snd_codec_t mp3_codec = { CODECTYPE_MP3, true, /* always available. */ "mp3", S_MP3_CodecInitialize, S_MP3_CodecShutdown, S_MP3_CodecOpenStream, S_MP3_CodecReadStream, S_MP3_CodecRewindStream, S_MP3_CodecCloseStream, NULL }; #endif /* USE_CODEC_MP3 */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/pr_exec.c�������������������������������������������������������������������0000644�0000000�0000000�00000031516�13123233765�015325� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" typedef struct { int s; dfunction_t *f; } prstack_t; #define MAX_STACK_DEPTH 64 /* was 32 */ static prstack_t pr_stack[MAX_STACK_DEPTH]; static int pr_depth; #define LOCALSTACK_SIZE 2048 static int localstack[LOCALSTACK_SIZE]; static int localstack_used; qboolean pr_trace; dfunction_t *pr_xfunction; int pr_xstatement; int pr_argc; static const char *pr_opnames[] = { "DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV", "ADD_F", "ADD_V", "SUB_F", "SUB_V", "EQ_F", "EQ_V", "EQ_S", "EQ_E", "EQ_FNC", "NE_F", "NE_V", "NE_S", "NE_E", "NE_FNC", "LE", "GE", "LT", "GT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "ADDRESS", "STORE_F", "STORE_V", "STORE_S", "STORE_ENT", "STORE_FLD", "STORE_FNC", "STOREP_F", "STOREP_V", "STOREP_S", "STOREP_ENT", "STOREP_FLD", "STOREP_FNC", "RETURN", "NOT_F", "NOT_V", "NOT_S", "NOT_ENT", "NOT_FNC", "IF", "IFNOT", "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7", "CALL8", "STATE", "GOTO", "AND", "OR", "BITAND", "BITOR" }; const char *PR_GlobalString (int ofs); const char *PR_GlobalStringNoContents (int ofs); //============================================================================= /* ================= PR_PrintStatement ================= */ static void PR_PrintStatement (dstatement_t *s) { int i; if ((unsigned int)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) { Con_Printf("%s ", pr_opnames[s->op]); i = strlen(pr_opnames[s->op]); for ( ; i < 10; i++) Con_Printf(" "); } if (s->op == OP_IF || s->op == OP_IFNOT) Con_Printf("%sbranch %i", PR_GlobalString(s->a), s->b); else if (s->op == OP_GOTO) { Con_Printf("branch %i", s->a); } else if ((unsigned int)(s->op-OP_STORE_F) < 6) { Con_Printf("%s", PR_GlobalString(s->a)); Con_Printf("%s", PR_GlobalStringNoContents(s->b)); } else { if (s->a) Con_Printf("%s", PR_GlobalString(s->a)); if (s->b) Con_Printf("%s", PR_GlobalString(s->b)); if (s->c) Con_Printf("%s", PR_GlobalStringNoContents(s->c)); } Con_Printf("\n"); } /* ============ PR_StackTrace ============ */ static void PR_StackTrace (void) { int i; dfunction_t *f; if (pr_depth == 0) { Con_Printf("<NO STACK>\n"); return; } pr_stack[pr_depth].f = pr_xfunction; for (i = pr_depth; i >= 0; i--) { f = pr_stack[i].f; if (!f) { Con_Printf("<NO FUNCTION>\n"); } else { Con_Printf("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); } } } /* ============ PR_Profile_f ============ */ void PR_Profile_f (void) { int i, num; int pmax; dfunction_t *f, *best; if (!sv.active) return; num = 0; do { pmax = 0; best = NULL; for (i = 0; i < progs->numfunctions; i++) { f = &pr_functions[i]; if (f->profile > pmax) { pmax = f->profile; best = f; } } if (best) { if (num < 10) Con_Printf("%7i %s\n", best->profile, PR_GetString(best->s_name)); num++; best->profile = 0; } } while (best); } /* ============ PR_RunError Aborts the currently executing function ============ */ void PR_RunError (const char *error, ...) { va_list argptr; char string[1024]; va_start (argptr, error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); PR_PrintStatement(pr_statements + pr_xstatement); PR_StackTrace(); Con_Printf("%s\n", string); pr_depth = 0; // dump the stack so host_error can shutdown functions Host_Error("Program error"); } /* ==================== PR_EnterFunction Returns the new program statement counter ==================== */ static int PR_EnterFunction (dfunction_t *f) { int i, j, c, o; pr_stack[pr_depth].s = pr_xstatement; pr_stack[pr_depth].f = pr_xfunction; pr_depth++; if (pr_depth >= MAX_STACK_DEPTH) PR_RunError("stack overflow"); // save off any locals that the new function steps on c = f->locals; if (localstack_used + c > LOCALSTACK_SIZE) PR_RunError("PR_ExecuteProgram: locals stack overflow\n"); for (i = 0; i < c ; i++) localstack[localstack_used + i] = ((int *)pr_globals)[f->parm_start + i]; localstack_used += c; // copy parameters o = f->parm_start; for (i = 0; i < f->numparms; i++) { for (j = 0; j < f->parm_size[i]; j++) { ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0 + i*3 + j]; o++; } } pr_xfunction = f; return f->first_statement - 1; // offset the s++ } /* ==================== PR_LeaveFunction ==================== */ static int PR_LeaveFunction (void) { int i, c; if (pr_depth <= 0) Host_Error("prog stack underflow"); // Restore locals from the stack c = pr_xfunction->locals; localstack_used -= c; if (localstack_used < 0) PR_RunError("PR_ExecuteProgram: locals stack underflow"); for (i = 0; i < c; i++) ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used + i]; // up stack pr_depth--; pr_xfunction = pr_stack[pr_depth].f; return pr_stack[pr_depth].s; } /* ==================== PR_ExecuteProgram The interpretation main loop ==================== */ #define OPA ((eval_t *)&pr_globals[(unsigned short)st->a]) #define OPB ((eval_t *)&pr_globals[(unsigned short)st->b]) #define OPC ((eval_t *)&pr_globals[(unsigned short)st->c]) void PR_ExecuteProgram (func_t fnum) { eval_t *ptr; dstatement_t *st; dfunction_t *f, *newf; int profile, startprofile; edict_t *ed; int exitdepth; if (!fnum || fnum >= progs->numfunctions) { if (pr_global_struct->self) ED_Print (PROG_TO_EDICT(pr_global_struct->self)); Host_Error ("PR_ExecuteProgram: NULL function"); } f = &pr_functions[fnum]; pr_trace = false; // make a stack frame exitdepth = pr_depth; st = &pr_statements[PR_EnterFunction(f)]; startprofile = profile = 0; while (1) { st++; /* next statement */ if (++profile > 100000) { pr_xstatement = st - pr_statements; PR_RunError("runaway loop error"); } if (pr_trace) PR_PrintStatement(st); switch (st->op) { case OP_ADD_F: OPC->_float = OPA->_float + OPB->_float; break; case OP_ADD_V: OPC->vector[0] = OPA->vector[0] + OPB->vector[0]; OPC->vector[1] = OPA->vector[1] + OPB->vector[1]; OPC->vector[2] = OPA->vector[2] + OPB->vector[2]; break; case OP_SUB_F: OPC->_float = OPA->_float - OPB->_float; break; case OP_SUB_V: OPC->vector[0] = OPA->vector[0] - OPB->vector[0]; OPC->vector[1] = OPA->vector[1] - OPB->vector[1]; OPC->vector[2] = OPA->vector[2] - OPB->vector[2]; break; case OP_MUL_F: OPC->_float = OPA->_float * OPB->_float; break; case OP_MUL_V: OPC->_float = OPA->vector[0] * OPB->vector[0] + OPA->vector[1] * OPB->vector[1] + OPA->vector[2] * OPB->vector[2]; break; case OP_MUL_FV: OPC->vector[0] = OPA->_float * OPB->vector[0]; OPC->vector[1] = OPA->_float * OPB->vector[1]; OPC->vector[2] = OPA->_float * OPB->vector[2]; break; case OP_MUL_VF: OPC->vector[0] = OPB->_float * OPA->vector[0]; OPC->vector[1] = OPB->_float * OPA->vector[1]; OPC->vector[2] = OPB->_float * OPA->vector[2]; break; case OP_DIV_F: OPC->_float = OPA->_float / OPB->_float; break; case OP_BITAND: OPC->_float = (int)OPA->_float & (int)OPB->_float; break; case OP_BITOR: OPC->_float = (int)OPA->_float | (int)OPB->_float; break; case OP_GE: OPC->_float = OPA->_float >= OPB->_float; break; case OP_LE: OPC->_float = OPA->_float <= OPB->_float; break; case OP_GT: OPC->_float = OPA->_float > OPB->_float; break; case OP_LT: OPC->_float = OPA->_float < OPB->_float; break; case OP_AND: OPC->_float = OPA->_float && OPB->_float; break; case OP_OR: OPC->_float = OPA->_float || OPB->_float; break; case OP_NOT_F: OPC->_float = !OPA->_float; break; case OP_NOT_V: OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]; break; case OP_NOT_S: OPC->_float = !OPA->string || !*PR_GetString(OPA->string); break; case OP_NOT_FNC: OPC->_float = !OPA->function; break; case OP_NOT_ENT: OPC->_float = (PROG_TO_EDICT(OPA->edict) == sv.edicts); break; case OP_EQ_F: OPC->_float = OPA->_float == OPB->_float; break; case OP_EQ_V: OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]); break; case OP_EQ_S: OPC->_float = !strcmp(PR_GetString(OPA->string), PR_GetString(OPB->string)); break; case OP_EQ_E: OPC->_float = OPA->_int == OPB->_int; break; case OP_EQ_FNC: OPC->_float = OPA->function == OPB->function; break; case OP_NE_F: OPC->_float = OPA->_float != OPB->_float; break; case OP_NE_V: OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]); break; case OP_NE_S: OPC->_float = strcmp(PR_GetString(OPA->string), PR_GetString(OPB->string)); break; case OP_NE_E: OPC->_float = OPA->_int != OPB->_int; break; case OP_NE_FNC: OPC->_float = OPA->function != OPB->function; break; case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: case OP_STORE_FNC: // pointers OPB->_int = OPA->_int; break; case OP_STORE_V: OPB->vector[0] = OPA->vector[0]; OPB->vector[1] = OPA->vector[1]; OPB->vector[2] = OPA->vector[2]; break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers ptr = (eval_t *)((byte *)sv.edicts + OPB->_int); ptr->_int = OPA->_int; break; case OP_STOREP_V: ptr = (eval_t *)((byte *)sv.edicts + OPB->_int); ptr->vector[0] = OPA->vector[0]; ptr->vector[1] = OPA->vector[1]; ptr->vector[2] = OPA->vector[2]; break; case OP_ADDRESS: ed = PROG_TO_EDICT(OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif if (ed == (edict_t *)sv.edicts && sv.state == ss_active) { pr_xstatement = st - pr_statements; PR_RunError("assignment to world entity"); } OPC->_int = (byte *)((int *)&ed->v + OPB->_int) - (byte *)sv.edicts; break; case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: ed = PROG_TO_EDICT(OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif OPC->_int = ((eval_t *)((int *)&ed->v + OPB->_int))->_int; break; case OP_LOAD_V: ed = PROG_TO_EDICT(OPA->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif ptr = (eval_t *)((int *)&ed->v + OPB->_int); OPC->vector[0] = ptr->vector[0]; OPC->vector[1] = ptr->vector[1]; OPC->vector[2] = ptr->vector[2]; break; case OP_IFNOT: if (!OPA->_int) st += st->b - 1; /* -1 to offset the st++ */ break; case OP_IF: if (OPA->_int) st += st->b - 1; /* -1 to offset the st++ */ break; case OP_GOTO: st += st->a - 1; /* -1 to offset the st++ */ break; case OP_CALL0: case OP_CALL1: case OP_CALL2: case OP_CALL3: case OP_CALL4: case OP_CALL5: case OP_CALL6: case OP_CALL7: case OP_CALL8: pr_xfunction->profile += profile - startprofile; startprofile = profile; pr_xstatement = st - pr_statements; pr_argc = st->op - OP_CALL0; if (!OPA->function) PR_RunError("NULL function"); newf = &pr_functions[OPA->function]; if (newf->first_statement < 0) { // Built-in function int i = -newf->first_statement; if (i >= pr_numbuiltins) PR_RunError("Bad builtin call number %d", i); pr_builtins[i](); break; } // Normal function st = &pr_statements[PR_EnterFunction(newf)]; break; case OP_DONE: case OP_RETURN: pr_xfunction->profile += profile - startprofile; startprofile = profile; pr_xstatement = st - pr_statements; pr_globals[OFS_RETURN] = pr_globals[(unsigned short)st->a]; pr_globals[OFS_RETURN + 1] = pr_globals[(unsigned short)st->a + 1]; pr_globals[OFS_RETURN + 2] = pr_globals[(unsigned short)st->a + 2]; st = &pr_statements[PR_LeaveFunction()]; if (pr_depth == exitdepth) { // Done return; } break; case OP_STATE: ed = PROG_TO_EDICT(pr_global_struct->self); ed->v.nextthink = pr_global_struct->time + 0.1; ed->v.frame = OPA->_float; ed->v.think = OPB->function; break; default: pr_xstatement = st - pr_statements; PR_RunError("Bad opcode %i", st->op); } } /* end of while(1) loop */ } #undef OPA #undef OPB #undef OPC ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_mp3.h�������������������������������������������������������������������0000644�0000000�0000000�00000000331�11526305154�015234� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* MP3 decoding support using libmad or libmpg123. */ #if !defined(_SND_MP3_H_) #define _SND_MP3_H_ #if defined(USE_CODEC_MP3) extern snd_codec_t mp3_codec; #endif /* USE_CODEC_MP3 */ #endif /* ! _SND_MP3_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cl_input.c������������������������������������������������������������������0000644�0000000�0000000�00000027063�13147214764�015523� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl.input.c -- builds an intended movement command to send to the server // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All // rights reserved. #include "quakedef.h" extern cvar_t cl_maxpitch; //johnfitz -- variable pitch clamping extern cvar_t cl_minpitch; //johnfitz -- variable pitch clamping /* =============================================================================== KEY BUTTONS Continuous button event tracking is complicated by the fact that two different input sources (say, mouse button 1 and the control key) can both press the same button, but the button should only be released when both of the pressing key have been released. When a key event issues a button command (+forward, +attack, etc), it appends its key number as a parameter to the command so it can be matched up with the release. state bit 0 is the current state of the key state bit 1 is edge triggered on the up to down transition state bit 2 is edge triggered on the down to up transition =============================================================================== */ kbutton_t in_mlook, in_klook; kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; kbutton_t in_up, in_down; int in_impulse; void KeyDown (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else k = -1; // typed manually at the console for continuous down if (k == b->down[0] || k == b->down[1]) return; // repeating key if (!b->down[0]) b->down[0] = k; else if (!b->down[1]) b->down[1] = k; else { Con_Printf ("Three keys down for a button!\n"); return; } if (b->state & 1) return; // still down b->state |= 1 + 2; // down + impulse down } void KeyUp (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->state = 4; // impulse up return; } if (b->down[0] == k) b->down[0] = 0; else if (b->down[1] == k) b->down[1] = 0; else return; // key up without coresponding down (menu pass through) if (b->down[0] || b->down[1]) return; // some other key is still holding it down if (!(b->state & 1)) return; // still up (this should not happen) b->state &= ~1; // now up b->state |= 4; // impulse up } void IN_KLookDown (void) {KeyDown(&in_klook);} void IN_KLookUp (void) {KeyUp(&in_klook);} void IN_MLookDown (void) {KeyDown(&in_mlook);} void IN_MLookUp (void) { KeyUp(&in_mlook); if ( !(in_mlook.state&1) && lookspring.value) V_StartPitchDrift(); } void IN_UpDown(void) {KeyDown(&in_up);} void IN_UpUp(void) {KeyUp(&in_up);} void IN_DownDown(void) {KeyDown(&in_down);} void IN_DownUp(void) {KeyUp(&in_down);} void IN_LeftDown(void) {KeyDown(&in_left);} void IN_LeftUp(void) {KeyUp(&in_left);} void IN_RightDown(void) {KeyDown(&in_right);} void IN_RightUp(void) {KeyUp(&in_right);} void IN_ForwardDown(void) {KeyDown(&in_forward);} void IN_ForwardUp(void) {KeyUp(&in_forward);} void IN_BackDown(void) {KeyDown(&in_back);} void IN_BackUp(void) {KeyUp(&in_back);} void IN_LookupDown(void) {KeyDown(&in_lookup);} void IN_LookupUp(void) {KeyUp(&in_lookup);} void IN_LookdownDown(void) {KeyDown(&in_lookdown);} void IN_LookdownUp(void) {KeyUp(&in_lookdown);} void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} void IN_MoverightDown(void) {KeyDown(&in_moveright);} void IN_MoverightUp(void) {KeyUp(&in_moveright);} void IN_SpeedDown(void) {KeyDown(&in_speed);} void IN_SpeedUp(void) {KeyUp(&in_speed);} void IN_StrafeDown(void) {KeyDown(&in_strafe);} void IN_StrafeUp(void) {KeyUp(&in_strafe);} void IN_AttackDown(void) {KeyDown(&in_attack);} void IN_AttackUp(void) {KeyUp(&in_attack);} void IN_UseDown (void) {KeyDown(&in_use);} void IN_UseUp (void) {KeyUp(&in_use);} void IN_JumpDown (void) {KeyDown(&in_jump);} void IN_JumpUp (void) {KeyUp(&in_jump);} void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));} /* =============== CL_KeyState Returns 0.25 if a key was pressed and released during the frame, 0.5 if it was pressed and held 0 if held then released, and 1.0 if held for the entire time =============== */ float CL_KeyState (kbutton_t *key) { float val; qboolean impulsedown, impulseup, down; impulsedown = key->state & 2; impulseup = key->state & 4; down = key->state & 1; val = 0; if (impulsedown && !impulseup) { if (down) val = 0.5; // pressed and held this frame else val = 0; // I_Error (); } if (impulseup && !impulsedown) { if (down) val = 0; // I_Error (); else val = 0; // released this frame } if (!impulsedown && !impulseup) { if (down) val = 1.0; // held the entire frame else val = 0; // up the entire frame } if (impulsedown && impulseup) { if (down) val = 0.75; // released and re-pressed this frame else val = 0.25; // pressed and released this frame } key->state &= 1; // clear impulses return val; } //========================================================================== cvar_t cl_upspeed = {"cl_upspeed","200",CVAR_NONE}; cvar_t cl_forwardspeed = {"cl_forwardspeed","200", CVAR_ARCHIVE}; cvar_t cl_backspeed = {"cl_backspeed","200", CVAR_ARCHIVE}; cvar_t cl_sidespeed = {"cl_sidespeed","350",CVAR_NONE}; cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0",CVAR_NONE}; cvar_t cl_yawspeed = {"cl_yawspeed","140",CVAR_NONE}; cvar_t cl_pitchspeed = {"cl_pitchspeed","150",CVAR_NONE}; cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5",CVAR_NONE}; cvar_t cl_alwaysrun = {"cl_alwaysrun","0",CVAR_ARCHIVE}; // QuakeSpasm -- new always run /* ================ CL_AdjustAngles Moves the local angle positions ================ */ void CL_AdjustAngles (void) { float speed; float up, down; if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0)) speed = host_frametime * cl_anglespeedkey.value; else speed = host_frametime; if (!(in_strafe.state & 1)) { cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if (in_klook.state & 1) { V_StopPitchDrift (); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); } up = CL_KeyState (&in_lookup); down = CL_KeyState(&in_lookdown); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; if (up || down) V_StopPitchDrift (); //johnfitz -- variable pitch clamping if (cl.viewangles[PITCH] > cl_maxpitch.value) cl.viewangles[PITCH] = cl_maxpitch.value; if (cl.viewangles[PITCH] < cl_minpitch.value) cl.viewangles[PITCH] = cl_minpitch.value; //johnfitz if (cl.viewangles[ROLL] > 50) cl.viewangles[ROLL] = 50; if (cl.viewangles[ROLL] < -50) cl.viewangles[ROLL] = -50; } /* ================ CL_BaseMove Send the intended movement message to the server ================ */ void CL_BaseMove (usercmd_t *cmd) { if (cls.signon != SIGNONS) return; CL_AdjustAngles (); Q_memset (cmd, 0, sizeof(*cmd)); if (in_strafe.state & 1) { cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); } cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); if (! (in_klook.state & 1) ) { cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); } // // adjust for speed key // if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0)) { cmd->forwardmove *= cl_movespeedkey.value; cmd->sidemove *= cl_movespeedkey.value; cmd->upmove *= cl_movespeedkey.value; } } /* ============== CL_SendMove ============== */ void CL_SendMove (const usercmd_t *cmd) { int i; int bits; sizebuf_t buf; byte data[128]; buf.maxsize = 128; buf.cursize = 0; buf.data = data; cl.cmd = *cmd; // // send the movement message // MSG_WriteByte (&buf, clc_move); MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times for (i=0 ; i<3 ; i++) //johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE if (cl.protocol == PROTOCOL_NETQUAKE) MSG_WriteAngle (&buf, cl.viewangles[i], cl.protocolflags); else MSG_WriteAngle16 (&buf, cl.viewangles[i], cl.protocolflags); //johnfitz MSG_WriteShort (&buf, cmd->forwardmove); MSG_WriteShort (&buf, cmd->sidemove); MSG_WriteShort (&buf, cmd->upmove); // // send button bits // bits = 0; if ( in_attack.state & 3 ) bits |= 1; in_attack.state &= ~2; if (in_jump.state & 3) bits |= 2; in_jump.state &= ~2; MSG_WriteByte (&buf, bits); MSG_WriteByte (&buf, in_impulse); in_impulse = 0; // // deliver the message // if (cls.demoplayback) return; // // allways dump the first two message, because it may contain leftover inputs // from the last level // if (++cl.movemessages <= 2) return; if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1) { Con_Printf ("CL_SendMove: lost server connection\n"); CL_Disconnect (); } } /* ============ CL_InitInput ============ */ void CL_InitInput (void) { Cmd_AddCommand ("+moveup",IN_UpDown); Cmd_AddCommand ("-moveup",IN_UpUp); Cmd_AddCommand ("+movedown",IN_DownDown); Cmd_AddCommand ("-movedown",IN_DownUp); Cmd_AddCommand ("+left",IN_LeftDown); Cmd_AddCommand ("-left",IN_LeftUp); Cmd_AddCommand ("+right",IN_RightDown); Cmd_AddCommand ("-right",IN_RightUp); Cmd_AddCommand ("+forward",IN_ForwardDown); Cmd_AddCommand ("-forward",IN_ForwardUp); Cmd_AddCommand ("+back",IN_BackDown); Cmd_AddCommand ("-back",IN_BackUp); Cmd_AddCommand ("+lookup", IN_LookupDown); Cmd_AddCommand ("-lookup", IN_LookupUp); Cmd_AddCommand ("+lookdown", IN_LookdownDown); Cmd_AddCommand ("-lookdown", IN_LookdownUp); Cmd_AddCommand ("+strafe", IN_StrafeDown); Cmd_AddCommand ("-strafe", IN_StrafeUp); Cmd_AddCommand ("+moveleft", IN_MoveleftDown); Cmd_AddCommand ("-moveleft", IN_MoveleftUp); Cmd_AddCommand ("+moveright", IN_MoverightDown); Cmd_AddCommand ("-moveright", IN_MoverightUp); Cmd_AddCommand ("+speed", IN_SpeedDown); Cmd_AddCommand ("-speed", IN_SpeedUp); Cmd_AddCommand ("+attack", IN_AttackDown); Cmd_AddCommand ("-attack", IN_AttackUp); Cmd_AddCommand ("+use", IN_UseDown); Cmd_AddCommand ("-use", IN_UseUp); Cmd_AddCommand ("+jump", IN_JumpDown); Cmd_AddCommand ("-jump", IN_JumpUp); Cmd_AddCommand ("impulse", IN_Impulse); Cmd_AddCommand ("+klook", IN_KLookDown); Cmd_AddCommand ("-klook", IN_KLookUp); Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_opus.c������������������������������������������������������������������0000644�0000000�0000000�00000011365�12220541170�015517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Ogg/Opus streaming music support, loosely based on several open source * Quake engine based projects with many modifications. * * Copyright (C) 2012-2013 O.Sezer <sezero@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_OPUS) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_opus.h" #include <errno.h> #include <opusfile.h> /* CALLBACK FUNCTIONS: */ static int opc_fclose (void *f) { return 0; /* we fclose() elsewhere. */ } static int opc_fread (void *f, unsigned char *buf, int size) { int ret; if (size < 0) { errno = EINVAL; return -1; } ret = (int) FS_fread(buf, 1, (size_t)size, (fshandle_t *)f); if (ret == 0 && errno != 0) ret = -1; return ret; } static int opc_fseek (void *f, opus_int64 off, int whence) { if (f == NULL) return (-1); return FS_fseek((fshandle_t *)f, (long) off, whence); } static opus_int64 opc_ftell (void *f) { return (opus_int64) FS_ftell((fshandle_t *)f); } static const OpusFileCallbacks opc_qfs = { (int (*)(void *, unsigned char *, int)) opc_fread, (int (*)(void *, opus_int64, int)) opc_fseek, (opus_int64 (*)(void *)) opc_ftell, (int (*)(void *)) opc_fclose }; static qboolean S_OPUS_CodecInitialize (void) { return true; } static void S_OPUS_CodecShutdown (void) { } static qboolean S_OPUS_CodecOpenStream (snd_stream_t *stream) { OggOpusFile *opFile; const OpusHead *op_info; long numstreams; int res; opFile = op_open_callbacks(&stream->fh, &opc_qfs, NULL, 0, &res); if (!opFile) { Con_Printf("%s is not a valid Opus file (error %i).\n", stream->name, res); goto _fail; } stream->priv = opFile; if (!op_seekable(opFile)) { Con_Printf("Opus stream %s not seekable.\n", stream->name); goto _fail; } op_info = op_head(opFile, -1); if (!op_info) { Con_Printf("Unable to get stream information for %s.\n", stream->name); goto _fail; } /* FIXME: handle section changes */ numstreams = op_info->stream_count; if (numstreams != 1) { Con_Printf("More than one (%ld) stream in %s\n", (long)op_info->stream_count, stream->name); goto _fail; } if (op_info->channel_count != 1 && op_info->channel_count != 2) { Con_Printf("Unsupported number of channels %d in %s\n", op_info->channel_count, stream->name); goto _fail; } /* All Opus audio is coded at 48 kHz, and should also be decoded * at 48 kHz for playback: info->input_sample_rate only tells us * the sampling rate of the original input before opus encoding. * S_RawSamples() shall already downsample this, as necessary. */ stream->info.rate = 48000; stream->info.channels = op_info->channel_count; /* op_read() yields 16-bit output using native endian ordering: */ stream->info.bits = 16; stream->info.width = 2; return true; _fail: if (opFile) op_free(opFile); return false; } static int S_OPUS_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int section; /* FIXME: handle section changes */ int cnt, res, rem; opus_int16 * ptr; rem = bytes / stream->info.width; if (rem / stream->info.channels <= 0) return 0; cnt = 0; ptr = (opus_int16 *) buffer; while (1) { /* op_read() yields 16-bit output using native endian ordering. returns * the number of samples read per channel on success, or a negative value * on failure. */ res = op_read((OggOpusFile *)stream->priv, ptr, rem, §ion); if (res <= 0) break; cnt += res; res *= stream->info.channels; rem -= res; if (rem <= 0) break; ptr += res; } if (res < 0) return res; cnt *= (stream->info.channels * stream->info.width); return cnt; } static void S_OPUS_CodecCloseStream (snd_stream_t *stream) { op_free((OggOpusFile *)stream->priv); S_CodecUtilClose(&stream); } static int S_OPUS_CodecRewindStream (snd_stream_t *stream) { return op_pcm_seek ((OggOpusFile *)stream->priv, 0); } snd_codec_t opus_codec = { CODECTYPE_OPUS, true, /* always available. */ "opus", S_OPUS_CodecInitialize, S_OPUS_CodecShutdown, S_OPUS_CodecOpenStream, S_OPUS_CodecReadStream, S_OPUS_CodecRewindStream, S_OPUS_CodecCloseStream, NULL }; #endif /* USE_CODEC_OPUS */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/snd_flac.h������������������������������������������������������������������0000644�0000000�0000000�00000000317�12172764736�015463� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* fLaC streaming music support. */ #if !defined(_SND_FLAC_H_) #define _SND_FLAC_H_ 1 #if defined(USE_CODEC_FLAC) extern snd_codec_t flac_codec; #endif /* USE_CODEC_FLAC */ #endif /* ! _SND_FLAC_H_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/cl_tent.c�������������������������������������������������������������������0000644�0000000�0000000�00000021416�12733153600�015321� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_tent.c -- client side temporary entities #include "quakedef.h" int num_temp_entities; entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; beam_t cl_beams[MAX_BEAMS]; sfx_t *cl_sfx_wizhit; sfx_t *cl_sfx_knighthit; sfx_t *cl_sfx_tink1; sfx_t *cl_sfx_ric1; sfx_t *cl_sfx_ric2; sfx_t *cl_sfx_ric3; sfx_t *cl_sfx_r_exp3; /* ================= CL_ParseTEnt ================= */ void CL_InitTEnts (void) { cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); } /* ================= CL_ParseBeam ================= */ void CL_ParseBeam (qmodel_t *m) { int ent; vec3_t start, end; beam_t *b; int i; ent = MSG_ReadShort (); start[0] = MSG_ReadCoord (cl.protocolflags); start[1] = MSG_ReadCoord (cl.protocolflags); start[2] = MSG_ReadCoord (cl.protocolflags); end[0] = MSG_ReadCoord (cl.protocolflags); end[1] = MSG_ReadCoord (cl.protocolflags); end[2] = MSG_ReadCoord (cl.protocolflags); // override any beam with the same entity for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) if (b->entity == ent) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } // find a free beam for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) { if (!b->model || b->endtime < cl.time) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } } //johnfitz -- less spammy overflow message if (!dev_overflows.beams || dev_overflows.beams + CONSOLE_RESPAM_TIME < realtime ) { Con_Printf ("Beam list overflow!\n"); dev_overflows.beams = realtime; } //johnfitz } /* ================= CL_ParseTEnt ================= */ void CL_ParseTEnt (void) { int type; vec3_t pos; dlight_t *dl; int rnd; int colorStart, colorLength; type = MSG_ReadByte (); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_RunParticleEffect (pos, vec3_origin, 20, 30); S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_RunParticleEffect (pos, vec3_origin, 226, 20); S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_RunParticleEffect (pos, vec3_origin, 0, 10); if ( rand() % 5 ) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_RunParticleEffect (pos, vec3_origin, 0, 20); if ( rand() % 5 ) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_GUNSHOT: // bullet hitting wall pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_RunParticleEffect (pos, vec3_origin, 0, 20); break; case TE_EXPLOSION: // rocket explosion pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_ParticleExplosion (pos); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_TAREXPLOSION: // tarbaby explosion pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_BlobExplosion (pos); S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); break; case TE_LIGHTNING2: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); break; case TE_LIGHTNING3: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); break; // PGM 01/21/97 case TE_BEAM: // grappling hook beam CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); break; // PGM 01/21/97 case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_LavaSplash (pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); R_TeleportSplash (pos); break; case TE_EXPLOSION2: // color mapped explosion pos[0] = MSG_ReadCoord (cl.protocolflags); pos[1] = MSG_ReadCoord (cl.protocolflags); pos[2] = MSG_ReadCoord (cl.protocolflags); colorStart = MSG_ReadByte (); colorLength = MSG_ReadByte (); R_ParticleExplosion2 (pos, colorStart, colorLength); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; default: Sys_Error ("CL_ParseTEnt: bad type"); } } /* ================= CL_NewTempEntity ================= */ entity_t *CL_NewTempEntity (void) { entity_t *ent; if (cl_numvisedicts == MAX_VISEDICTS) return NULL; if (num_temp_entities == MAX_TEMP_ENTITIES) return NULL; ent = &cl_temp_entities[num_temp_entities]; memset (ent, 0, sizeof(*ent)); num_temp_entities++; cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; ent->colormap = vid.colormap; return ent; } /* ================= CL_UpdateTEnts ================= */ void CL_UpdateTEnts (void) { int i, j; //johnfitz -- use j instead of using i twice, so we don't corrupt memory beam_t *b; vec3_t dist, org; float d; entity_t *ent; float yaw, pitch; float forward; num_temp_entities = 0; srand ((int) (cl.time * 1000)); //johnfitz -- freeze beams when paused // update lightning for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) { if (!b->model || b->endtime < cl.time) continue; // if coming from the player, update the start position if (b->entity == cl.viewentity) { VectorCopy (cl_entities[cl.viewentity].origin, b->start); } // calculate pitch and yaw VectorSubtract (b->end, b->start, dist); if (dist[1] == 0 && dist[0] == 0) { yaw = 0; if (dist[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } // add new entities for the lightning VectorCopy (b->start, org); d = VectorNormalize(dist); while (d > 0) { ent = CL_NewTempEntity (); if (!ent) return; VectorCopy (org, ent->origin); ent->model = b->model; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = rand()%360; //johnfitz -- use j instead of using i twice, so we don't corrupt memory for (j=0 ; j<3 ; j++) org[j] += dist[j]*30; d -= 30; } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/image.c���������������������������������������������������������������������0000644�0000000�0000000�00000035020�13157425640�014756� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //image.c -- image loading #include "quakedef.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_STATIC #include "stb_image_write.h" #define LODEPNG_NO_COMPILE_DECODER #define LODEPNG_NO_COMPILE_CPP #define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS #define LODEPNG_NO_COMPILE_ERROR_TEXT #include "lodepng.h" #include "lodepng.c" static char loadfilename[MAX_OSPATH]; //file scope so that error messages can use it typedef struct stdio_buffer_s { FILE *f; unsigned char buffer[1024]; int size; int pos; } stdio_buffer_t; static stdio_buffer_t *Buf_Alloc(FILE *f) { stdio_buffer_t *buf = (stdio_buffer_t *) calloc(1, sizeof(stdio_buffer_t)); buf->f = f; return buf; } static void Buf_Free(stdio_buffer_t *buf) { free(buf); } static inline int Buf_GetC(stdio_buffer_t *buf) { if (buf->pos >= buf->size) { buf->size = fread(buf->buffer, 1, sizeof(buf->buffer), buf->f); buf->pos = 0; if (buf->size == 0) return EOF; } return buf->buffer[buf->pos++]; } /* ============ Image_LoadImage returns a pointer to hunk allocated RGBA data TODO: search order: tga png jpg pcx lmp ============ */ byte *Image_LoadImage (const char *name, int *width, int *height) { FILE *f; q_snprintf (loadfilename, sizeof(loadfilename), "%s.tga", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) return Image_LoadTGA (f, width, height); q_snprintf (loadfilename, sizeof(loadfilename), "%s.pcx", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) return Image_LoadPCX (f, width, height); return NULL; } //============================================================================== // // TGA // //============================================================================== typedef struct targaheader_s { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } targaheader_t; #define TARGAHEADERSIZE 18 //size on disk targaheader_t targa_header; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ============ Image_WriteTGA -- writes RGB or RGBA data to a TGA file returns true if successful TODO: support BGRA and BGR formats (since opengl can return them, and we don't have to swap) ============ */ qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown) { int handle, i, size, temp, bytes; char pathname[MAX_OSPATH]; byte header[TARGAHEADERSIZE]; Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name); handle = Sys_FileOpenWrite (pathname); if (handle == -1) return false; Q_memset (header, 0, TARGAHEADERSIZE); header[2] = 2; // uncompressed type header[12] = width&255; header[13] = width>>8; header[14] = height&255; header[15] = height>>8; header[16] = bpp; // pixel size if (upsidedown) header[17] = 0x20; //upside-down attribute // swap red and blue bytes bytes = bpp/8; size = width*height*bytes; for (i=0; i<size; i+=bytes) { temp = data[i]; data[i] = data[i+2]; data[i+2] = temp; } Sys_FileWrite (handle, header, TARGAHEADERSIZE); Sys_FileWrite (handle, data, size); Sys_FileClose (handle); return true; } /* ============= Image_LoadTGA ============= */ byte *Image_LoadTGA (FILE *fin, int *width, int *height) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *targa_rgba; int realrow; //johnfitz -- fix for upside-down targas qboolean upside_down; //johnfitz -- fix for upside-down targas stdio_buffer_t *buf; targa_header.id_length = fgetc(fin); targa_header.colormap_type = fgetc(fin); targa_header.image_type = fgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); targa_header.colormap_size = fgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); targa_header.height = fgetLittleShort(fin); targa_header.pixel_size = fgetc(fin); targa_header.attributes = fgetc(fin); if (targa_header.image_type!=2 && targa_header.image_type!=10) Sys_Error ("Image_LoadTGA: %s is not a type 2 or type 10 targa\n", loadfilename); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) Sys_Error ("Image_LoadTGA: %s is not a 24bit or 32bit targa\n", loadfilename); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; upside_down = !(targa_header.attributes & 0x20); //johnfitz -- fix for upside-down targas targa_rgba = (byte *) Hunk_Alloc (numPixels*4); if (targa_header.id_length != 0) fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment buf = Buf_Alloc(fin); if (targa_header.image_type==2) // Uncompressed, RGB images { for(row=rows-1; row>=0; row--) { //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz for(column=0; column<columns; column++) { unsigned char red,green,blue,alphabyte; switch (targa_header.pixel_size) { case 24: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); alphabyte = Buf_GetC(buf); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } } } } else if (targa_header.image_type==10) // Runlength encoded RGB images { unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; for(row=rows-1; row>=0; row--) { //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz for(column=0; column<columns; ) { packetHeader=Buf_GetC(buf); packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) // run-length packet { switch (targa_header.pixel_size) { case 24: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); alphabyte = 255; break; case 32: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); alphabyte = Buf_GetC(buf); break; default: /* avoid compiler warnings */ blue = red = green = alphabyte = 0; } for(j=0;j<packetSize;j++) { *pixbuf++=red; *pixbuf++=green; *pixbuf++=blue; *pixbuf++=alphabyte; column++; if (column==columns) // run spans across rows { column=0; if (row>0) row--; else goto breakOut; //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz } } } else // non run-length packet { for(j=0;j<packetSize;j++) { switch (targa_header.pixel_size) { case 24: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = Buf_GetC(buf); green = Buf_GetC(buf); red = Buf_GetC(buf); alphabyte = Buf_GetC(buf); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; default: /* avoid compiler warnings */ blue = red = green = alphabyte = 0; } column++; if (column==columns) // pixel packet run spans across rows { column=0; if (row>0) row--; else goto breakOut; //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz } } } } breakOut:; } } Buf_Free(buf); fclose(fin); *width = (int)(targa_header.width); *height = (int)(targa_header.height); return targa_rgba; } //============================================================================== // // PCX // //============================================================================== typedef struct { char signature; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hdpi,vdpi; byte colortable[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; } pcxheader_t; /* ============ Image_LoadPCX ============ */ byte *Image_LoadPCX (FILE *f, int *width, int *height) { pcxheader_t pcx; int x, y, w, h, readbyte, runlength, start; byte *p, *data; byte palette[768]; stdio_buffer_t *buf; start = ftell (f); //save start of file (since we might be inside a pak file, SEEK_SET might not be the start of the pcx) fread(&pcx, sizeof(pcx), 1, f); pcx.xmin = (unsigned short)LittleShort (pcx.xmin); pcx.ymin = (unsigned short)LittleShort (pcx.ymin); pcx.xmax = (unsigned short)LittleShort (pcx.xmax); pcx.ymax = (unsigned short)LittleShort (pcx.ymax); pcx.bytes_per_line = (unsigned short)LittleShort (pcx.bytes_per_line); if (pcx.signature != 0x0A) Sys_Error ("'%s' is not a valid PCX file", loadfilename); if (pcx.version != 5) Sys_Error ("'%s' is version %i, should be 5", loadfilename, pcx.version); if (pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.color_planes != 1) Sys_Error ("'%s' has wrong encoding or bit depth", loadfilename); w = pcx.xmax - pcx.xmin + 1; h = pcx.ymax - pcx.ymin + 1; data = (byte *) Hunk_Alloc((w*h+1)*4); //+1 to allow reading padding byte on last line //load palette fseek (f, start + com_filesize - 768, SEEK_SET); fread (palette, 1, 768, f); //back to start of image data fseek (f, start + sizeof(pcx), SEEK_SET); buf = Buf_Alloc(f); for (y=0; y<h; y++) { p = data + y * w * 4; for (x=0; x<(pcx.bytes_per_line); ) //read the extra padding byte if necessary { readbyte = Buf_GetC(buf); if(readbyte >= 0xC0) { runlength = readbyte & 0x3F; readbyte = Buf_GetC(buf); } else runlength = 1; while(runlength--) { p[0] = palette[readbyte*3]; p[1] = palette[readbyte*3+1]; p[2] = palette[readbyte*3+2]; p[3] = 255; p += 4; x++; } } } Buf_Free(buf); fclose(f); *width = w; *height = h; return data; } //============================================================================== // // STB_IMAGE_WRITE // //============================================================================== static byte *CopyFlipped(const byte *data, int width, int height, int bpp) { int y, rowsize; byte *flipped; rowsize = width * (bpp / 8); flipped = (byte *) malloc(height * rowsize); if (!flipped) return NULL; for (y=0; y<height; y++) { memcpy(&flipped[y * rowsize], &data[(height - 1 - y) * rowsize], rowsize); } return flipped; } /* ============ Image_WriteJPG -- writes using stb_image_write returns true if successful ============ */ qboolean Image_WriteJPG (const char *name, byte *data, int width, int height, int bpp, int quality, qboolean upsidedown) { unsigned error; char pathname[MAX_OSPATH]; byte *flipped; int bytes_per_pixel; if (!(bpp == 32 || bpp == 24)) Sys_Error ("bpp not 24 or 32"); bytes_per_pixel = bpp / 8; Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name); if (!upsidedown) { flipped = CopyFlipped (data, width, height, bpp); if (!flipped) return false; } else flipped = data; error = stbi_write_jpg (pathname, width, height, bytes_per_pixel, flipped, quality); if (!upsidedown) free (flipped); return (error != 0); } qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown) { unsigned error; char pathname[MAX_OSPATH]; byte *flipped; unsigned char *filters; unsigned char *png; size_t pngsize; LodePNGState state; if (!(bpp == 32 || bpp == 24)) Sys_Error("bpp not 24 or 32"); Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name); flipped = (!upsidedown)? CopyFlipped (data, width, height, bpp) : data; filters = (unsigned char *) malloc (height); if (!filters || !flipped) { if (!upsidedown) free (flipped); free (filters); return false; } // set some options for faster compression lodepng_state_init(&state); state.encoder.zlibsettings.use_lz77 = 0; state.encoder.auto_convert = 0; state.encoder.filter_strategy = LFS_PREDEFINED; memset(filters, 1, height); //use filter 1; see https://www.w3.org/TR/PNG-Filters.html state.encoder.predefined_filters = filters; if (bpp == 24) { state.info_raw.colortype = LCT_RGB; state.info_png.color.colortype = LCT_RGB; } else { state.info_raw.colortype = LCT_RGBA; state.info_png.color.colortype = LCT_RGBA; } error = lodepng_encode (&png, &pngsize, flipped, width, height, &state); if (error == 0) lodepng_save_file (png, pngsize, pathname); #ifdef LODEPNG_COMPILE_ERROR_TEXT else Con_Printf("WritePNG: %s\n", lodepng_error_text()); #endif lodepng_state_cleanup (&state); free (png); free (filters); if (!upsidedown) free (flipped); return (error == 0); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������quakespasm-0.93.0/Quake/gl_rmain.c������������������������������������������������������������������0000644�0000000�0000000�00000072624�13112111036�015455� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_main.c #include "quakedef.h" qboolean r_cache_thrash; // compatability vec3_t modelorg, r_entorigin; entity_t *currententity; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; //johnfitz -- rendering statistics int rs_brushpolys, rs_aliaspolys, rs_skypolys, rs_particles, rs_fogpolys; int rs_dynamiclightmaps, rs_brushpasses, rs_aliaspasses, rs_skypasses; float rs_megatexels; // // view origin // vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_fovx, r_fovy; //johnfitz -- rendering fov may be different becuase of r_waterwarp and r_stereo // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; int d_lightstylevalue[256]; // 8.8 fraction of base light value cvar_t r_norefresh = {"r_norefresh","0",CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities","1",CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel","1",CVAR_NONE}; cvar_t r_speeds = {"r_speeds","0",CVAR_NONE}; cvar_t r_pos = {"r_pos","0",CVAR_NONE}; cvar_t r_fullbright = {"r_fullbright","0",CVAR_NONE}; cvar_t r_lightmap = {"r_lightmap","0",CVAR_NONE}; cvar_t r_shadows = {"r_shadows","0",CVAR_ARCHIVE}; cvar_t r_wateralpha = {"r_wateralpha","1",CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic","1",CVAR_ARCHIVE}; cvar_t r_novis = {"r_novis","0",CVAR_ARCHIVE}; cvar_t gl_finish = {"gl_finish","0",CVAR_NONE}; cvar_t gl_clear = {"gl_clear","1",CVAR_NONE}; cvar_t gl_cull = {"gl_cull","1",CVAR_NONE}; cvar_t gl_smoothmodels = {"gl_smoothmodels","1",CVAR_NONE}; cvar_t gl_affinemodels = {"gl_affinemodels","0",CVAR_NONE}; cvar_t gl_polyblend = {"gl_polyblend","1",CVAR_NONE}; cvar_t gl_flashblend = {"gl_flashblend","0",CVAR_ARCHIVE}; cvar_t gl_playermip = {"gl_playermip","0",CVAR_NONE}; cvar_t gl_nocolors = {"gl_nocolors","0",CVAR_NONE}; //johnfitz -- new cvars cvar_t r_stereo = {"r_stereo","0",CVAR_NONE}; cvar_t r_stereodepth = {"r_stereodepth","128",CVAR_NONE}; cvar_t r_clearcolor = {"r_clearcolor","2",CVAR_ARCHIVE}; cvar_t r_drawflat = {"r_drawflat","0",CVAR_NONE}; cvar_t r_flatlightstyles = {"r_flatlightstyles", "0", CVAR_NONE}; cvar_t gl_fullbrights = {"gl_fullbrights", "1", CVAR_ARCHIVE}; cvar_t gl_farclip = {"gl_farclip", "16384", CVAR_ARCHIVE}; cvar_t gl_overbright = {"gl_overbright", "1", CVAR_ARCHIVE}; cvar_t gl_overbright_models = {"gl_overbright_models", "1", CVAR_ARCHIVE}; cvar_t r_oldskyleaf = {"r_oldskyleaf", "0", CVAR_NONE}; cvar_t r_drawworld = {"r_drawworld", "1", CVAR_NONE}; cvar_t r_showtris = {"r_showtris", "0", CVAR_NONE}; cvar_t r_showbboxes = {"r_showbboxes", "0", CVAR_NONE}; cvar_t r_lerpmodels = {"r_lerpmodels", "1", CVAR_NONE}; cvar_t r_lerpmove = {"r_lerpmove", "1", CVAR_NONE}; cvar_t r_nolerp_list = {"r_nolerp_list", "progs/flame.mdl,progs/flame2.mdl,progs/braztall.mdl,progs/brazshrt.mdl,progs/longtrch.mdl,progs/flame_pyre.mdl,progs/v_saw.mdl,progs/v_xfist.mdl,progs/h2stuff/newfire.mdl", CVAR_NONE}; cvar_t r_noshadow_list = {"r_noshadow_list", "progs/flame2.mdl,progs/flame.mdl,progs/bolt1.mdl,progs/bolt2.mdl,progs/bolt3.mdl,progs/laser.mdl", CVAR_NONE}; extern cvar_t r_vfog; //johnfitz cvar_t gl_zfix = {"gl_zfix", "0", CVAR_NONE}; // QuakeSpasm z-fighting fix cvar_t r_lavaalpha = {"r_lavaalpha","0",CVAR_NONE}; cvar_t r_telealpha = {"r_telealpha","0",CVAR_NONE}; cvar_t r_slimealpha = {"r_slimealpha","0",CVAR_NONE}; float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; qboolean r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz cvar_t r_scale = {"r_scale", "1", CVAR_ARCHIVE}; //============================================================================== // // GLSL GAMMA CORRECTION // //============================================================================== static GLuint r_gamma_texture; static GLuint r_gamma_program; static int r_gamma_texture_width, r_gamma_texture_height; // uniforms used in gamma shader static GLuint gammaLoc; static GLuint contrastLoc; static GLuint textureLoc; /* ============= GLSLGamma_DeleteTexture ============= */ void GLSLGamma_DeleteTexture (void) { glDeleteTextures (1, &r_gamma_texture); r_gamma_texture = 0; r_gamma_program = 0; // deleted in R_DeleteShaders } /* ============= GLSLGamma_CreateShaders ============= */ static void GLSLGamma_CreateShaders (void) { const GLchar *vertSource = \ "#version 110\n" "\n" "void main(void) {\n" " gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0);\n" " gl_TexCoord[0] = gl_MultiTexCoord0;\n" "}\n"; const GLchar *fragSource = \ "#version 110\n" "\n" "uniform sampler2D GammaTexture;\n" "uniform float GammaValue;\n" "uniform float ContrastValue;\n" "\n" "void main(void) {\n" " vec4 frag = texture2D(GammaTexture, gl_TexCoord[0].xy);\n" " frag.rgb = frag.rgb * ContrastValue;\n" " gl_FragColor = vec4(pow(frag.rgb, vec3(GammaValue)), 1.0);\n" "}\n"; if (!gl_glsl_gamma_able) return; r_gamma_program = GL_CreateProgram (vertSource, fragSource, 0, NULL); // get uniform locations gammaLoc = GL_GetUniformLocation (&r_gamma_program, "GammaValue"); contrastLoc = GL_GetUniformLocation (&r_gamma_program, "ContrastValue"); textureLoc = GL_GetUniformLocation (&r_gamma_program, "GammaTexture"); } /* ============= GLSLGamma_GammaCorrect ============= */ void GLSLGamma_GammaCorrect (void) { float smax, tmax; if (!gl_glsl_gamma_able) return; if (vid_gamma.value == 1 && vid_contrast.value == 1) return; // create render-to-texture texture if needed if (!r_gamma_texture) { glGenTextures (1, &r_gamma_texture); glBindTexture (GL_TEXTURE_2D, r_gamma_texture); r_gamma_texture_width = glwidth; r_gamma_texture_height = glheight; if (!gl_texture_NPOT) { r_gamma_texture_width = TexMgr_Pad(r_gamma_texture_width); r_gamma_texture_height = TexMgr_Pad(r_gamma_texture_height); } glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, r_gamma_texture_width, r_gamma_texture_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } // create shader if needed if (!r_gamma_program) { GLSLGamma_CreateShaders (); if (!r_gamma_program) { Sys_Error("GLSLGamma_CreateShaders failed"); } } // copy the framebuffer to the texture GL_DisableMultitexture(); glBindTexture (GL_TEXTURE_2D, r_gamma_texture); glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, glx, gly, glwidth, glheight); // draw the texture back to the framebuffer with a fragment shader GL_UseProgramFunc (r_gamma_program); GL_Uniform1fFunc (gammaLoc, vid_gamma.value); GL_Uniform1fFunc (contrastLoc, q_min(2.0, q_max(1.0, vid_contrast.value))); GL_Uniform1iFunc (textureLoc, 0); // use texture unit 0 glDisable (GL_ALPHA_TEST); glDisable (GL_DEPTH_TEST); glViewport (glx, gly, glwidth, glheight); smax = glwidth/(float)r_gamma_texture_width; tmax = glheight/(float)r_gamma_texture_height; glBegin (GL_QUADS); glTexCoord2f (0, 0); glVertex2f (-1, -1); glTexCoord2f (smax, 0); glVertex2f (1, -1); glTexCoord2f (smax, tmax); glVertex2f (1, 1); glTexCoord2f (0, tmax); glVertex2f (-1, 1); glEnd (); GL_UseProgramFunc (0); // clear cached binding GL_ClearBindings (); } /* ================= R_CullBox -- johnfitz -- replaced with new function from lordhavoc Returns true if the box is completely outside the frustum ================= */ qboolean R_CullBox (vec3_t emins, vec3_t emaxs) { int i; mplane_t *p; for (i = 0;i < 4;i++) { p = frustum + i; switch(p->signbits) { default: case 0: if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 1: if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 2: if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 3: if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) return true; break; case 4: if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 5: if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 6: if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) return true; break; case 7: if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) return true; break; } } return false; } /* =============== R_CullModelForEntity -- johnfitz -- uses correct bounds based on rotation =============== */ qboolean R_CullModelForEntity (entity_t *e) { vec3_t mins, maxs; if (e->angles[0] || e->angles[2]) //pitch or roll { VectorAdd (e->origin, e->model->rmins, mins); VectorAdd (e->origin, e->model->rmaxs, maxs); } else if (e->angles[1]) //yaw { VectorAdd (e->origin, e->model->ymins, mins); VectorAdd (e->origin, e->model->ymaxs, maxs); } else //no rotation { VectorAdd (e->origin, e->model->mins, mins); VectorAdd (e->origin, e->model->maxs, maxs); } return R_CullBox (mins, maxs); } /* =============== R_RotateForEntity -- johnfitz -- modified to take origin and angles instead of pointer to entity =============== */ void R_RotateForEntity (vec3_t origin, vec3_t angles) { glTranslatef (origin[0], origin[1], origin[2]); glRotatef (angles[1], 0, 0, 1); glRotatef (-angles[0], 0, 1, 0); glRotatef (angles[2], 1, 0, 0); } /* ============= GL_PolygonOffset -- johnfitz negative offset moves polygon closer to camera ============= */ void GL_PolygonOffset (int offset) { if (offset > 0) { glEnable (GL_POLYGON_OFFSET_FILL); glEnable (GL_POLYGON_OFFSET_LINE); glPolygonOffset(1, offset); } else if (offset < 0) { glEnable (GL_POLYGON_OFFSET_FILL); glEnable (GL_POLYGON_OFFSET_LINE); glPolygonOffset(-1, offset); } else { glDisable (GL_POLYGON_OFFSET_FILL); glDisable (GL_POLYGON_OFFSET_LINE); } } //============================================================================== // // SETUP FRAME // //============================================================================== int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) bits |= 1<<j; } return bits; } /* =============== TurnVector -- johnfitz turn forward towards side on the plane defined by forward and side if angle = 90, the result will be equal to side assumes side and forward are perpendicular, and normalized to turn away from side, use a negative angle =============== */ #define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) void TurnVector (vec3_t out, const vec3_t forward, const vec3_t side, float angle) { float scale_forward, scale_side; scale_forward = cos( DEG2RAD( angle ) ); scale_side = sin( DEG2RAD( angle ) ); out[0] = scale_forward*forward[0] + scale_side*side[0]; out[1] = scale_forward*forward[1] + scale_side*side[1]; out[2] = scale_forward*forward[2] + scale_side*side[2]; } /* =============== R_SetFrustum -- johnfitz -- rewritten =============== */ void R_SetFrustum (float fovx, float fovy) { int i; if (r_stereo.value) fovx += 10; //silly hack so that polygons don't drop out becuase of stereo skew TurnVector(frustum[0].normal, vpn, vright, fovx/2 - 90); //left plane TurnVector(frustum[1].normal, vpn, vright, 90 - fovx/2); //right plane TurnVector(frustum[2].normal, vpn, vup, 90 - fovy/2); //bottom plane TurnVector(frustum[3].normal, vpn, vup, fovy/2 - 90); //top plane for (i=0 ; i<4 ; i++) { frustum[i].type = PLANE_ANYZ; frustum[i].dist = DotProduct (r_origin, frustum[i].normal); //FIXME: shouldn't this always be zero? frustum[i].signbits = SignbitsForPlane (&frustum[i]); } } /* ============= GL_SetFrustum -- johnfitz -- written to replace MYgluPerspective ============= */ #define NEARCLIP 4 float frustum_skew = 0.0; //used by r_stereo void GL_SetFrustum(float fovx, float fovy) { float xmax, ymax; xmax = NEARCLIP * tan( fovx * M_PI / 360.0 ); ymax = NEARCLIP * tan( fovy * M_PI / 360.0 ); glFrustum(-xmax + frustum_skew, xmax + frustum_skew, -ymax, ymax, NEARCLIP, gl_farclip.value); } /* ============= R_SetupGL ============= */ void R_SetupGL (void) { int scale; //johnfitz -- rewrote this section glMatrixMode(GL_PROJECTION); glLoadIdentity (); scale = CLAMP(1, (int)r_scale.value, 4); // ericw -- see R_ScaleView glViewport (glx + r_refdef.vrect.x, gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width / scale, r_refdef.vrect.height / scale); //johnfitz GL_SetFrustum (r_fovx, r_fovy); //johnfitz -- use r_fov* vars // glCullFace(GL_BACK); //johnfitz -- glquake used CCW with backwards culling -- let's do it right glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up glRotatef (-r_refdef.viewangles[2], 1, 0, 0); glRotatef (-r_refdef.viewangles[0], 0, 1, 0); glRotatef (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); // // set drawing parms // if (gl_cull.value) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); } /* ============= R_Clear -- johnfitz -- rewritten and gutted ============= */ void R_Clear (void) { unsigned int clearbits; clearbits = GL_DEPTH_BUFFER_BIT; // from mh -- if we get a stencil buffer, we should clear it, even though we don't use it if (gl_stencilbits) clearbits |= GL_STENCIL_BUFFER_BIT; if (gl_clear.value) clearbits |= GL_COLOR_BUFFER_BIT; glClear (clearbits); } /* =============== R_SetupScene -- johnfitz -- this is the stuff that needs to be done once per eye in stereo mode =============== */ void R_SetupScene (void) { R_PushDlights (); R_AnimateLight (); r_framecount++; R_SetupGL (); } /* =============== R_SetupView -- johnfitz -- this is the stuff that needs to be done once per frame, even in stereo mode =============== */ void R_SetupView (void) { Fog_SetupFrame (); //johnfitz // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); V_SetContentsColor (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; //johnfitz -- calculate r_fovx and r_fovy here r_fovx = r_refdef.fov_x; r_fovy = r_refdef.fov_y; if (r_waterwarp.value) { int contents = Mod_PointInLeaf (r_origin, cl.worldmodel)->contents; if (contents == CONTENTS_WATER || contents == CONTENTS_SLIME || contents == CONTENTS_LAVA) { //variance is a percentage of width, where width = 2 * tan(fov / 2) otherwise the effect is too dramatic at high FOV and too subtle at low FOV. what a mess! r_fovx = atan(tan(DEG2RAD(r_refdef.fov_x) / 2) * (0.97 + sin(cl.time * 1.5) * 0.03)) * 2 / M_PI_DIV_180; r_fovy = atan(tan(DEG2RAD(r_refdef.fov_y) / 2) * (1.03 - sin(cl.time * 1.5) * 0.03)) * 2 / M_PI_DIV_180; } } //johnfitz R_SetFrustum (r_fovx, r_fovy); //johnfitz -- use r_fov* vars R_MarkSurfaces (); //johnfitz -- create texture chains from PVS R_CullSurfaces (); //johnfitz -- do after R_SetFrustum and R_MarkSurfaces R_UpdateWarpTextures (); //johnfitz -- do this before R_Clear R_Clear (); //johnfitz -- cheat-protect some draw modes r_drawflat_cheatsafe = r_fullbright_cheatsafe = r_lightmap_cheatsafe = false; r_drawworld_cheatsafe = true; if (cl.maxclients == 1) { if (!r_drawworld.value) r_drawworld_cheatsafe = false; if (r_drawflat.value) r_drawflat_cheatsafe = true; else if (r_fullbright.value || !cl.worldmodel->lightdata) r_fullbright_cheatsafe = true; else if (r_lightmap.value) r_lightmap_cheatsafe = true; } //johnfitz } //============================================================================== // // RENDER VIEW // //============================================================================== /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (qboolean alphapass) //johnfitz -- added parameter { int i; if (!r_drawentities.value) return; //johnfitz -- sprites are not a special case for (i=0 ; i<cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; //johnfitz -- if alphapass is true, draw only alpha entites this time //if alphapass is false, draw only nonalpha entities this time if ((ENTALPHA_DECODE(currententity->alpha) < 1 && !alphapass) || (ENTALPHA_DECODE(currententity->alpha) == 1 && alphapass)) continue; //johnfitz -- chasecam if (currententity == &cl_entities[cl.viewentity]) currententity->angles[0] *= 0.3; //johnfitz switch (currententity->model->type) { case mod_alias: R_DrawAliasModel (currententity); break; case mod_brush: R_DrawBrushModel (currententity); break; case mod_sprite: R_DrawSpriteModel (currententity); break; } } } /* ============= R_DrawViewModel -- johnfitz -- gutted ============= */ void R_DrawViewModel (void) { if (!r_drawviewmodel.value || !r_drawentities.value || chase_active.value) return; if (cl.items & IT_INVISIBILITY || cl.stats[STAT_HEALTH] <= 0) return; currententity = &cl.viewent; if (!currententity->model) return; //johnfitz -- this fixes a crash if (currententity->model->type != mod_alias) return; //johnfitz // hack the depth range to prevent view model from poking into walls glDepthRange (0, 0.3); R_DrawAliasModel (currententity); glDepthRange (0, 1); } /* ================ R_EmitWirePoint -- johnfitz -- draws a wireframe cross shape for point entities ================ */ void R_EmitWirePoint (vec3_t origin) { int size=8; glBegin (GL_LINES); glVertex3f (origin[0]-size, origin[1], origin[2]); glVertex3f (origin[0]+size, origin[1], origin[2]); glVertex3f (origin[0], origin[1]-size, origin[2]); glVertex3f (origin[0], origin[1]+size, origin[2]); glVertex3f (origin[0], origin[1], origin[2]-size); glVertex3f (origin[0], origin[1], origin[2]+size); glEnd (); } /* ================ R_EmitWireBox -- johnfitz -- draws one axis aligned bounding box ================ */ void R_EmitWireBox (vec3_t mins, vec3_t maxs) { glBegin (GL_QUAD_STRIP); glVertex3f (mins[0], mins[1], mins[2]); glVertex3f (mins[0], mins[1], maxs[2]); glVertex3f (maxs[0], mins[1], mins[2]); glVertex3f (maxs[0], mins[1], maxs[2]); glVertex3f (maxs[0], maxs[1], mins[2]); glVertex3f (maxs[0], maxs[1], maxs[2]); glVertex3f (mins[0], maxs[1], mins[2]); glVertex3f (mins[0], maxs[1], maxs[2]); glVertex3f (mins[0], mins[1], mins[2]); glVertex3f (mins[0], mins[1], maxs[2]); glEnd (); } /* ================ R_ShowBoundingBoxes -- johnfitz draw bounding boxes -- the server-side boxes, not the renderer cullboxes ================ */ void R_ShowBoundingBoxes (void) { extern edict_t *sv_player; vec3_t mins,maxs; edict_t *ed; int i; if (!r_showbboxes.value || cl.maxclients > 1 || !r_drawentities.value || !sv.active) return; glDisable (GL_DEPTH_TEST); glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); GL_PolygonOffset (OFFSET_SHOWTRIS); glDisable (GL_TEXTURE_2D); glDisable (GL_CULL_FACE); glColor3f (1,1,1); for (i=0, ed=NEXT_EDICT(sv.edicts) ; i<sv.num_edicts ; i++, ed=NEXT_EDICT(ed)) { if (ed == sv_player) continue; //don't draw player's own bbox // if (r_showbboxes.value != 2) // if (!SV_VisibleToClient (sv_player, ed, sv.worldmodel)) // continue; //don't draw if not in pvs if (ed->v.mins[0] == ed->v.maxs[0] && ed->v.mins[1] == ed->v.maxs[1] && ed->v.mins[2] == ed->v.maxs[2]) { //point entity R_EmitWirePoint (ed->v.origin); } else { //box entity VectorAdd (ed->v.mins, ed->v.origin, mins); VectorAdd (ed->v.maxs, ed->v.origin, maxs); R_EmitWireBox (mins, maxs); } } glColor3f (1,1,1); glEnable (GL_TEXTURE_2D); glEnable (GL_CULL_FACE); glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); GL_PolygonOffset (OFFSET_NONE); glEnable (GL_DEPTH_TEST); Sbar_Changed (); //so we don't get dots collecting on the statusbar } /* ================ R_ShowTris -- johnfitz ================ */ void R_ShowTris (void) { extern cvar_t r_particles; int i; if (r_showtris.value < 1 || r_showtris.value > 2 || cl.maxclients > 1) return; if (r_showtris.value == 1) glDisable (GL_DEPTH_TEST); glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); GL_PolygonOffset (OFFSET_SHOWTRIS); glDisable (GL_TEXTURE_2D); glColor3f (1,1,1); // glEnable (GL_BLEND); // glBlendFunc (GL_ONE, GL_ONE); if (r_drawworld.value) { R_DrawWorld_ShowTris (); } if (r_drawentities.value) { for (i=0 ; i<cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; if (currententity == &cl_entities[cl.viewentity]) // chasecam currententity->angles[0] *= 0.3; switch (currententity->model->type) { case mod_brush: R_DrawBrushModel_ShowTris (currententity); break; case mod_alias: R_DrawAliasModel_ShowTris (currententity); break; case mod_sprite: R_DrawSpriteModel (currententity); break; default: break; } } // viewmodel currententity = &cl.viewent; if (r_drawviewmodel.value && !chase_active.value && cl.stats[STAT_HEALTH] > 0 && !(cl.items & IT_INVISIBILITY) && currententity->model && currententity->model->type == mod_alias) { glDepthRange (0, 0.3); R_DrawAliasModel_ShowTris (currententity); glDepthRange (0, 1); } } if (r_particles.value) { R_DrawParticles_ShowTris (); } // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glDisable (GL_BLEND); glColor3f (1,1,1); glEnable (GL_TEXTURE_2D); glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); GL_PolygonOffset (OFFSET_NONE); if (r_showtris.value == 1) glEnable (GL_DEPTH_TEST); Sbar_Changed (); //so we don't get dots collecting on the statusbar } /* ================ R_DrawShadows ================ */ void R_DrawShadows (void) { int i; if (!r_shadows.value || !r_drawentities.value || r_drawflat_cheatsafe || r_lightmap_cheatsafe) return; // Use stencil buffer to prevent self-intersecting shadows, from Baker (MarkV) if (gl_stencilbits) { glClear(GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_EQUAL, 0, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glEnable(GL_STENCIL_TEST); } for (i=0 ; i<cl_numvisedicts ; i++) { currententity = cl_visedicts[i]; if (currententity->model->type != mod_alias) continue; if (currententity == &cl.viewent) return; GL_DrawAliasShadow (currententity); } if (gl_stencilbits) { glDisable(GL_STENCIL_TEST); } } /* ================ R_RenderScene ================ */ void R_RenderScene (void) { R_SetupScene (); //johnfitz -- this does everything that should be done once per call to RenderScene Fog_EnableGFog (); //johnfitz Sky_DrawSky (); //johnfitz R_DrawWorld (); S_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawShadows (); //johnfitz -- render entity shadows R_DrawEntitiesOnList (false); //johnfitz -- false means this is the pass for nonalpha entities R_DrawWorld_Water (); //johnfitz -- drawn here since they might have transparency R_DrawEntitiesOnList (true); //johnfitz -- true means this is the pass for alpha entities R_RenderDlights (); //triangle fan dlights -- johnfitz -- moved after water R_DrawParticles (); Fog_DisableGFog (); //johnfitz R_DrawViewModel (); //johnfitz -- moved here from R_RenderView R_ShowTris (); //johnfitz R_ShowBoundingBoxes (); //johnfitz } static GLuint r_scaleview_texture; static int r_scaleview_texture_width, r_scaleview_texture_height; /* ============= R_ScaleView_DeleteTexture ============= */ void R_ScaleView_DeleteTexture (void) { glDeleteTextures (1, &r_scaleview_texture); r_scaleview_texture = 0; } /* ================ R_ScaleView The r_scale cvar allows rendering the 3D view at 1/2, 1/3, or 1/4 resolution. This function scales the reduced resolution 3D view back up to fill r_refdef.vrect. This is for emulating a low-resolution pixellated look, or possibly as a perforance boost on slow graphics cards. ================ */ void R_ScaleView (void) { float smax, tmax; int scale; int srcx, srcy, srcw, srch; // copied from R_SetupGL() scale = CLAMP(1, (int)r_scale.value, 4); srcx = glx + r_refdef.vrect.x; srcy = gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height; srcw = r_refdef.vrect.width / scale; srch = r_refdef.vrect.height / scale; if (scale == 1) return; // make sure texture unit 0 is selected GL_DisableMultitexture (); // create (if needed) and bind the render-to-texture texture if (!r_scaleview_texture) { glGenTextures (1, &r_scaleview_texture); r_scaleview_texture_width = 0; r_scaleview_texture_height = 0; } glBindTexture (GL_TEXTURE_2D, r_scaleview_texture); // resize render-to-texture texture if needed if (r_scaleview_texture_width < srcw || r_scaleview_texture_height < srch) { r_scaleview_texture_width = srcw; r_scaleview_texture_height = srch; if (!gl_texture_NPOT) { r_scaleview_texture_width = TexMgr_Pad(r_scaleview_texture_width); r_scaleview_texture_height = TexMgr_Pad(r_scaleview_texture_height); } glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, r_scaleview_texture_width, r_scaleview_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } // copy the framebuffer to the texture glBindTexture (GL_TEXTURE_2D, r_scaleview_texture); glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, srcx, srcy, srcw, srch); // draw the texture back to the framebuffer glDisable (GL_ALPHA_TEST); glDisable (GL_DEPTH_TEST); glDisable (GL_CULL_FACE); glDisable (GL_BLEND); glViewport (srcx, srcy, r_refdef.vrect.width, r_refdef.vrect.height); glMatrixMode(GL_PROJECTION); glLoadIdentity (); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); // correction factor if we lack NPOT textures, normally these are 1.0f smax = srcw/(float)r_scaleview_texture_width; tmax = srch/(float)r_scaleview_texture_height; glBegin (GL_QUADS); glTexCoord2f (0, 0); glVertex2f (-1, -1); glTexCoord2f (smax, 0); glVertex2f (1, -1); glTexCoord2f (smax, tmax); glVertex2f (1, 1); glTexCoord2f (0, tmax); glVertex2f (-1, 1); glEnd (); // clear cached binding GL_ClearBindings (); } /* ================ R_RenderView ================ */ void R_RenderView (void) { double time1, time2; if (r_norefresh.value) return; if (!cl.worldmodel) Sys_Error ("R_RenderView: NULL worldmodel"); time1 = 0; /* avoid compiler warning */ if (r_speeds.value) { glFinish (); time1 = Sys_DoubleTime (); //johnfitz -- rendering statistics rs_brushpolys = rs_aliaspolys = rs_skypolys = rs_particles = rs_fogpolys = rs_megatexels = rs_dynamiclightmaps = rs_aliaspasses = rs_skypasses = rs_brushpasses = 0; } else if (gl_finish.value) glFinish (); R_SetupView (); //johnfitz -- this does everything that should be done once per frame //johnfitz -- stereo rendering -- full of hacky goodness if (r_stereo.value) { float eyesep = CLAMP(-8.0f, r_stereo.value, 8.0f); float fdepth = CLAMP(32.0f, r_stereodepth.value, 1024.0f); AngleVectors (r_refdef.viewangles, vpn, vright, vup); //render left eye (red) glColorMask(1, 0, 0, 1); VectorMA (r_refdef.vieworg, -0.5f * eyesep, vright, r_refdef.vieworg); frustum_skew = 0.5 * eyesep * NEARCLIP / fdepth; srand((int) (cl.time * 1000)); //sync random stuff between eyes R_RenderScene (); //render right eye (cyan) glClear (GL_DEPTH_BUFFER_BIT); glColorMask(0, 1, 1, 1); VectorMA (r_refdef.vieworg, 1.0f * eyesep, vright, r_refdef.vieworg); frustum_skew = -frustum_skew; srand((int) (cl.time * 1000)); //sync random stuff between eyes R_RenderScene (); //restore glColorMask(1, 1, 1, 1); VectorMA (r_refdef.vieworg, -0.5f * eyesep, vright, r_refdef.vieworg); frustum_skew = 0.0f; } else { R_RenderScene (); } //johnfitz R_ScaleView (); //johnfitz -- modified r_speeds output time2 = Sys_DoubleTime (); if (r_pos.value) Con_Printf ("x %i y %i z %i (pitch %i yaw %i roll %i)\n", (int)cl_entities[cl.viewentity].origin[0], (int)cl_entities[cl.viewentity].origin[1], (int)cl_entities[cl.viewentity].origin[2], (int)cl.viewangles[PITCH], (int)cl.viewangles[YAW], (int)cl.viewangles[ROLL]); else if (r_speeds.value == 2) Con_Printf ("%3i ms %4i/%4i wpoly %4i/%4i epoly %3i lmap %4i/%4i sky %1.1f mtex\n", (int)((time2-time1)*1000), rs_brushpolys, rs_brushpasses, rs_aliaspolys, rs_aliaspasses, rs_dynamiclightmaps, rs_skypolys, rs_skypasses, TexMgr_FrameUsage ()); else if (r_speeds.value) Con_Printf ("%3i ms %4i wpoly %4i epoly %3i lmap\n", (int)((time2-time1)*1000), rs_brushpolys, rs_aliaspolys, rs_dynamiclightmaps); //johnfitz } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������